diff --git a/.mailmap b/.mailmap index 0b9298a55d2d..34b9b51e9e8c 100644 --- a/.mailmap +++ b/.mailmap @@ -402,6 +402,7 @@ Jesper Dangaard Brouer Jesper Dangaard Brouer Jesper Dangaard Brouer Jesper Dangaard Brouer +Jesse Brandeburg Jessica Zhang Jessica Zhang Jessica Zhang diff --git a/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 new file mode 100644 index 000000000000..7dfdce08a42f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 @@ -0,0 +1,24 @@ +What: /sys/class/ieee80211/phyX/rtw89_usb/serial_number +Date: May 2026 +Contact: Johnson Tsai , linux-wireless@vger.kernel.org +Description: (Read) Serial number burned into EFUSE of the RTL8852CU-based + USB Wi-Fi adapter. Only present on devices that set the + RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de / + PID 0x2432). + + Format: %10phN (5 raw bytes printed as 10 lowercase hex + digits, no separators). + + Example: 3642000123 + +What: /sys/class/ieee80211/phyX/rtw89_usb/uuid +Date: May 2026 +Contact: Johnson Tsai , linux-wireless@vger.kernel.org +Description: (Read) UUID burned into EFUSE of the RTL8852CU-based USB Wi-Fi + adapter. Only present on devices that set the + RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de / + PID 0x2432). + + Format: %pUb (RFC 4122 UUID in lowercase with hyphens). + + Example: aaec2b7c-0a55-4727-8de0-b30febccbbaa diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a54463034367..265dbc6ac1d8 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -556,9 +556,6 @@ Kernel parameters 1 -- enable. Default value is set via kernel config option. - arcrimi= [HW,NET] ARCnet - "RIM I" (entirely mem-mapped) cards - Format: ,, - arm64.no32bit_el0 [ARM64] Unconditionally disable the execution of 32 bit applications. @@ -891,17 +888,6 @@ Kernel parameters Sets the size of memory pool for coherent, atomic dma allocations, by default set to 256K. - com20020= [HW,NET] ARCnet - COM20020 chipset - Format: - [,[,[,[,[,]]]]] - - com90io= [HW,NET] ARCnet - COM90xx chipset (IO-mapped buffers) - Format: [,] - - com90xx= [HW,NET] - ARCnet - COM90xx chipset (memory-mapped buffers) - Format: [,[,]] - condev= [HW,S390] console device conmode= @@ -4250,12 +4236,8 @@ Kernel parameters n2= [NET] SDL Inc. RISCom/N2 synchronous serial card - netdev= [NET] Network devices parameters + netdev= [NET] NE2000 ISA network devices parameters Format: ,,,, - Note that mem_start is often overloaded to mean - something different and driver-specific. - This usage is only documented in each driver source - file if at all. netpoll.carrier_timeout= [NET] Specifies amount of time (in seconds) that diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst index 3db464e006a3..e586e17fc7a5 100644 --- a/Documentation/admin-guide/sysctl/net.rst +++ b/Documentation/admin-guide/sysctl/net.rst @@ -474,51 +474,7 @@ Please see: Documentation/networking/ip-sysctl.rst and Documentation/admin-guide/sysctl/net.rst for descriptions of these entries. -4. Appletalk ------------- - -The /proc/sys/net/appletalk directory holds the Appletalk configuration data -when Appletalk is loaded. The configurable parameters are: - -aarp-expiry-time ----------------- - -The amount of time we keep an ARP entry before expiring it. Used to age out -old hosts. - -aarp-resolve-time ------------------ - -The amount of time we will spend trying to resolve an Appletalk address. - -aarp-retransmit-limit ---------------------- - -The number of times we will retransmit a query before giving up. - -aarp-tick-time --------------- - -Controls the rate at which expires are checked. - -The directory /proc/net/appletalk holds the list of active Appletalk sockets -on a machine. - -The fields indicate the DDP type, the local address (in network:node format) -the remote address, the size of the transmit pending queue, the size of the -received queue (bytes waiting for applications to read) the state and the uid -owning the socket. - -/proc/net/atalk_iface lists all the interfaces configured for appletalk.It -shows the name of the interface, its Appletalk address, the network range on -that address (or network number for phase 1 networks), and the status of the -interface. - -/proc/net/atalk_route lists each known network route. It lists the target -(network) that the route leads to, the router (may be directly connected), the -route flags, and the device the route is using. - -5. TIPC +4. TIPC ------- tipc_rmem diff --git a/Documentation/arch/m68k/kernel-options.rst b/Documentation/arch/m68k/kernel-options.rst index 2008a20b4329..c59e7aaed44e 100644 --- a/Documentation/arch/m68k/kernel-options.rst +++ b/Documentation/arch/m68k/kernel-options.rst @@ -244,23 +244,7 @@ drive (with "root="). 3) General Device Options (Amiga and Atari) =========================================== -3.1) ether= ------------ - -:Syntax: ether=[[,[,[,]]]], - - is the name of a net driver, as specified in -drivers/net/Space.c in the Linux source. Most prominent are eth0, ... -eth3, sl0, ... sl3, ppp0, ..., ppp3, dummy, and lo. - -The non-ethernet drivers (sl, ppp, dummy, lo) obviously ignore the -settings by this options. Also, the existing ethernet drivers for -Linux/m68k (ariadne, a2065, hydra) don't use them because Zorro boards -are really Plug-'n-Play, so the "ether=" option is useless altogether -for Linux/m68k. - - -3.2) hd= +3.1) hd= -------- :Syntax: hd=,, @@ -273,7 +257,7 @@ itself. It exists just for the case that this fails for one of your disks. -3.3) max_scsi_luns= +3.2) max_scsi_luns= ------------------- :Syntax: max_scsi_luns= @@ -284,7 +268,7 @@ be scanned. Valid values for are between 1 and 8. Default is 8 if configuration, else 1. -3.4) st= +3.3) st= -------- :Syntax: st=,[,[]] @@ -297,7 +281,7 @@ total number of buffers. limits the total number of buffers allocated for all tape devices. -3.5) dmasound= +3.4) dmasound= -------------- :Syntax: dmasound=[,[,]] diff --git a/Documentation/devicetree/bindings/net/airoha,an8801.yaml b/Documentation/devicetree/bindings/net/airoha,an8801.yaml new file mode 100644 index 000000000000..d6e5a60a6b20 --- /dev/null +++ b/Documentation/devicetree/bindings/net/airoha,an8801.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/airoha,an8801.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha AN8801R Series PHY + +maintainers: + - AngeloGioacchino Del Regno + +description: + The Airoha AN8801R is a low power single-port Ethernet PHY Transceiver + with Single-port serdes interface for 1000Base-X/RGMII; this chip is + compliant with 10Base-T, 100Base-TX and 1000Base-T IEEE 802.3(u,ab) + and supports Energy Efficient Ethernet (802.3az), Full Duplex Control + Flow (802.3x), auto-negotiation, crossover detect and autocorrection, + Wake-on-LAN with Magic Packet, and Jumbo Frame up to 9 Kilobytes. + This PHY also supports up to three user-configurable LEDs, which are + usually used for LAN Activity, 100M, 1000M indication. + +allOf: + - $ref: ethernet-phy.yaml# + +properties: + compatible: + enum: + - ethernet-phy-idc0ff.0421 + + reg: + maxItems: 1 + + leds: + type: object + description: + Describes the LEDs associated to the PHY + + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + patternProperties: + "^led@[0-2]$": + type: object + description: PHY LEDs + $ref: /schemas/leds/common.yaml# + + properties: + reg: + enum: [0, 1, 2] + + function-enumerator: + enum: [0, 1, 2] + description: | + Specifies a function for offloading LED functionality to the PHY: + 0 - No offloading + 1 - Link Availability + 2 - Network Activity + + required: + - reg + + unevaluatedProperties: false + + additionalProperties: false + + wakeup-source: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable Wake-on-LAN support + +required: + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-phy@0 { + compatible = "ethernet-phy-idc0ff.0421"; + reg = <0>; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + default-state = "keep"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + default-state = "keep"; + }; + + led@2 { + reg = <2>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <2>; + default-state = "keep"; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml index fbe2ddcdd909..17fe2edf4886 100644 --- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml +++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml @@ -130,6 +130,42 @@ patternProperties: maximum: 4 description: GMAC port identifier + allOf: + - if: + properties: + reg: + contains: + items: + - enum: + - 3 + - 4 + then: + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + "^ethernet@[0-5]$": + type: object + unevaluatedProperties: false + $ref: ethernet-controller.yaml# + description: External ethernet port ID available on the GDM port + + properties: + compatible: + const: airoha,eth-port + + reg: + maximum: 5 + description: External ethernet port identifier + + required: + - reg + - compatible + required: - reg - compatible @@ -191,9 +227,27 @@ examples: #address-cells = <1>; #size-cells = <0>; - mac: ethernet@1 { + ethernet@1 { compatible = "airoha,eth-mac"; reg = <1>; }; + + ethernet@4 { + compatible = "airoha,eth-mac"; + reg = <4>; + + #address-cells = <1>; + #size-cells = <0>; + + ethernet@0 { + compatible = "airoha,eth-port"; + reg = <0>; + }; + + ethernet@1 { + compatible = "airoha,eth-port"; + reg = <1>; + }; + }; }; }; diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt deleted file mode 100644 index dab208b5c7c7..000000000000 --- a/Documentation/devicetree/bindings/net/dsa/dsa.txt +++ /dev/null @@ -1,4 +0,0 @@ -Distributed Switch Architecture Device Tree Bindings ----------------------------------------------------- - -See Documentation/devicetree/bindings/net/dsa/dsa.yaml for the documentation. diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.yaml b/Documentation/devicetree/bindings/net/dsa/dsa.yaml index 2abd036578d1..801e1411e5c2 100644 --- a/Documentation/devicetree/bindings/net/dsa/dsa.yaml +++ b/Documentation/devicetree/bindings/net/dsa/dsa.yaml @@ -28,7 +28,11 @@ properties: A two element list indicates which DSA cluster, and position within the cluster a switch takes. <0 0> is cluster 0, switch 0. <0 1> is cluster 0, switch 1. <1 0> is cluster 1, switch 0. A switch not part of any cluster - (single device hanging off a CPU port) must not specify this property + (single device hanging off a CPU port) does not usually need to specify + this property, and then it becomes cluster 0, switch 0. For a topology + aware switch, its switch index can be specified through this property, + even if it is not part of any cluster. Also, topology-unaware switches + must always be defined as index 0 of their cluster. $ref: /schemas/types.yaml#/definitions/uint32-array additionalProperties: true diff --git a/Documentation/devicetree/bindings/net/dsa/lan9303.txt b/Documentation/devicetree/bindings/net/dsa/lan9303.txt deleted file mode 100644 index 46a732087f5c..000000000000 --- a/Documentation/devicetree/bindings/net/dsa/lan9303.txt +++ /dev/null @@ -1,100 +0,0 @@ -SMSC/MicroChip LAN9303 three port ethernet switch -------------------------------------------------- - -Required properties: - -- compatible: should be - - "smsc,lan9303-i2c" for I2C managed mode - or - - "smsc,lan9303-mdio" for mdio managed mode - -Optional properties: - -- reset-gpios: GPIO to be used to reset the whole device -- reset-duration: reset duration in milliseconds, defaults to 200 ms - -Subnodes: - -The integrated switch subnode should be specified according to the binding -described in dsa/dsa.txt. The CPU port of this switch is always port 0. - -Note: always use 'reg = <0/1/2>;' for the three DSA ports, even if the device is -configured to use 1/2/3 instead. This hardware configuration will be -auto-detected and mapped accordingly. - -Example: - -I2C managed mode: - - master: masterdevice@X { - - fixed-link { /* RMII fixed link to LAN9303 */ - speed = <100>; - full-duplex; - }; - }; - - switch: switch@a { - compatible = "smsc,lan9303-i2c"; - reg = <0xa>; - reset-gpios = <&gpio7 6 GPIO_ACTIVE_LOW>; - reset-duration = <200>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { /* RMII fixed link to master */ - reg = <0>; - ethernet = <&master>; - }; - - port@1 { /* external port 1 */ - reg = <1>; - label = "lan1"; - }; - - port@2 { /* external port 2 */ - reg = <2>; - label = "lan2"; - }; - }; - }; - -MDIO managed mode: - - master: masterdevice@X { - phy-handle = <&switch>; - - mdio { - #address-cells = <1>; - #size-cells = <0>; - - switch: switch-phy@0 { - compatible = "smsc,lan9303-mdio"; - reg = <0>; - reset-gpios = <&gpio7 6 GPIO_ACTIVE_LOW>; - reset-duration = <100>; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - ethernet = <&master>; - }; - - port@1 { /* external port 1 */ - reg = <1>; - label = "lan1"; - }; - - port@2 { /* external port 2 */ - reg = <2>; - label = "lan2"; - }; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml b/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml new file mode 100644 index 000000000000..1b35e4cbd049 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/nxp,netc-switch.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NETC Switch family + +description: > + The NETC presents itself as a multi-function PCIe Root Complex Integrated + Endpoint (RCiEP) and provides full 802.1Q Ethernet switch functionality, + advanced QoS with 8 traffic classes and 4 drop resilience levels, and a + full range of TSN standards capabilities. + + The CPU port of the switch connects to an internal ENETC. The switch and + the internal ENETC are fully integrated into the NETC IP, a back-to-back + MAC is not required. Instead, a light-weight "pseudo MAC" provides the + delineation between the switch and ENETC. This translates to lower power + (less logic and memory) and lower delay (as there is no serialization + delay across this link). + +maintainers: + - Wei Fang + +properties: + compatible: + enum: + - pci1131,eef2 + + reg: + maxItems: 1 + + dsa,member: + description: > + The property indicates DSA cluster and switch index. For NETC switch, + the valid range of the switch index is 1 ~ 7, the index is reflected + in the switch tag as an indication of the switch ID where the frame + originated. The value 0 is reserved for ENETC VEPA switch, whose ID + is hardwired to zero. + items: + - true + - minimum: 1 + maximum: 7 + + ethernet-ports: + type: object + patternProperties: + "^ethernet-port@[0-9a-f]$": + type: object + $ref: dsa-port.yaml# + + properties: + clocks: + items: + - description: MAC transmit/receive reference clock. + + clock-names: + items: + - const: ref + + mdio: + $ref: /schemas/net/mdio.yaml# + unevaluatedProperties: false + description: + Optional child node for switch port, otherwise use NETC EMDIO. + + unevaluatedProperties: false + +required: + - compatible + - reg + - dsa,member + - ethernet-ports + +allOf: + - $ref: /schemas/pci/pci-device.yaml + - $ref: dsa.yaml# + +unevaluatedProperties: false + +examples: + - | + pcie { + #address-cells = <3>; + #size-cells = <2>; + + ethernet-switch@0,2 { + compatible = "pci1131,eef2"; + reg = <0x200 0 0 0 0>; + dsa,member = <0 1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_switch>; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + ethernet-port@0 { + reg = <0>; + phy-handle = <ðphy0>; + phy-mode = "mii"; + }; + + ethernet-port@1 { + reg = <1>; + phy-handle = <ðphy1>; + phy-mode = "mii"; + }; + + ethernet-port@2 { + reg = <2>; + clocks = <&scmi_clk 103>; + clock-names = "ref"; + phy-handle = <ðphy2>; + phy-mode = "rgmii-id"; + }; + + ethernet-port@3 { + reg = <3>; + ethernet = <&enetc3>; + phy-mode = "internal"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/dsa/smsc,lan9303.yaml b/Documentation/devicetree/bindings/net/dsa/smsc,lan9303.yaml new file mode 100644 index 000000000000..42f8473538a0 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/smsc,lan9303.yaml @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/smsc,lan9303.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SMSC/MicroChip LAN9303 three port ethernet switch + +maintainers: + - Frank Li + +description: + The LAN9303 is a three port ethernet switch with integrated PHYs for the + two external ports. The third port is an RMII/MII interface to a host + processor. The device can be managed via I2C or MDIO. + + Note - always use 'reg = <0/1/2>;' for the three DSA ports, even if the + device is configured to use 1/2/3 instead. This hardware configuration + will be auto-detected and mapped accordingly. + +properties: + compatible: + enum: + - smsc,lan9303-i2c + - smsc,lan9303-mdio + + reg: + maxItems: 1 + + reset-gpios: + description: + GPIO to be used to reset the whole device + maxItems: 1 + + reset-duration: + description: + Reset duration in milliseconds + default: 200 + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + +unevaluatedProperties: false + +allOf: + - $ref: dsa.yaml# + +examples: + - | + #include + + /* I2C managed mode */ + i2c { + #address-cells = <1>; + #size-cells = <0>; + + switch@a { + compatible = "smsc,lan9303-i2c"; + reg = <0xa>; + reset-gpios = <&gpio7 6 GPIO_ACTIVE_LOW>; + reset-duration = <200>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "cpu"; + ethernet = <&master>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + }; + }; + }; + + - | + #include + + /* MDIO managed mode */ + mdio { + #address-cells = <1>; + #size-cells = <0>; + + switch@0 { + compatible = "smsc,lan9303-mdio"; + reg = <0>; + reset-gpios = <&gpio7 6 GPIO_ACTIVE_LOW>; + reset-duration = <100>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "cpu"; + ethernet = <&master>; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index 21a1a63506f0..c3ebb3af8b52 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -106,10 +106,13 @@ properties: by software. clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 description: - External clock connected to the PHY. If not specified it is assumed - that the PHY uses a fixed crystal or an internal oscillator. + External clock connected to the PHY or RX and TX clocks that the PHY + requires to enable explicitly. If not specified it is assumed + that the PHY uses a fixed crystal or an internal oscillator or that the + RX/TX clocks are hardware enabled by default. enet-phy-lane-swap: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/net/mdio.txt b/Documentation/devicetree/bindings/net/mdio.txt deleted file mode 100644 index cf8a0105488e..000000000000 --- a/Documentation/devicetree/bindings/net/mdio.txt +++ /dev/null @@ -1 +0,0 @@ -This file has moved to mdio.yaml. diff --git a/Documentation/devicetree/bindings/net/microchip,lan8650.yaml b/Documentation/devicetree/bindings/net/microchip,lan8650.yaml index 61e11d4a07c4..766ff58147ae 100644 --- a/Documentation/devicetree/bindings/net/microchip,lan8650.yaml +++ b/Documentation/devicetree/bindings/net/microchip,lan8650.yaml @@ -67,7 +67,7 @@ examples: pinctrl-names = "default"; pinctrl-0 = <ð0_pins>; interrupt-parent = <&gpio>; - interrupts = <6 IRQ_TYPE_EDGE_FALLING>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; local-mac-address = [04 05 06 01 02 03]; spi-max-frequency = <15000000>; }; diff --git a/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml b/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml index accff93d38f8..b9c394009040 100644 --- a/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml +++ b/Documentation/devicetree/bindings/net/microchip,lan95xx.yaml @@ -20,6 +20,8 @@ properties: compatible: items: - enum: + - usb424,7500 # SMSC LAN7500 USB Ethernet Device + - usb424,7505 # SMSC LAN7505 USB Ethernet Device - usb424,9500 # SMSC9500 USB Ethernet Device - usb424,9505 # SMSC9505 USB Ethernet Device - usb424,9530 # SMSC LAN9530 USB Ethernet Device diff --git a/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml b/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml index 306ef9ecf2b9..0f0f35865ef4 100644 --- a/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml +++ b/Documentation/devicetree/bindings/net/microchip,lan966x-switch.yaml @@ -17,7 +17,7 @@ description: | properties: $nodename: - pattern: "^switch@[0-9a-f]+$" + pattern: "^(ethernet-)?switch@[0-9a-f]+$" compatible: const: microchip,lan966x-switch @@ -70,7 +70,7 @@ properties: additionalProperties: false patternProperties: - "^port@[0-9a-f]+$": + "^(ethernet-)?port@[0-9a-f]+$": type: object $ref: /schemas/net/ethernet-controller.yaml# @@ -138,7 +138,7 @@ additionalProperties: false examples: - | #include - switch: switch@e0000000 { + switch: ethernet-switch@e0000000 { compatible = "microchip,lan966x-switch"; reg = <0xe0000000 0x0100000>, <0xe2000000 0x0800000>; @@ -151,14 +151,14 @@ examples: #address-cells = <1>; #size-cells = <0>; - port0: port@0 { + port0: ethernet-port@0 { reg = <0>; phy-handle = <&phy0>; phys = <&serdes 0 0>; phy-mode = "gmii"; }; - port1: port@1 { + port1: ethernet-port@1 { reg = <1>; sfp = <&sfp_eth1>; managed = "in-band-status"; diff --git a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml index ca61cc37a790..ca4200afa793 100644 --- a/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml +++ b/Documentation/devicetree/bindings/net/pse-pd/microchip,pd692x0.yaml @@ -22,6 +22,10 @@ properties: reg: maxItems: 1 + disable-ports-gpios: + description: GPIO pin to disable PoE on all ports + maxItems: 1 + vdd-supply: description: Regulator that provides 3.3V VDD power supply. diff --git a/Documentation/devicetree/bindings/net/qca,ar803x.yaml b/Documentation/devicetree/bindings/net/qca,ar803x.yaml index 7ae5110e7aa2..53f648c4135f 100644 --- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml +++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml @@ -28,6 +28,16 @@ allOf: reg: const: 7 # This PHY is always at MDIO address 7 in the IPQ5018 SoC + clocks: + items: + - description: RX clock + - description: TX clock + + clock-names: + items: + - const: rx + - const: tx + resets: items: - description: @@ -42,6 +52,11 @@ allOf: of this PHY are directly connected to an RJ45 connector. type: boolean + required: + - clocks + - clock-names + - resets + properties: compatible: enum: @@ -162,6 +177,7 @@ examples: }; }; - | + #include #include mdio { @@ -172,6 +188,9 @@ examples: compatible = "ethernet-phy-id004d.d0c0"; reg = <7>; + clocks = <&gcc GCC_GEPHY_RX_CLK>, + <&gcc GCC_GEPHY_TX_CLK>; + clock-names = "rx", "tx"; resets = <&gcc GCC_GEPHY_MISC_ARES>; }; }; diff --git a/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml b/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml index 02e4e33e9969..271e05bae9c5 100644 --- a/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml +++ b/Documentation/devicetree/bindings/net/realtek,rtl9301-mdio.yaml @@ -18,7 +18,14 @@ properties: - realtek,rtl9302c-mdio - realtek,rtl9303-mdio - const: realtek,rtl9301-mdio - - const: realtek,rtl9301-mdio + - items: + - enum: + - realtek,rtl9312-mdio + - realtek,rtl9313-mdio + - const: realtek,rtl9311-mdio + - enum: + - realtek,rtl9301-mdio + - realtek,rtl9311-mdio '#address-cells': const: 1 diff --git a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml index 313a15331661..fdcc61c65f87 100644 --- a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml @@ -31,7 +31,7 @@ properties: - const: starfive,jh7110-dwmac - const: snps,dwmac-5.20 - items: - - const: starfive,jh8100-dwmac + - const: starfive,jhb100-dwmac - const: starfive,jh7110-dwmac - const: snps,dwmac-5.20 @@ -39,20 +39,24 @@ properties: maxItems: 1 clocks: + minItems: 5 items: - description: GMAC main clock - description: GMAC AHB clock - description: PTP clock - description: TX clock - description: GTX clock + - description: SGMII RX clock clock-names: + minItems: 5 items: - const: stmmaceth - const: pclk - const: ptp_ref - const: tx - const: gtx + - const: sgmii_rx starfive,tx-use-rgmii-clk: description: @@ -111,22 +115,19 @@ allOf: contains: const: starfive,jh7110-dwmac then: - properties: - interrupts: - minItems: 3 - maxItems: 3 - - interrupt-names: - minItems: 3 - maxItems: 3 - if: properties: compatible: contains: - const: starfive,jh8100-dwmac + const: starfive,jhb100-dwmac then: properties: + interrupts: + maxItems: 1 + + interrupt-names: + const: macirq + resets: maxItems: 1 @@ -134,6 +135,14 @@ allOf: const: stmmaceth else: properties: + interrupts: + minItems: 3 + maxItems: 3 + + interrupt-names: + minItems: 3 + maxItems: 3 + resets: minItems: 2 diff --git a/Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml b/Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml new file mode 100644 index 000000000000..a32265c07350 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/st,stlc4560.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST/Intersil/Conexant stlc45xx/p54spi/cx3110x SPI wireless device + +maintainers: + - Christian Lamparter + +description: + The SPI variant of the Intersil Prism54 wireless device was sold + under a variety of names, including Conexant CX3110x and + ST Microelectronics STLC5460. + +allOf: + - $ref: ieee80211.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + oneOf: + - const: st,stlc4560 + - items: + - enum: + - cnxt,3110x + - isil,p54spi + - st,stlc4550 + - const: st,stlc4560 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + powerdown-gpios: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + wifi@0 { + compatible = "st,stlc4560"; + reg = <0>; + spi-max-frequency = <48000000>; + interrupts-extended = <&gpio 23>; + powerdown-gpios = <&gpio 1>; + }; + }; diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst index 93c191b2d089..bae14766d4f7 100644 --- a/Documentation/driver-api/dpll.rst +++ b/Documentation/driver-api/dpll.rst @@ -65,35 +65,43 @@ request, where user provides attributes that result in single pin match. Pin selection ============= -In general, selected pin (the one which signal is driving the dpll -device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only -one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll -device. +Pin state (``DPLL_A_PIN_STATE``) reflects the administrative intent set +by the user. Pin operational state (``DPLL_A_PIN_OPERSTATE``) reflects +what the hardware is actually doing with the pin. Pin selection can be done either manually or automatically, depending on hardware capabilities and active dpll device work mode (``DPLL_A_MODE`` attribute). The consequence is that there are -differences for each mode in terms of available pin states, as well as -for the states the user can request for a dpll device. +differences for each mode in terms of available pin states the user can +request for a dpll device. -In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive -one of following pin states: +In manual mode (``DPLL_MODE_MANUAL``) the user can request one of +following pin states: -- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device -- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll +- ``DPLL_PIN_STATE_CONNECTED`` - the pin is selected to drive dpll device +- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not selected to drive + dpll device -In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or -receive one of following pin states: +In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request one of +following pin states: - ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid input for automatic selection algorithm - ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as a valid input for automatic selection algorithm -In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive -pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection -algorithm locks a dpll device with one of the inputs. +The actual hardware status of a pin is reported via the operational +state (``DPLL_A_PIN_OPERSTATE``) attribute nested under the parent +device: + +- ``DPLL_PIN_OPERSTATE_ACTIVE`` - pin is qualified and actively used + by the DPLL +- ``DPLL_PIN_OPERSTATE_STANDBY`` - pin is qualified but not actively + used by the DPLL +- ``DPLL_PIN_OPERSTATE_NO_SIGNAL`` - pin does not have a valid signal +- ``DPLL_PIN_OPERSTATE_QUAL_FAILED`` - pin signal failed qualification + checks Shared pins =========== @@ -250,6 +258,26 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute. ``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature =============================== ======================== +Fractional frequency offset +=========================== + +The fractional frequency offset (FFO) is reported through two attributes +that carry the same measurement at different precisions: + +- ``DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET`` in PPM (parts per million) +- ``DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT`` in PPT (parts per trillion) + +Both attributes appear at the top level of a pin and inside each +``pin-parent-device`` nest. Two FFO types are defined: + +- ``DPLL_FFO_PORT_RXTX_RATE`` - RX vs TX symbol rate offset (top-level) +- ``DPLL_FFO_PIN_DEVICE`` - pin vs parent DPLL offset (per-parent) + +The driver declares which types it supports via the ``supported_ffo`` +bitmask in ``struct dpll_pin_ops``. The core only calls the ``ffo_get`` +callback for types the driver has opted into. The requested type is +passed to the driver in the ``struct dpll_ffo_param``. + Frequency monitor ================= diff --git a/Documentation/firmware-guide/acpi/dsd/motorcomm-yt8xxx-phy.rst b/Documentation/firmware-guide/acpi/dsd/motorcomm-yt8xxx-phy.rst new file mode 100644 index 000000000000..d64a396fac81 --- /dev/null +++ b/Documentation/firmware-guide/acpi/dsd/motorcomm-yt8xxx-phy.rst @@ -0,0 +1,107 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +Motorcomm yt8xxx PHY properties (_DSD) +====================================== + +This document describes ACPI _DSD device properties for Motorcomm yt8xxx +Ethernet PHYs supported by the in-kernel driver in +``drivers/net/phy/motorcomm.c``. + +The properties are exposed on the PHY device object under the MDIO bus ACPI +device (the same objects that are registered via +``fwnode_mdiobus_register_phy()``). MAC-side connection properties such as +``phy-handle`` and ``phy-mode`` are documented in [acpi-mdio-phy]_. + +Property names and semantics are intentionally aligned with the Devicetree +binding [motorcomm-yt8xxx]_ so that the same driver code path +(``device_property_*`` on ``&phydev->mdio.dev``) can consume firmware +described either as Devicetree or ACPI _DSD. + +UUID and placement +================== + +Per [acpi-dsd-properties-rules]_ and [acpi-mdio-phy]_, properties must be +placed in an _DSD package using the standard Device Properties UUID:: + + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") + +Properties +========== + +Unless noted otherwise, integer properties use the same allowed values and +defaults as [motorcomm-yt8xxx]_. + +``rx-internal-delay-ps`` (u32, optional) + RGMII RX internal delay in picoseconds. Only meaningful when the link is + using RGMII modes with RX internal delay; see [motorcomm-yt8xxx]_. + +``tx-internal-delay-ps`` (u32, optional) + RGMII TX internal delay in picoseconds. Only meaningful when the link is + using RGMII modes with TX internal delay; see [motorcomm-yt8xxx]_. + +``motorcomm,clk-out-frequency-hz`` (u32, optional) + Clock output frequency on the PHY clock output pin. Allowed values and + default are defined in [motorcomm-yt8xxx]_. + +``motorcomm,keep-pll-enabled`` (boolean, optional) + If true, keep the PLL enabled even when there is no link (useful for using + the clock output without an Ethernet link). See [motorcomm-yt8xxx]_. + +``motorcomm,auto-sleep-disabled`` (boolean, optional) + If true, disable the PHY auto-sleep behavior described in + [motorcomm-yt8xxx]_. + +``motorcomm,rx-clk-drv-microamp`` (u32, optional) + Drive strength for the ``rx_clk`` RGMII pad in microamps. Allowed values + depend on the configured RGMII LDO voltage; see [motorcomm-yt8xxx]_. + +``motorcomm,rx-data-drv-microamp`` (u32, optional) + Drive strength for the ``rx_data`` and ``rx_ctl`` RGMII pads in microamps. + See [motorcomm-yt8xxx]_. + +``motorcomm,tx-clk-adj-enabled`` (boolean, optional) + Enables adjustments related to ``motorcomm,tx-clk-*-inverted`` usage; see + [motorcomm-yt8xxx]_. + +``motorcomm,tx-clk-10-inverted`` (boolean, optional) +``motorcomm,tx-clk-100-inverted`` (boolean, optional) +``motorcomm,tx-clk-1000-inverted`` (boolean, optional) + Per-speed TX clock inversion options; see [motorcomm-yt8xxx]_. + +ASL example (illustrative) +========================== + +The example below only shows PHY-local _DSD properties. A real platform +still needs a MAC ``phy-handle`` and ``phy-mode`` package as in +[acpi-mdio-phy]_. + +.. code-block:: none + + Scope (\_SB.MDI0) + { + Device (PHY4) + { + Name (_ADR, 0x4) + + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package (2) { "rx-internal-delay-ps", 2100 }, + Package (2) { "tx-internal-delay-ps", 150 }, + Package (2) { "motorcomm,clk-out-frequency-hz", 0 }, + Package (2) { "motorcomm,keep-pll-enabled", 1 }, + Package (2) { "motorcomm,auto-sleep-disabled", 1 }, + } + }) + } + } + +References +========== + +.. [acpi-mdio-phy] Documentation/firmware-guide/acpi/dsd/phy.rst +.. [acpi-dsd-properties-rules] + Documentation/firmware-guide/acpi/DSD-properties-rules.rst +.. [motorcomm-yt8xxx] + Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml diff --git a/Documentation/firmware-guide/acpi/index.rst b/Documentation/firmware-guide/acpi/index.rst index b246902f523f..db9eb7229948 100644 --- a/Documentation/firmware-guide/acpi/index.rst +++ b/Documentation/firmware-guide/acpi/index.rst @@ -11,6 +11,7 @@ ACPI Support dsd/graph dsd/data-node-references dsd/leds + dsd/motorcomm-yt8xxx-phy dsd/phy enumeration osi diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml index 247b147d689f..52ad1e7805d1 100644 --- a/Documentation/netlink/specs/devlink.yaml +++ b/Documentation/netlink/specs/devlink.yaml @@ -234,6 +234,10 @@ definitions: value: 10 - name: binary + - + name: u64-array + value: 129 + - name: rate-tc-index-max type: const diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml index 40465a3d7fc2..2bf83f6732ab 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -138,6 +138,9 @@ definitions: - name: eec doc: dpll drives the Ethernet Equipment Clock + - + name: generic + doc: generic dpll type for devices outside PPS/EEC classes render-max: true - type: enum @@ -212,6 +215,27 @@ definitions: name: selectable doc: pin enabled for automatic input selection render-max: true + - + type: enum + name: pin-operstate + doc: | + defines possible operational states of a pin with respect to its + parent DPLL device, valid values for DPLL_A_PIN_OPERSTATE attribute + entries: + - + name: active + doc: pin is qualified and actively used by the DPLL + value: 1 + - + name: standby + doc: pin is qualified but not actively used by the DPLL + - + name: no-signal + doc: pin does not have a valid signal + - + name: qual-failed + doc: pin signal failed qualification (e.g. frequency or phase monitor) + render-max: true - type: flags name: pin-capabilities @@ -427,12 +451,14 @@ attribute-sets: name: fractional-frequency-offset type: sint doc: | - The FFO (Fractional Frequency Offset) between the RX and TX - symbol rate on the media associated with the pin: - (rx_frequency-tx_frequency)/rx_frequency + The FFO (Fractional Frequency Offset) of the pin. + At top level this represents the RX vs TX symbol rate + offset on the media associated with the pin. Inside + the pin-parent-device nest it represents the frequency + offset between the pin and its parent DPLL device. Value is in PPM (parts per million). - This may be implemented for example for pin of type - PIN_TYPE_SYNCE_ETH_PORT. + This is a lower-precision version of + fractional-frequency-offset-ppt. - name: esync-frequency type: u64 @@ -471,12 +497,14 @@ attribute-sets: name: fractional-frequency-offset-ppt type: sint doc: | - The FFO (Fractional Frequency Offset) of the pin with respect to - the nominal frequency. - Value = (frequency_measured - frequency_nominal) / frequency_nominal + The FFO (Fractional Frequency Offset) of the pin. + At top level this represents the RX vs TX symbol rate + offset on the media associated with the pin. Inside + the pin-parent-device nest it represents the frequency + offset between the pin and its parent DPLL device. Value is in PPT (parts per trillion, 10^-12). - Note: This attribute provides higher resolution than the standard - fractional-frequency-offset (which is in PPM). + This is a higher-precision version of + fractional-frequency-offset. - name: measured-frequency type: u64 @@ -488,6 +516,14 @@ attribute-sets: Value of (DPLL_A_PIN_MEASURED_FREQUENCY % DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of a measured frequency value. + - + name: operstate + type: u32 + enum: pin-operstate + doc: | + Operational state of the pin with respect to its parent DPLL + device. Unlike state (which reflects the administrative intent), + operstate reflects the actual hardware status. - name: pin-parent-device @@ -501,8 +537,14 @@ attribute-sets: name: prio - name: state + - + name: operstate - name: phase-offset + - + name: fractional-frequency-offset + - + name: fractional-frequency-offset-ppt - name: pin-parent-pin subset-of: pin diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml index 1024297b3851..ffec12b46759 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -125,6 +125,7 @@ operations: name: done doc: Handler reports handshake completion attribute-set: done + flags: [admin-perm] do: request: attributes: diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml index b93beb247a11..5f143da7458c 100644 --- a/Documentation/netlink/specs/netdev.yaml +++ b/Documentation/netlink/specs/netdev.yaml @@ -127,7 +127,14 @@ attribute-sets: enum: xsk-flags - name: io-uring-provider-info - attributes: [] + attributes: + - + name: rx-buf-len + type: uint + doc: | + RX buffer length in bytes for this io_uring memory provider. + Reflects the rx_buf_len passed at io_uring zerocopy rx + registration time. - name: page-pool attributes: @@ -649,6 +656,9 @@ operations: - dmabuf - io-uring dump: + request: + attributes: + - ifindex reply: *pp-reply config-cond: page-pool - @@ -692,6 +702,9 @@ operations: - recycle-ring-full - recycle-released-refcnt dump: + request: + attributes: + - info reply: *pp-stats-reply config-cond: page-pool-stats - @@ -792,7 +805,7 @@ operations: name: bind-rx doc: Bind dmabuf to netdev attribute-set: dmabuf - flags: [admin-perm] + flags: [uns-admin-perm] do: request: attributes: diff --git a/Documentation/netlink/specs/ovs_packet.yaml b/Documentation/netlink/specs/ovs_packet.yaml new file mode 100644 index 000000000000..e4c5f24b3b01 --- /dev/null +++ b/Documentation/netlink/specs/ovs_packet.yaml @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +--- +name: ovs_packet +version: 1 +protocol: genetlink-legacy +uapi-header: linux/openvswitch.h + +doc: | + OVS packet execution over generic netlink. + + Only OVS_PACKET_CMD_EXECUTE is exposed as a genl operation. + OVS_PACKET_CMD_MISS and OVS_PACKET_CMD_ACTION are kernel-to-userspace + upcalls sent via genlmsg_unicast() to the vport's upcall_pid and have + no associated genl_ops or multicast group. + + Several attributes in the attribute set (userdata, egress-tun-key, len) + exist for the upcall path and are not used by the EXECUTE operation. + For EXECUTE, packet, key, and actions are mandatory (kernel returns + -EINVAL without them). + +definitions: + - + name: ovs-header + type: struct + members: + - + name: dp-ifindex + type: u32 + +attribute-sets: + - + name: packet + name-prefix: ovs-packet-attr- + enum-name: ovs-packet-attr + attributes: + - + name: packet + type: binary + doc: Packet data, from the start of the Ethernet header. + checks: + min-len: 14 + - + name: key + type: binary + doc: | + Nested OVS_KEY_ATTR_* attributes, extracted flow key. + Defined as binary because the key attribute-set belongs to the + ovs_flow family spec; cross-spec references are not supported. + - + name: actions + type: binary + doc: | + Nested OVS_ACTION_ATTR_* attributes. + Defined as binary for the same reason as key. + - + name: userdata + type: binary + doc: Opaque userspace cookie from OVS_USERSPACE_ATTR_USERDATA. + - + name: egress-tun-key + type: binary + doc: Nested OVS_TUNNEL_KEY_ATTR_* for output tunnel metadata. + - + name: unused1 + type: unused + - + name: unused2 + type: unused + - + name: probe + type: flag + doc: Packet operation is a feature probe, error logging suppressed. + - + name: mru + type: u16 + doc: Maximum received IP fragment size. + - + name: len + type: u32 + doc: Packet size before truncation. + - + name: hash + type: u64 + doc: Packet hash, low 32 bits are skb hash, upper bits are flags. + - + name: upcall-pid + type: u32 + doc: Netlink PID to use for upcalls during EXECUTE processing. + +operations: + fixed-header: ovs-header + name-prefix: ovs-packet-cmd- + list: + - + name: miss + doc: Notify userspace of a flow table miss for a received packet. + value: 1 + attribute-set: packet + event: + attributes: &event-attrs + - packet + - key + - userdata + - actions + - egress-tun-key + - mru + - len + - hash + - + name: action + doc: Notify userspace as requested by an OVS_ACTION_ATTR_USERSPACE action. + value: 2 + attribute-set: packet + event: + attributes: *event-attrs + - + name: execute + doc: Apply actions to a packet. + value: 3 + attribute-set: packet + do: + request: + attributes: + - packet + - key + - actions + - probe + - mru + - hash + - upcall-pid diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml index bfcd6e4ecb85..e9c2ee7e28e0 100644 --- a/Documentation/netlink/specs/psp.yaml +++ b/Documentation/netlink/specs/psp.yaml @@ -13,6 +13,17 @@ definitions: hdr0-aes-gmac-128, hdr0-aes-gmac-256] attribute-sets: + - + name: assoc-dev-info + attributes: + - + name: ifindex + doc: ifindex of an associated network device. + type: u32 + - + name: nsid + doc: Network namespace ID of the associated device. + type: s32 - name: dev attributes: @@ -24,7 +35,9 @@ attribute-sets: min: 1 - name: ifindex - doc: ifindex of the main netdevice linked to the PSP device. + doc: | + ifindex of the main netdevice linked to the PSP device, + or the ifindex to associate with the PSP device. type: u32 - name: psp-versions-cap @@ -38,6 +51,28 @@ attribute-sets: type: u32 enum: version enum-as-flags: true + - + name: assoc-list + doc: List of associated virtual devices. + type: nest + nested-attributes: assoc-dev-info + multi-attr: true + - + name: nsid + doc: | + Network namespace ID for the device to associate/disassociate. + Optional for dev-assoc and dev-disassoc; if not present, the + device is looked up in the caller's network namespace. + type: s32 + - + name: by-association + doc: | + Flag indicating the PSP device is an associated device from a + different network namespace. + Present when in associated namespace, absent when in primary/host + namespace. + type: flag + - name: assoc attributes: @@ -170,6 +205,8 @@ operations: - ifindex - psp-versions-cap - psp-versions-ena + - assoc-list + - by-association pre: psp-device-get-locked post: psp-device-unlock dump: @@ -196,7 +233,7 @@ operations: - psp-versions-ena reply: attributes: [] - pre: psp-device-get-locked + pre: psp-device-get-locked-admin post: psp-device-unlock - name: dev-change-ntf @@ -216,7 +253,7 @@ operations: reply: attributes: - id - pre: psp-device-get-locked + pre: psp-device-get-locked-admin post: psp-device-unlock - name: key-rotate-ntf @@ -281,6 +318,36 @@ operations: post: psp-device-unlock dump: reply: *stats-all + - + name: dev-assoc + doc: Associate a network device with a PSP device. + attribute-set: dev + flags: [admin-perm] + do: + request: + attributes: + - id + - ifindex + - nsid + reply: + attributes: [] + pre: psp-device-get-locked-dev-assoc + post: psp-device-unlock + - + name: dev-disassoc + doc: Disassociate a network device from a PSP device. + attribute-set: dev + flags: [admin-perm] + do: + request: + attributes: + - id + - ifindex + - nsid + reply: + attributes: [] + pre: psp-device-get-locked + post: psp-device-unlock mcast-groups: list: diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index f23aa5f229c5..892979da098e 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -880,19 +880,21 @@ attribute-sets: struct: rtnl-link-stats - name: cost - type: string + type: unused - name: priority - type: string + type: unused - name: master type: u32 - name: wireless - type: string + type: binary + doc: struct iw_event - name: protinfo - type: string + type: binary + doc: A nest of ifla6-attrs or linkinfo-brport-attrs - name: txqlen type: u32 @@ -1351,6 +1353,12 @@ attribute-sets: - name: coupled-control type: u8 + - + name: broadcast-neigh + type: u8 + - + name: lacp-strict + type: u8 - name: bond-ad-info-attrs name-prefix: ifla-bond-ad-info- @@ -1700,6 +1708,9 @@ attribute-sets: - name: backup-nhid type: u32 + - + name: neigh-forward-grat + type: u8 - name: linkinfo-gre-attrs name-prefix: ifla-gre- @@ -1936,6 +1947,15 @@ attribute-sets: - name: gro-hint type: flag + - + name: local + type: u32 + byte-order: big-endian + display-hint: ipv4 + - + name: local6 + type: binary + display-hint: ipv6 - name: linkinfo-hsr-attrs name-prefix: ifla-hsr- diff --git a/Documentation/networking/arcnet-hardware.rst b/Documentation/networking/arcnet-hardware.rst index 20e5075d0d0e..37c016cee353 100644 --- a/Documentation/networking/arcnet-hardware.rst +++ b/Documentation/networking/arcnet-hardware.rst @@ -8,16 +8,13 @@ ARCnet Hardware .. note:: - 1) This file is a supplement to arcnet.rst. Please read that for general - driver configuration help. - 2) This file is no longer Linux-specific. It should probably be moved out - of the kernel sources. Ideas? + This file is a supplement to arcnet.rst. Please read that for general + driver configuration help. Because so many people (myself included) seem to have obtained ARCnet cards -without manuals, this file contains a quick introduction to ARCnet hardware, -some cabling tips, and a listing of all jumper settings I can find. If you -have any settings for your particular card, and/or any other information you -have, do not hesitate to :ref:`email to netdev `. +without manuals, this file contains a quick introduction to ARCnet hardware +and some cabling tips. If you have any other information, do not hesitate to +:ref:`send an email to netdev `. Introduction to ARCnet @@ -135,13 +132,11 @@ And now to the cabling. What you can connect together: network. 2. A card to a passive hub. Remember that all unused connectors on the hub - must be properly terminated with 93 Ohm (or something else if you don't - have the right ones) terminators. + must be properly terminated with 93 Ohm terminators (or something else if you + don't have the right ones), although the network may work without + terminators. - (Avery's note: oops, I didn't know that. Mine (TV cable) works - anyway, though.) - -3. A card to an active hub. Here is no need to terminate the unused +3. A card to an active hub. Here there is no need to terminate the unused connectors except some kind of aesthetic feeling. But, there may not be more than eleven active hubs between any two computers. That of course doesn't limit the number of active hubs on the network. @@ -151,7 +146,7 @@ And now to the cabling. What you can connect together: 5. An active hub to passive hub. Remember that you cannot connect two passive hubs together. The power loss -implied by such a connection is too high for the net to operate reliably. +implied by such a connection is too high for the network to operate reliably. An example of a typical ARCnet network:: @@ -164,8 +159,8 @@ An example of a typical ARCnet network:: | S -The BUS topology is very similar to the one used by Ethernet. The only -difference is in cable and terminators: they should be 93 Ohm. Ethernet +The BUS topology is very similar to the one used by 10BASE2 Ethernet. The only +difference is in cable and terminators: they should be 93 Ohm. 10BASE2 Ethernet uses 50 Ohm impedance. You use T connectors to put the computers on a single line of cable, the bus. You have to put terminators at both ends of the cable. A typical BUS ARCnet network looks like:: @@ -178,7 +173,7 @@ cable. A typical BUS ARCnet network looks like:: T - T connector But that is not all! The two types can be connected together. According to -the official documentation the only way of connecting them is using an active +the official documentation, the only way of connecting them is using an active hub:: A------T------T------TR @@ -187,7 +182,7 @@ hub:: | S -The official docs also state that you can use STAR cards at the ends of +The official docs also state that you can use STAR cards at the ends of a BUS network in place of a BUS card and a terminator:: S------T------T------S @@ -212,7 +207,7 @@ example:: | | S------T----H---S | S S B R S -A basically different cabling scheme is used with Twisted Pair cabling. Each +A completely different cabling scheme is used with Twisted Pair cabling. Each of the TP cards has two RJ (phone-cord style) connectors. The cards are then daisy-chained together using a cable connecting every two neighboring cards. The ends are terminated with RJ 93 Ohm terminators which plug into @@ -264,95 +259,13 @@ of a TP cable between two cards/hubs is 650 meters. Setting the Jumpers =================== -All ARCnet cards should have a total of four or five different settings: - - - the I/O address: this is the "port" your ARCnet card is on. Probed - values in the Linux ARCnet driver are only from 0x200 through 0x3F0. (If - your card has additional ones, which is possible, please tell me.) This - should not be the same as any other device on your system. According to - a doc I got from Novell, MS Windows prefers values of 0x300 or more, - eating net connections on my system (at least) otherwise. My guess is - this may be because, if your card is at 0x2E0, probing for a serial port - at 0x2E8 will reset the card and probably mess things up royally. - - - Avery's favourite: 0x300. - - - the IRQ: on 8-bit cards, it might be 2 (9), 3, 4, 5, or 7. - on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15. - - Make sure this is different from any other card on your system. Note - that IRQ2 is the same as IRQ9, as far as Linux is concerned. You can - "cat /proc/interrupts" for a somewhat complete list of which ones are in - use at any given time. Here is a list of common usages from Vojtech - Pavlik : - - ("Not on bus" means there is no way for a card to generate this - interrupt) - - ====== ========================================================= - IRQ 0 Timer 0 (Not on bus) - IRQ 1 Keyboard (Not on bus) - IRQ 2 IRQ Controller 2 (Not on bus, nor does interrupt the CPU) - IRQ 3 COM2 - IRQ 4 COM1 - IRQ 5 FREE (LPT2 if you have it; sometimes COM3; maybe PLIP) - IRQ 6 Floppy disk controller - IRQ 7 FREE (LPT1 if you don't use the polling driver; PLIP) - IRQ 8 Realtime Clock Interrupt (Not on bus) - IRQ 9 FREE (VGA vertical sync interrupt if enabled) - IRQ 10 FREE - IRQ 11 FREE - IRQ 12 FREE - IRQ 13 Numeric Coprocessor (Not on bus) - IRQ 14 Fixed Disk Controller - IRQ 15 FREE (Fixed Disk Controller 2 if you have it) - ====== ========================================================= - - - .. note:: - - IRQ 9 is used on some video cards for the "vertical retrace" - interrupt. This interrupt would have been handy for things like - video games, as it occurs exactly once per screen refresh, but - unfortunately IBM cancelled this feature starting with the original - VGA and thus many VGA/SVGA cards do not support it. For this - reason, no modern software uses this interrupt and it can almost - always be safely disabled, if your video card supports it at all. - - If your card for some reason CANNOT disable this IRQ (usually there - is a jumper), one solution would be to clip the printed circuit - contact on the board: it's the fourth contact from the left on the - back side. I take no responsibility if you try this. - - - Avery's favourite: IRQ2 (actually IRQ9). Watch that VGA, though. - - - the memory address: Unlike most cards, ARCnets use "shared memory" for - copying buffers around. Make SURE it doesn't conflict with any other - used memory in your system! - - :: - - A0000 - VGA graphics memory (ok if you don't have VGA) - B0000 - Monochrome text mode - C0000 \ One of these is your VGA BIOS - usually C0000. - E0000 / - F0000 - System BIOS - - Anything less than 0xA0000 is, well, a BAD idea since it isn't above - 640k. - - - Avery's favourite: 0xD0000 - - - the station address: Every ARCnet card has its own "unique" network - address from 0 to 255. Unlike Ethernet, you can set this address - yourself with a jumper or switch (or on some cards, with special - software). Since it's only 8 bits, you can only have 254 ARCnet cards - on a network. DON'T use 0 or 255, since these are reserved (although - neat stuff will probably happen if you DO use them). By the way, if you - haven't already guessed, don't set this the same as any other ARCnet on - your network! - - - Avery's favourite: 3 and 4. Not that it matters. + - Every ARCnet card has its own "unique" network address from 0 to 255. + Unlike Ethernet, you can set this address yourself with a jumper or switch + (or on some cards, with special software). Since it's only 8 bits, you can + only have 254 ARCnet cards on a network. DON'T use 0 or 255, since these + are reserved (although neat stuff will probably happen if you DO use them). + By the way, if you haven't already guessed, don't set this the same as any + other ARCnet device on your network! - There may be ETS1 and ETS2 settings. These may or may not make a difference on your card (many manuals call them "reserved"), but are @@ -375,11 +288,13 @@ All ARCnet cards should have a total of four or five different settings: Make sure you set ETS1 and ETS2 to the SAME VALUE for all cards on your network. -Also, on many cards (not mine, though) there are red and green LED's. -Vojtech Pavlik tells me this is what they mean: +LED Indicators +============== + +Many cards have red and green LEDs, which have the following meanings: =============== =============== ===================================== - GREEN RED Status + Green Red Status =============== =============== ===================================== OFF OFF Power off OFF Short flashes Cabling problems (broken cable or not @@ -390,2843 +305,3 @@ Vojtech Pavlik tells me this is what they mean: ON Long flashes Data transfer ON OFF Never happens (maybe when wrong ID) =============== =============== ===================================== - - -The following is all the specific information people have sent me about -their own particular ARCnet cards. It is officially a mess, and contains -huge amounts of duplicated information. I have no time to fix it. If you -want to, PLEASE DO! Just send me a 'diff -u' of all your changes. - -The model # is listed right above specifics for that card, so you should be -able to use your text viewer's "search" function to find the entry you want. -If you don't KNOW what kind of card you have, try looking through the -various diagrams to see if you can tell. - -If your model isn't listed and/or has different settings, PLEASE PLEASE -tell me. I had to figure mine out without the manual, and it WASN'T FUN! - -Even if your ARCnet model isn't listed, but has the same jumpers as another -model that is, please e-mail me to say so. - -Cards Listed in this file (in this order, mostly): - - =============== ======================= ==== - Manufacturer Model # Bits - =============== ======================= ==== - SMC PC100 8 - SMC PC110 8 - SMC PC120 8 - SMC PC130 8 - SMC PC270E 8 - SMC PC500 16 - SMC PC500Longboard 16 - SMC PC550Longboard 16 - SMC PC600 16 - SMC PC710 8 - SMC? LCS-8830(-T) 8/16 - Puredata PDI507 8 - CNet Tech CN120-Series 8 - CNet Tech CN160-Series 16 - Lantech? UM9065L chipset 8 - Acer 5210-003 8 - Datapoint? LAN-ARC-8 8 - Topware TA-ARC/10 8 - Thomas-Conrad 500-6242-0097 REV A 8 - Waterloo? (C)1985 Waterloo Micro. 8 - No Name -- 8/16 - No Name Taiwan R.O.C? 8 - No Name Model 9058 8 - Tiara Tiara Lancard? 8 - =============== ======================= ==== - - -* SMC = Standard Microsystems Corp. -* CNet Tech = CNet Technology, Inc. - -Unclassified Stuff -================== - - - Please send any other information you can find. - - - And some other stuff (more info is welcome!):: - - From: root@ultraworld.xs4all.nl (Timo Hilbrink) - To: apenwarr@foxnet.net (Avery Pennarun) - Date: Wed, 26 Oct 1994 02:10:32 +0000 (GMT) - Reply-To: timoh@xs4all.nl - - [...parts deleted...] - - About the jumpers: On my PC130 there is one more jumper, located near the - cable-connector and it's for changing to star or bus topology; - closed: star - open: bus - On the PC500 are some more jumper-pins, one block labeled with RX,PDN,TXI - and another with ALE,LA17,LA18,LA19 these are undocumented.. - - [...more parts deleted...] - - --- CUT --- - -Standard Microsystems Corp (SMC) -================================ - -PC100, PC110, PC120, PC130 (8-bit cards) and PC500, PC600 (16-bit cards) ------------------------------------------------------------------------- - - - mainly from Avery Pennarun . Values depicted - are from Avery's setup. - - special thanks to Timo Hilbrink for noting that PC120, - 130, 500, and 600 all have the same switches as Avery's PC100. - PC500/600 have several extra, undocumented pins though. (?) - - PC110 settings were verified by Stephen A. Wood - - Also, the JP- and S-numbers probably don't match your card exactly. Try - to find jumpers/switches with the same number of settings - it's - probably more reliable. - -:: - - JP5 [|] : : : : - (IRQ Setting) IRQ2 IRQ3 IRQ4 IRQ5 IRQ7 - Put exactly one jumper on exactly one set of pins. - - - 1 2 3 4 5 6 7 8 9 10 - S1 /----------------------------------\ - (I/O and Memory | 1 1 * 0 0 0 0 * 1 1 0 1 | - addresses) \----------------------------------/ - |--| |--------| |--------| - (a) (b) (m) - - WARNING. It's very important when setting these which way - you're holding the card, and which way you think is '1'! - - If you suspect that your settings are not being made - correctly, try reversing the direction or inverting the - switch positions. - - a: The first digit of the I/O address. - Setting Value - ------- ----- - 00 0 - 01 1 - 10 2 - 11 3 - - b: The second digit of the I/O address. - Setting Value - ------- ----- - 0000 0 - 0001 1 - 0010 2 - ... ... - 1110 E - 1111 F - - The I/O address is in the form ab0. For example, if - a is 0x2 and b is 0xE, the address will be 0x2E0. - - DO NOT SET THIS LESS THAN 0x200!!!!! - - - m: The first digit of the memory address. - Setting Value - ------- ----- - 0000 0 - 0001 1 - 0010 2 - ... ... - 1110 E - 1111 F - - The memory address is in the form m0000. For example, if - m is D, the address will be 0xD0000. - - DO NOT SET THIS TO C0000, F0000, OR LESS THAN A0000! - - 1 2 3 4 5 6 7 8 - S2 /--------------------------\ - (Station Address) | 1 1 0 0 0 0 0 0 | - \--------------------------/ - - Setting Value - ------- ----- - 00000000 00 - 10000000 01 - 01000000 02 - ... - 01111111 FE - 11111111 FF - - Note that this is binary with the digits reversed! - - DO NOT SET THIS TO 0 OR 255 (0xFF)! - - -PC130E/PC270E (8-bit cards) ---------------------------- - - - from Juergen Seifert - -This description has been written by Juergen Seifert -using information from the following Original SMC Manual - - "Configuration Guide for ARCNET(R)-PC130E/PC270 Network - Controller Boards Pub. # 900.044A June, 1989" - -ARCNET is a registered trademark of the Datapoint Corporation -SMC is a registered trademark of the Standard Microsystems Corporation - -The PC130E is an enhanced version of the PC130 board, is equipped with a -standard BNC female connector for connection to RG-62/U coax cable. -Since this board is designed both for point-to-point connection in star -networks and for connection to bus networks, it is downwardly compatible -with all the other standard boards designed for coax networks (that is, -the PC120, PC110 and PC100 star topology boards and the PC220, PC210 and -PC200 bus topology boards). - -The PC270E is an enhanced version of the PC260 board, is equipped with two -modular RJ11-type jacks for connection to twisted pair wiring. -It can be used in a star or a daisy-chained network. - -:: - - 8 7 6 5 4 3 2 1 - ________________________________________________________________ - | | S1 | | - | |_________________| | - | Offs|Base |I/O Addr | - | RAM Addr | ___| - | ___ ___ CR3 |___| - | | \/ | CR4 |___| - | | PROM | ___| - | | | N | | 8 - | | SOCKET | o | | 7 - | |________| d | | 6 - | ___________________ e | | 5 - | | | A | S | 4 - | |oo| EXT2 | | d | 2 | 3 - | |oo| EXT1 | SMC | d | | 2 - | |oo| ROM | 90C63 | r |___| 1 - | |oo| IRQ7 | | |o| _____| - | |oo| IRQ5 | | |o| | J1 | - | |oo| IRQ4 | | STAR |_____| - | |oo| IRQ3 | | | J2 | - | |oo| IRQ2 |___________________| |_____| - |___ ______________| - | | - |_____________________________________________| - -Legend:: - - SMC 90C63 ARCNET Controller / Transceiver /Logic - S1 1-3: I/O Base Address Select - 4-6: Memory Base Address Select - 7-8: RAM Offset Select - S2 1-8: Node ID Select - EXT Extended Timeout Select - ROM ROM Enable Select - STAR Selected - Star Topology (PC130E only) - Deselected - Bus Topology (PC130E only) - CR3/CR4 Diagnostic LEDs - J1 BNC RG62/U Connector (PC130E only) - J1 6-position Telephone Jack (PC270E only) - J2 6-position Telephone Jack (PC270E only) - -Setting one of the switches to Off/Open means "1", On/Closed means "0". - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in group S2 are used to set the node ID. -These switches work in a way similar to the PC100-series cards; see that -entry for more information. - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first three switches in switch group S1 are used to select one -of eight possible I/O Base addresses using the following table:: - - - Switch | Hex I/O - 1 2 3 | Address - -------|-------- - 0 0 0 | 260 - 0 0 1 | 290 - 0 1 0 | 2E0 (Manufacturer's default) - 0 1 1 | 2F0 - 1 0 0 | 300 - 1 0 1 | 350 - 1 1 0 | 380 - 1 1 1 | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer requires 2K of a 16K block of RAM. The base of this -16K block can be located in any of eight positions. -Switches 4-6 of switch group S1 select the Base of the 16K block. -Within that 16K address space, the buffer may be assigned any one of four -positions, determined by the offset, switches 7 and 8 of group S1. - -:: - - Switch | Hex RAM | Hex ROM - 4 5 6 7 8 | Address | Address *) - -----------|---------|----------- - 0 0 0 0 0 | C0000 | C2000 - 0 0 0 0 1 | C0800 | C2000 - 0 0 0 1 0 | C1000 | C2000 - 0 0 0 1 1 | C1800 | C2000 - | | - 0 0 1 0 0 | C4000 | C6000 - 0 0 1 0 1 | C4800 | C6000 - 0 0 1 1 0 | C5000 | C6000 - 0 0 1 1 1 | C5800 | C6000 - | | - 0 1 0 0 0 | CC000 | CE000 - 0 1 0 0 1 | CC800 | CE000 - 0 1 0 1 0 | CD000 | CE000 - 0 1 0 1 1 | CD800 | CE000 - | | - 0 1 1 0 0 | D0000 | D2000 (Manufacturer's default) - 0 1 1 0 1 | D0800 | D2000 - 0 1 1 1 0 | D1000 | D2000 - 0 1 1 1 1 | D1800 | D2000 - | | - 1 0 0 0 0 | D4000 | D6000 - 1 0 0 0 1 | D4800 | D6000 - 1 0 0 1 0 | D5000 | D6000 - 1 0 0 1 1 | D5800 | D6000 - | | - 1 0 1 0 0 | D8000 | DA000 - 1 0 1 0 1 | D8800 | DA000 - 1 0 1 1 0 | D9000 | DA000 - 1 0 1 1 1 | D9800 | DA000 - | | - 1 1 0 0 0 | DC000 | DE000 - 1 1 0 0 1 | DC800 | DE000 - 1 1 0 1 0 | DD000 | DE000 - 1 1 0 1 1 | DD800 | DE000 - | | - 1 1 1 0 0 | E0000 | E2000 - 1 1 1 0 1 | E0800 | E2000 - 1 1 1 1 0 | E1000 | E2000 - 1 1 1 1 1 | E1800 | E2000 - - *) To enable the 8K Boot PROM install the jumper ROM. - The default is jumper ROM not installed. - - -Setting the Timeouts and Interrupt -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The jumpers labeled EXT1 and EXT2 are used to determine the timeout -parameters. These two jumpers are normally left open. - -To select a hardware interrupt level set one (only one!) of the jumpers -IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufacturer's default is IRQ2. - - -Configuring the PC130E for Star or Bus Topology -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The single jumper labeled STAR is used to configure the PC130E board for -star or bus topology. -When the jumper is installed, the board may be used in a star network, when -it is removed, the board can be used in a bus topology. - - -Diagnostic LEDs -^^^^^^^^^^^^^^^ - -Two diagnostic LEDs are visible on the rear bracket of the board. -The green LED monitors the network activity: the red one shows the -board activity:: - - Green | Status Red | Status - -------|------------------- ---------|------------------- - on | normal activity flash/on | data transfer - blink | reconfiguration off | no data transfer; - off | defective board or | incorrect memory or - | node ID is zero | I/O address - - -PC500/PC550 Longboard (16-bit cards) ------------------------------------- - - - from Juergen Seifert - - - .. note:: - - There is another Version of the PC500 called Short Version, which - is different in hard- and software! The most important differences - are: - - - The long board has no Shared memory. - - On the long board the selection of the interrupt is done by binary - coded switch, on the short board directly by jumper. - -[Avery's note: pay special attention to that: the long board HAS NO SHARED -MEMORY. This means the current Linux-ARCnet driver can't use these cards. -I have obtained a PC500Longboard and will be doing some experiments on it in -the future, but don't hold your breath. Thanks again to Juergen Seifert for -his advice about this!] - -This description has been written by Juergen Seifert -using information from the following Original SMC Manual - - "Configuration Guide for SMC ARCNET-PC500/PC550 - Series Network Controller Boards Pub. # 900.033 Rev. A - November, 1989" - -ARCNET is a registered trademark of the Datapoint Corporation -SMC is a registered trademark of the Standard Microsystems Corporation - -The PC500 is equipped with a standard BNC female connector for connection -to RG-62/U coax cable. -The board is designed both for point-to-point connection in star networks -and for connection to bus networks. - -The PC550 is equipped with two modular RJ11-type jacks for connection -to twisted pair wiring. -It can be used in a star or a daisy-chained (BUS) network. - -:: - - 1 - 0 9 8 7 6 5 4 3 2 1 6 5 4 3 2 1 - ____________________________________________________________________ - < | SW1 | | SW2 | | - > |_____________________| |_____________| | - < IRQ |I/O Addr | - > ___| - < CR4 |___| - > CR3 |___| - < ___| - > N | | 8 - < o | | 7 - > d | S | 6 - < e | W | 5 - > A | 3 | 4 - < d | | 3 - > d | | 2 - < r |___| 1 - > |o| _____| - < |o| | J1 | - > 3 1 JP6 |_____| - < |o|o| JP2 | J2 | - > |o|o| |_____| - < 4 2__ ______________| - > | | | - <____| |_____________________________________________| - -Legend:: - - SW1 1-6: I/O Base Address Select - 7-10: Interrupt Select - SW2 1-6: Reserved for Future Use - SW3 1-8: Node ID Select - JP2 1-4: Extended Timeout Select - JP6 Selected - Star Topology (PC500 only) - Deselected - Bus Topology (PC500 only) - CR3 Green Monitors Network Activity - CR4 Red Monitors Board Activity - J1 BNC RG62/U Connector (PC500 only) - J1 6-position Telephone Jack (PC550 only) - J2 6-position Telephone Jack (PC550 only) - -Setting one of the switches to Off/Open means "1", On/Closed means "0". - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in group SW3 are used to set the node ID. Each node -attached to the network must have an unique node ID which must be -different from 0. -Switch 1 serves as the least significant bit (LSB). - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Value - -------|------- - 1 | 1 - 2 | 2 - 3 | 4 - 4 | 8 - 5 | 16 - 6 | 32 - 7 | 64 - 8 | 128 - -Some Examples:: - - Switch | Hex | Decimal - 8 7 6 5 4 3 2 1 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed - 0 0 0 0 0 0 0 1 | 1 | 1 - 0 0 0 0 0 0 1 0 | 2 | 2 - 0 0 0 0 0 0 1 1 | 3 | 3 - . . . | | - 0 1 0 1 0 1 0 1 | 55 | 85 - . . . | | - 1 0 1 0 1 0 1 0 | AA | 170 - . . . | | - 1 1 1 1 1 1 0 1 | FD | 253 - 1 1 1 1 1 1 1 0 | FE | 254 - 1 1 1 1 1 1 1 1 | FF | 255 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first six switches in switch group SW1 are used to select one -of 32 possible I/O Base addresses using the following table:: - - Switch | Hex I/O - 6 5 4 3 2 1 | Address - -------------|-------- - 0 1 0 0 0 0 | 200 - 0 1 0 0 0 1 | 210 - 0 1 0 0 1 0 | 220 - 0 1 0 0 1 1 | 230 - 0 1 0 1 0 0 | 240 - 0 1 0 1 0 1 | 250 - 0 1 0 1 1 0 | 260 - 0 1 0 1 1 1 | 270 - 0 1 1 0 0 0 | 280 - 0 1 1 0 0 1 | 290 - 0 1 1 0 1 0 | 2A0 - 0 1 1 0 1 1 | 2B0 - 0 1 1 1 0 0 | 2C0 - 0 1 1 1 0 1 | 2D0 - 0 1 1 1 1 0 | 2E0 (Manufacturer's default) - 0 1 1 1 1 1 | 2F0 - 1 1 0 0 0 0 | 300 - 1 1 0 0 0 1 | 310 - 1 1 0 0 1 0 | 320 - 1 1 0 0 1 1 | 330 - 1 1 0 1 0 0 | 340 - 1 1 0 1 0 1 | 350 - 1 1 0 1 1 0 | 360 - 1 1 0 1 1 1 | 370 - 1 1 1 0 0 0 | 380 - 1 1 1 0 0 1 | 390 - 1 1 1 0 1 0 | 3A0 - 1 1 1 0 1 1 | 3B0 - 1 1 1 1 0 0 | 3C0 - 1 1 1 1 0 1 | 3D0 - 1 1 1 1 1 0 | 3E0 - 1 1 1 1 1 1 | 3F0 - - -Setting the Interrupt -^^^^^^^^^^^^^^^^^^^^^ - -Switches seven through ten of switch group SW1 are used to select the -interrupt level. The interrupt level is binary coded, so selections -from 0 to 15 would be possible, but only the following eight values will -be supported: 3, 4, 5, 7, 9, 10, 11, 12. - -:: - - Switch | IRQ - 10 9 8 7 | - ---------|-------- - 0 0 1 1 | 3 - 0 1 0 0 | 4 - 0 1 0 1 | 5 - 0 1 1 1 | 7 - 1 0 0 1 | 9 (=2) (default) - 1 0 1 0 | 10 - 1 0 1 1 | 11 - 1 1 0 0 | 12 - - -Setting the Timeouts -^^^^^^^^^^^^^^^^^^^^ - -The two jumpers JP2 (1-4) are used to determine the timeout parameters. -These two jumpers are normally left open. -Refer to the COM9026 Data Sheet for alternate configurations. - - -Configuring the PC500 for Star or Bus Topology -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The single jumper labeled JP6 is used to configure the PC500 board for -star or bus topology. -When the jumper is installed, the board may be used in a star network, when -it is removed, the board can be used in a bus topology. - - -Diagnostic LEDs -^^^^^^^^^^^^^^^ - -Two diagnostic LEDs are visible on the rear bracket of the board. -The green LED monitors the network activity: the red one shows the -board activity:: - - Green | Status Red | Status - -------|------------------- ---------|------------------- - on | normal activity flash/on | data transfer - blink | reconfiguration off | no data transfer; - off | defective board or | incorrect memory or - | node ID is zero | I/O address - - -PC710 (8-bit card) ------------------- - - - from J.S. van Oosten - -Note: this data is gathered by experimenting and looking at info of other -cards. However, I'm sure I got 99% of the settings right. - -The SMC710 card resembles the PC270 card, but is much more basic (i.e. no -LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing:: - - _______________________________________ - | +---------+ +---------+ |____ - | | S2 | | S1 | | - | +---------+ +---------+ | - | | - | +===+ __ | - | | R | | | X-tal ###___ - | | O | |__| ####__'| - | | M | || ### - | +===+ | - | | - | .. JP1 +----------+ | - | .. | big chip | | - | .. | 90C63 | | - | .. | | | - | .. +----------+ | - ------- ----------- - ||||||||||||||||||||| - -The row of jumpers at JP1 actually consists of 8 jumpers, (sometimes -labelled) the same as on the PC270, from top to bottom: EXT2, EXT1, ROM, -IRQ7, IRQ5, IRQ4, IRQ3, IRQ2 (gee, wonder what they would do? :-) ) - -S1 and S2 perform the same function as on the PC270, only their numbers -are swapped (S1 is the nodeaddress, S2 sets IO- and RAM-address). - -I know it works when connected to a PC110 type ARCnet board. - - -***************************************************************************** - -Possibly SMC -============ - -LCS-8830(-T) (8 and 16-bit cards) ---------------------------------- - - - from Mathias Katzer - - Marek Michalkiewicz says the - LCS-8830 is slightly different from LCS-8830-T. These are 8 bit, BUS - only (the JP0 jumper is hardwired), and BNC only. - -This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC, -nowhere else, not even on the few Xeroxed sheets from the manual). - -SMC ARCnet Board Type LCS-8830-T:: - - ------------------------------------ - | | - | JP3 88 8 JP2 | - | ##### | \ | - | ##### ET1 ET2 ###| - | 8 ###| - | U3 SW 1 JP0 ###| Phone Jacks - | -- ###| - | | | | - | | | SW2 | - | | | | - | | | ##### | - | -- ##### #### BNC Connector - | #### - | 888888 JP1 | - | 234567 | - -- ------- - ||||||||||||||||||||||||||| - -------------------------- - - - SW1: DIP-Switches for Station Address - SW2: DIP-Switches for Memory Base and I/O Base addresses - - JP0: If closed, internal termination on (default open) - JP1: IRQ Jumpers - JP2: Boot-ROM enabled if closed - JP3: Jumpers for response timeout - - U3: Boot-ROM Socket - - - ET1 ET2 Response Time Idle Time Reconfiguration Time - - 78 86 840 - X 285 316 1680 - X 563 624 1680 - X X 1130 1237 1680 - - (X means closed jumper) - - (DIP-Switch downwards means "0") - -The station address is binary-coded with SW1. - -The I/O base address is coded with DIP-Switches 6,7 and 8 of SW2: - -======== ======== -Switches Base -678 Address -======== ======== -000 260-26f -100 290-29f -010 2e0-2ef -110 2f0-2ff -001 300-30f -101 350-35f -011 380-38f -111 3e0-3ef -======== ======== - - -DIP Switches 1-5 of SW2 encode the RAM and ROM Address Range: - -======== ============= ================ -Switches RAM ROM -12345 Address Range Address Range -======== ============= ================ -00000 C:0000-C:07ff C:2000-C:3fff -10000 C:0800-C:0fff -01000 C:1000-C:17ff -11000 C:1800-C:1fff -00100 C:4000-C:47ff C:6000-C:7fff -10100 C:4800-C:4fff -01100 C:5000-C:57ff -11100 C:5800-C:5fff -00010 C:C000-C:C7ff C:E000-C:ffff -10010 C:C800-C:Cfff -01010 C:D000-C:D7ff -11010 C:D800-C:Dfff -00110 D:0000-D:07ff D:2000-D:3fff -10110 D:0800-D:0fff -01110 D:1000-D:17ff -11110 D:1800-D:1fff -00001 D:4000-D:47ff D:6000-D:7fff -10001 D:4800-D:4fff -01001 D:5000-D:57ff -11001 D:5800-D:5fff -00101 D:8000-D:87ff D:A000-D:bfff -10101 D:8800-D:8fff -01101 D:9000-D:97ff -11101 D:9800-D:9fff -00011 D:C000-D:c7ff D:E000-D:ffff -10011 D:C800-D:cfff -01011 D:D000-D:d7ff -11011 D:D800-D:dfff -00111 E:0000-E:07ff E:2000-E:3fff -10111 E:0800-E:0fff -01111 E:1000-E:17ff -11111 E:1800-E:1fff -======== ============= ================ - - -PureData Corp -============= - -PDI507 (8-bit card) --------------------- - - - from Mark Rejhon (slight modifications by Avery) - - Avery's note: I think PDI508 cards (but definitely NOT PDI508Plus cards) - are mostly the same as this. PDI508Plus cards appear to be mainly - software-configured. - -Jumpers: - - There is a jumper array at the bottom of the card, near the edge - connector. This array is labelled J1. They control the IRQs and - something else. Put only one jumper on the IRQ pins. - - ETS1, ETS2 are for timing on very long distance networks. See the - more general information near the top of this file. - - There is a J2 jumper on two pins. A jumper should be put on them, - since it was already there when I got the card. I don't know what - this jumper is for though. - - There is a two-jumper array for J3. I don't know what it is for, - but there were already two jumpers on it when I got the card. It's - a six pin grid in a two-by-three fashion. The jumpers were - configured as follows:: - - .-------. - o | o o | - :-------: ------> Accessible end of card with connectors - o | o o | in this direction -------> - `-------' - -Carl de Billy explains J3 and J4: - - J3 Diagram:: - - .-------. - o | o o | - :-------: TWIST Technology - o | o o | - `-------' - .-------. - | o o | o - :-------: COAX Technology - | o o | o - `-------' - - - If using coax cable in a bus topology the J4 jumper must be removed; - place it on one pin. - - - If using bus topology with twisted pair wiring move the J3 - jumpers so they connect the middle pin and the pins closest to the RJ11 - Connectors. Also the J4 jumper must be removed; place it on one pin of - J4 jumper for storage. - - - If using star topology with twisted pair wiring move the J3 - jumpers so they connect the middle pin and the pins closest to the RJ11 - connectors. - - -DIP Switches: - - The DIP switches accessible on the accessible end of the card while - it is installed, is used to set the ARCnet address. There are 8 - switches. Use an address from 1 to 254 - - ========== ========================= - Switch No. ARCnet address - 12345678 - ========== ========================= - 00000000 FF (Don't use this!) - 00000001 FE - 00000010 FD - ... - 11111101 2 - 11111110 1 - 11111111 0 (Don't use this!) - ========== ========================= - - There is another array of eight DIP switches at the top of the - card. There are five labelled MS0-MS4 which seem to control the - memory address, and another three labelled IO0-IO2 which seem to - control the base I/O address of the card. - - This was difficult to test by trial and error, and the I/O addresses - are in a weird order. This was tested by setting the DIP switches, - rebooting the computer, and attempting to load ARCETHER at various - addresses (mostly between 0x200 and 0x400). The address that caused - the red transmit LED to blink, is the one that I thought works. - - Also, the address 0x3D0 seem to have a special meaning, since the - ARCETHER packet driver loaded fine, but without the red LED - blinking. I don't know what 0x3D0 is for though. I recommend using - an address of 0x300 since Windows may not like addresses below - 0x300. - - ============= =========== - IO Switch No. I/O address - 210 - ============= =========== - 111 0x260 - 110 0x290 - 101 0x2E0 - 100 0x2F0 - 011 0x300 - 010 0x350 - 001 0x380 - 000 0x3E0 - ============= =========== - - The memory switches set a reserved address space of 0x1000 bytes - (0x100 segment units, or 4k). For example if I set an address of - 0xD000, it will use up addresses 0xD000 to 0xD100. - - The memory switches were tested by booting using QEMM386 stealth, - and using LOADHI to see what address automatically became excluded - from the upper memory regions, and then attempting to load ARCETHER - using these addresses. - - I recommend using an ARCnet memory address of 0xD000, and putting - the EMS page frame at 0xC000 while using QEMM stealth mode. That - way, you get contiguous high memory from 0xD100 almost all the way - the end of the megabyte. - - Memory Switch 0 (MS0) didn't seem to work properly when set to OFF - on my card. It could be malfunctioning on my card. Experiment with - it ON first, and if it doesn't work, set it to OFF. (It may be a - modifier for the 0x200 bit?) - - ============= ============================================ - MS Switch No. - 43210 Memory address - ============= ============================================ - 00001 0xE100 (guessed - was not detected by QEMM) - 00011 0xE000 (guessed - was not detected by QEMM) - 00101 0xDD00 - 00111 0xDC00 - 01001 0xD900 - 01011 0xD800 - 01101 0xD500 - 01111 0xD400 - 10001 0xD100 - 10011 0xD000 - 10101 0xCD00 - 10111 0xCC00 - 11001 0xC900 (guessed - crashes tested system) - 11011 0xC800 (guessed - crashes tested system) - 11101 0xC500 (guessed - crashes tested system) - 11111 0xC400 (guessed - crashes tested system) - ============= ============================================ - -CNet Technology Inc. (8-bit cards) -================================== - -120 Series (8-bit cards) ------------------------- - - from Juergen Seifert - -This description has been written by Juergen Seifert -using information from the following Original CNet Manual - - "ARCNET USER'S MANUAL for - CN120A - CN120AB - CN120TP - CN120ST - CN120SBT - P/N:12-01-0007 - Revision 3.00" - -ARCNET is a registered trademark of the Datapoint Corporation - -- P/N 120A ARCNET 8 bit XT/AT Star -- P/N 120AB ARCNET 8 bit XT/AT Bus -- P/N 120TP ARCNET 8 bit XT/AT Twisted Pair -- P/N 120ST ARCNET 8 bit XT/AT Star, Twisted Pair -- P/N 120SBT ARCNET 8 bit XT/AT Star, Bus, Twisted Pair - -:: - - __________________________________________________________________ - | | - | ___| - | LED |___| - | ___| - | N | | ID7 - | o | | ID6 - | d | S | ID5 - | e | W | ID4 - | ___________________ A | 2 | ID3 - | | | d | | ID2 - | | | 1 2 3 4 5 6 7 8 d | | ID1 - | | | _________________ r |___| ID0 - | | 90C65 || SW1 | ____| - | JP 8 7 | ||_________________| | | - | |o|o| JP1 | | | J2 | - | |o|o| |oo| | | JP 1 1 1 | | - | ______________ | | 0 1 2 |____| - | | PROM | |___________________| |o|o|o| _____| - | > SOCKET | JP 6 5 4 3 2 |o|o|o| | J1 | - | |______________| |o|o|o|o|o| |o|o|o| |_____| - |_____ |o|o|o|o|o| ______________| - | | - |_____________________________________________| - -Legend:: - - 90C65 ARCNET Probe - S1 1-5: Base Memory Address Select - 6-8: Base I/O Address Select - S2 1-8: Node ID Select (ID0-ID7) - JP1 ROM Enable Select - JP2 IRQ2 - JP3 IRQ3 - JP4 IRQ4 - JP5 IRQ5 - JP6 IRQ7 - JP7/JP8 ET1, ET2 Timeout Parameters - JP10/JP11 Coax / Twisted Pair Select (CN120ST/SBT only) - JP12 Terminator Select (CN120AB/ST/SBT only) - J1 BNC RG62/U Connector (all except CN120TP) - J2 Two 6-position Telephone Jack (CN120TP/ST/SBT only) - -Setting one of the switches to Off means "1", On means "0". - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in SW2 are used to set the node ID. Each node attached -to the network must have an unique node ID which must be different from 0. -Switch 1 (ID0) serves as the least significant bit (LSB). - -The node ID is the sum of the values of all switches set to "1" -These values are: - - ======= ====== ===== - Switch Label Value - ======= ====== ===== - 1 ID0 1 - 2 ID1 2 - 3 ID2 4 - 4 ID3 8 - 5 ID4 16 - 6 ID5 32 - 7 ID6 64 - 8 ID7 128 - ======= ====== ===== - -Some Examples:: - - Switch | Hex | Decimal - 8 7 6 5 4 3 2 1 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed - 0 0 0 0 0 0 0 1 | 1 | 1 - 0 0 0 0 0 0 1 0 | 2 | 2 - 0 0 0 0 0 0 1 1 | 3 | 3 - . . . | | - 0 1 0 1 0 1 0 1 | 55 | 85 - . . . | | - 1 0 1 0 1 0 1 0 | AA | 170 - . . . | | - 1 1 1 1 1 1 0 1 | FD | 253 - 1 1 1 1 1 1 1 0 | FE | 254 - 1 1 1 1 1 1 1 1 | FF | 255 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The last three switches in switch block SW1 are used to select one -of eight possible I/O Base addresses using the following table:: - - - Switch | Hex I/O - 6 7 8 | Address - ------------|-------- - ON ON ON | 260 - OFF ON ON | 290 - ON OFF ON | 2E0 (Manufacturer's default) - OFF OFF ON | 2F0 - ON ON OFF | 300 - OFF ON OFF | 350 - ON OFF OFF | 380 - OFF OFF OFF | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer (RAM) requires 2K. The base of this buffer can be -located in any of eight positions. The address of the Boot Prom is -memory base + 8K or memory base + 0x2000. -Switches 1-5 of switch block SW1 select the Memory Base address. - -:: - - Switch | Hex RAM | Hex ROM - 1 2 3 4 5 | Address | Address *) - --------------------|---------|----------- - ON ON ON ON ON | C0000 | C2000 - ON ON OFF ON ON | C4000 | C6000 - ON ON ON OFF ON | CC000 | CE000 - ON ON OFF OFF ON | D0000 | D2000 (Manufacturer's default) - ON ON ON ON OFF | D4000 | D6000 - ON ON OFF ON OFF | D8000 | DA000 - ON ON ON OFF OFF | DC000 | DE000 - ON ON OFF OFF OFF | E0000 | E2000 - - *) To enable the Boot ROM install the jumper JP1 - -.. note:: - - Since the switches 1 and 2 are always set to ON it may be possible - that they can be used to add an offset of 2K, 4K or 6K to the base - address, but this feature is not documented in the manual and I - haven't tested it yet. - - -Setting the Interrupt Line -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To select a hardware interrupt level install one (only one!) of the jumpers -JP2, JP3, JP4, JP5, JP6. JP2 is the default:: - - Jumper | IRQ - -------|----- - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | 7 - - -Setting the Internal Terminator on CN120AB/TP/SBT -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The jumper JP12 is used to enable the internal terminator:: - - ----- - 0 | 0 | - ----- ON | | ON - | 0 | | 0 | - | | OFF ----- OFF - | 0 | 0 - ----- - Terminator Terminator - disabled enabled - - -Selecting the Connector Type on CN120ST/SBT -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: - - JP10 JP11 JP10 JP11 - ----- ----- - 0 0 | 0 | | 0 | - ----- ----- | | | | - | 0 | | 0 | | 0 | | 0 | - | | | | ----- ----- - | 0 | | 0 | 0 0 - ----- ----- - Coaxial Cable Twisted Pair Cable - (Default) - - -Setting the Timeout Parameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The jumpers labeled EXT1 and EXT2 are used to determine the timeout -parameters. These two jumpers are normally left open. - - -CNet Technology Inc. (16-bit cards) -=================================== - -160 Series (16-bit cards) -------------------------- - - from Juergen Seifert - -This description has been written by Juergen Seifert -using information from the following Original CNet Manual - - "ARCNET USER'S MANUAL for - CN160A CN160AB CN160TP - P/N:12-01-0006 Revision 3.00" - -ARCNET is a registered trademark of the Datapoint Corporation - -- P/N 160A ARCNET 16 bit XT/AT Star -- P/N 160AB ARCNET 16 bit XT/AT Bus -- P/N 160TP ARCNET 16 bit XT/AT Twisted Pair - -:: - - ___________________________________________________________________ - < _________________________ ___| - > |oo| JP2 | | LED |___| - < |oo| JP1 | 9026 | LED |___| - > |_________________________| ___| - < N | | ID7 - > 1 o | | ID6 - < 1 2 3 4 5 6 7 8 9 0 d | S | ID5 - > _______________ _____________________ e | W | ID4 - < | PROM | | SW1 | A | 2 | ID3 - > > SOCKET | |_____________________| d | | ID2 - < |_______________| | IO-Base | MEM | d | | ID1 - > r |___| ID0 - < ____| - > | | - < | J1 | - > | | - < |____| - > 1 1 1 1 | - < 3 4 5 6 7 JP 8 9 0 1 2 3 | - > |o|o|o|o|o| |o|o|o|o|o|o| | - < |o|o|o|o|o| __ |o|o|o|o|o|o| ___________| - > | | | - <____________| |_______________________________________| - -Legend:: - - 9026 ARCNET Probe - SW1 1-6: Base I/O Address Select - 7-10: Base Memory Address Select - SW2 1-8: Node ID Select (ID0-ID7) - JP1/JP2 ET1, ET2 Timeout Parameters - JP3-JP13 Interrupt Select - J1 BNC RG62/U Connector (CN160A/AB only) - J1 Two 6-position Telephone Jack (CN160TP only) - LED - -Setting one of the switches to Off means "1", On means "0". - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in SW2 are used to set the node ID. Each node attached -to the network must have an unique node ID which must be different from 0. -Switch 1 (ID0) serves as the least significant bit (LSB). - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Label | Value - -------|-------|------- - 1 | ID0 | 1 - 2 | ID1 | 2 - 3 | ID2 | 4 - 4 | ID3 | 8 - 5 | ID4 | 16 - 6 | ID5 | 32 - 7 | ID6 | 64 - 8 | ID7 | 128 - -Some Examples:: - - Switch | Hex | Decimal - 8 7 6 5 4 3 2 1 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed - 0 0 0 0 0 0 0 1 | 1 | 1 - 0 0 0 0 0 0 1 0 | 2 | 2 - 0 0 0 0 0 0 1 1 | 3 | 3 - . . . | | - 0 1 0 1 0 1 0 1 | 55 | 85 - . . . | | - 1 0 1 0 1 0 1 0 | AA | 170 - . . . | | - 1 1 1 1 1 1 0 1 | FD | 253 - 1 1 1 1 1 1 1 0 | FE | 254 - 1 1 1 1 1 1 1 1 | FF | 255 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first six switches in switch block SW1 are used to select the I/O Base -address using the following table:: - - Switch | Hex I/O - 1 2 3 4 5 6 | Address - ------------------------|-------- - OFF ON ON OFF OFF ON | 260 - OFF ON OFF ON ON OFF | 290 - OFF ON OFF OFF OFF ON | 2E0 (Manufacturer's default) - OFF ON OFF OFF OFF OFF | 2F0 - OFF OFF ON ON ON ON | 300 - OFF OFF ON OFF ON OFF | 350 - OFF OFF OFF ON ON ON | 380 - OFF OFF OFF OFF OFF ON | 3E0 - -Note: Other IO-Base addresses seem to be selectable, but only the above - combinations are documented. - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The switches 7-10 of switch block SW1 are used to select the Memory -Base address of the RAM (2K) and the PROM:: - - Switch | Hex RAM | Hex ROM - 7 8 9 10 | Address | Address - ----------------|---------|----------- - OFF OFF ON ON | C0000 | C8000 - OFF OFF ON OFF | D0000 | D8000 (Default) - OFF OFF OFF ON | E0000 | E8000 - -.. note:: - - Other MEM-Base addresses seem to be selectable, but only the above - combinations are documented. - - -Setting the Interrupt Line -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To select a hardware interrupt level install one (only one!) of the jumpers -JP3 through JP13 using the following table:: - - Jumper | IRQ - -------|----------------- - 3 | 14 - 4 | 15 - 5 | 12 - 6 | 11 - 7 | 10 - 8 | 3 - 9 | 4 - 10 | 5 - 11 | 6 - 12 | 7 - 13 | 2 (=9) Default! - -.. note:: - - - Do not use JP11=IRQ6, it may conflict with your Floppy Disk - Controller - - Use JP3=IRQ14 only, if you don't have an IDE-, MFM-, or RLL- - Hard Disk, it may conflict with their controllers - - -Setting the Timeout Parameters ------------------------------- - -The jumpers labeled JP1 and JP2 are used to determine the timeout -parameters. These two jumpers are normally left open. - - -Lantech -======= - -8-bit card, unknown model -------------------------- - - from Vlad Lungu - his e-mail address seemed broken at - the time I tried to reach him. Sorry Vlad, if you didn't get my reply. - -:: - - ________________________________________________________________ - | 1 8 | - | ___________ __| - | | SW1 | LED |__| - | |__________| | - | ___| - | _____________________ |S | 8 - | | | |W | - | | | |2 | - | | | |__| 1 - | | UM9065L | |o| JP4 ____|____ - | | | |o| | CN | - | | | |________| - | | | | - | |___________________| | - | | - | | - | _____________ | - | | | | - | | PROM | |ooooo| JP6 | - | |____________| |ooooo| | - |_____________ _ _| - |____________________________________________| |__| - - -UM9065L : ARCnet Controller - -SW 1 : Shared Memory Address and I/O Base - -:: - - ON=0 - - 12345|Memory Address - -----|-------------- - 00001| D4000 - 00010| CC000 - 00110| D0000 - 01110| D1000 - 01101| D9000 - 10010| CC800 - 10011| DC800 - 11110| D1800 - -It seems that the bits are considered in reverse order. Also, you must -observe that some of those addresses are unusual and I didn't probe them; I -used a memory dump in DOS to identify them. For the 00000 configuration and -some others that I didn't write here the card seems to conflict with the -video card (an S3 GENDAC). I leave the full decoding of those addresses to -you. - -:: - - 678| I/O Address - ---|------------ - 000| 260 - 001| failed probe - 010| 2E0 - 011| 380 - 100| 290 - 101| 350 - 110| failed probe - 111| 3E0 - - SW 2 : Node ID (binary coded) - - JP 4 : Boot PROM enable CLOSE - enabled - OPEN - disabled - - JP 6 : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6) - - -Acer -==== - -8-bit card, Model 5210-003 --------------------------- - - - from Vojtech Pavlik using portions of the existing - arcnet-hardware file. - -This is a 90C26 based card. Its configuration seems similar to the SMC -PC100, but has some additional jumpers I don't know the meaning of. - -:: - - __ - | | - ___________|__|_________________________ - | | | | - | | BNC | | - | |______| ___| - | _____________________ |___ - | | | | - | | Hybrid IC | | - | | | o|o J1 | - | |_____________________| 8|8 | - | 8|8 J5 | - | o|o | - | 8|8 | - |__ 8|8 | - (|__| LED o|o | - | 8|8 | - | 8|8 J15 | - | | - | _____ | - | | | _____ | - | | | | | ___| - | | | | | | - | _____ | ROM | | UFS | | - | | | | | | | | - | | | ___ | | | | | - | | | | | |__.__| |__.__| | - | | NCR | |XTL| _____ _____ | - | | | |___| | | | | | - | |90C26| | | | | | - | | | | RAM | | UFS | | - | | | J17 o|o | | | | | - | | | J16 o|o | | | | | - | |__.__| |__.__| |__.__| | - | ___ | - | | |8 | - | |SW2| | - | | | | - | |___|1 | - | ___ | - | | |10 J18 o|o | - | | | o|o | - | |SW1| o|o | - | | | J21 o|o | - | |___|1 | - | | - |____________________________________| - - -Legend:: - - 90C26 ARCNET Chip - XTL 20 MHz Crystal - SW1 1-6 Base I/O Address Select - 7-10 Memory Address Select - SW2 1-8 Node ID Select (ID0-ID7) - J1-J5 IRQ Select - J6-J21 Unknown (Probably extra timeouts & ROM enable ...) - LED1 Activity LED - BNC Coax connector (STAR ARCnet) - RAM 2k of SRAM - ROM Boot ROM socket - UFS Unidentified Flying Sockets - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in SW2 are used to set the node ID. Each node attached -to the network must have an unique node ID which must not be 0. -Switch 1 (ID0) serves as the least significant bit (LSB). - -Setting one of the switches to OFF means "1", ON means "0". - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Value - -------|------- - 1 | 1 - 2 | 2 - 3 | 4 - 4 | 8 - 5 | 16 - 6 | 32 - 7 | 64 - 8 | 128 - -Don't set this to 0 or 255; these values are reserved. - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The switches 1 to 6 of switch block SW1 are used to select one -of 32 possible I/O Base addresses using the following tables:: - - | Hex - Switch | Value - -------|------- - 1 | 200 - 2 | 100 - 3 | 80 - 4 | 40 - 5 | 20 - 6 | 10 - -The I/O address is sum of all switches set to "1". Remember that -the I/O address space below 0x200 is RESERVED for mainboard, so -switch 1 should be ALWAYS SET TO OFF. - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer (RAM) requires 2K. The base of this buffer can be -located in any of sixteen positions. However, the addresses below -A0000 are likely to cause system hang because there's main RAM. - -Jumpers 7-10 of switch block SW1 select the Memory Base address:: - - Switch | Hex RAM - 7 8 9 10 | Address - ----------------|--------- - OFF OFF OFF OFF | F0000 (conflicts with main BIOS) - OFF OFF OFF ON | E0000 - OFF OFF ON OFF | D0000 - OFF OFF ON ON | C0000 (conflicts with video BIOS) - OFF ON OFF OFF | B0000 (conflicts with mono video) - OFF ON OFF ON | A0000 (conflicts with graphics) - - -Setting the Interrupt Line -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Jumpers 1-5 of the jumper block J1 control the IRQ level. ON means -shorted, OFF means open:: - - Jumper | IRQ - 1 2 3 4 5 | - ---------------------------- - ON OFF OFF OFF OFF | 7 - OFF ON OFF OFF OFF | 5 - OFF OFF ON OFF OFF | 4 - OFF OFF OFF ON OFF | 3 - OFF OFF OFF OFF ON | 2 - - -Unknown jumpers & sockets -^^^^^^^^^^^^^^^^^^^^^^^^^ - -I know nothing about these. I just guess that J16&J17 are timeout -jumpers and maybe one of J18-J21 selects ROM. Also J6-J10 and -J11-J15 are connecting IRQ2-7 to some pins on the UFSs. I can't -guess the purpose. - -Datapoint? -========== - -LAN-ARC-8, an 8-bit card ------------------------- - - - from Vojtech Pavlik - -This is another SMC 90C65-based ARCnet card. I couldn't identify the -manufacturer, but it might be DataPoint, because the card has the -original arcNet logo in its upper right corner. - -:: - - _______________________________________________________ - | _________ | - | | SW2 | ON arcNet | - | |_________| OFF ___| - | _____________ 1 ______ 8 | | 8 - | | | SW1 | XTAL | ____________ | S | - | > RAM (2k) | |______|| | | W | - | |_____________| | H | | 3 | - | _________|_____ y | |___| 1 - | _________ | | |b | | - | |_________| | | |r | | - | | SMC | |i | | - | | 90C65| |d | | - | _________ | | | | | - | | SW1 | ON | | |I | | - | |_________| OFF |_________|_____/C | _____| - | 1 8 | | | |___ - | ______________ | | | BNC |___| - | | | |____________| |_____| - | > EPROM SOCKET | _____________ | - | |______________| |_____________| | - | ______________| - | | - |________________________________________| - -Legend:: - - 90C65 ARCNET Chip - SW1 1-5: Base Memory Address Select - 6-8: Base I/O Address Select - SW2 1-8: Node ID Select - SW3 1-5: IRQ Select - 6-7: Extra Timeout - 8 : ROM Enable - BNC Coax connector - XTAL 20 MHz Crystal - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in SW3 are used to set the node ID. Each node attached -to the network must have an unique node ID which must not be 0. -Switch 1 serves as the least significant bit (LSB). - -Setting one of the switches to Off means "1", On means "0". - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Value - -------|------- - 1 | 1 - 2 | 2 - 3 | 4 - 4 | 8 - 5 | 16 - 6 | 32 - 7 | 64 - 8 | 128 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The last three switches in switch block SW1 are used to select one -of eight possible I/O Base addresses using the following table:: - - - Switch | Hex I/O - 6 7 8 | Address - ------------|-------- - ON ON ON | 260 - OFF ON ON | 290 - ON OFF ON | 2E0 (Manufacturer's default) - OFF OFF ON | 2F0 - ON ON OFF | 300 - OFF ON OFF | 350 - ON OFF OFF | 380 - OFF OFF OFF | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer (RAM) requires 2K. The base of this buffer can be -located in any of eight positions. The address of the Boot Prom is -memory base + 0x2000. - -Jumpers 3-5 of switch block SW1 select the Memory Base address. - -:: - - Switch | Hex RAM | Hex ROM - 1 2 3 4 5 | Address | Address *) - --------------------|---------|----------- - ON ON ON ON ON | C0000 | C2000 - ON ON OFF ON ON | C4000 | C6000 - ON ON ON OFF ON | CC000 | CE000 - ON ON OFF OFF ON | D0000 | D2000 (Manufacturer's default) - ON ON ON ON OFF | D4000 | D6000 - ON ON OFF ON OFF | D8000 | DA000 - ON ON ON OFF OFF | DC000 | DE000 - ON ON OFF OFF OFF | E0000 | E2000 - - *) To enable the Boot ROM set the switch 8 of switch block SW3 to position ON. - -The switches 1 and 2 probably add 0x0800 and 0x1000 to RAM base address. - - -Setting the Interrupt Line -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Switches 1-5 of the switch block SW3 control the IRQ level:: - - Jumper | IRQ - 1 2 3 4 5 | - ---------------------------- - ON OFF OFF OFF OFF | 3 - OFF ON OFF OFF OFF | 4 - OFF OFF ON OFF OFF | 5 - OFF OFF OFF ON OFF | 7 - OFF OFF OFF OFF ON | 2 - - -Setting the Timeout Parameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The switches 6-7 of the switch block SW3 are used to determine the timeout -parameters. These two switches are normally left in the OFF position. - - -Topware -======= - -8-bit card, TA-ARC/10 ---------------------- - - - from Vojtech Pavlik - -This is another very similar 90C65 card. Most of the switches and jumpers -are the same as on other clones. - -:: - - _____________________________________________________________________ - | ___________ | | ______ | - | |SW2 NODE ID| | | | XTAL | | - | |___________| | Hybrid IC | |______| | - | ___________ | | __| - | |SW1 MEM+I/O| |_________________________| LED1|__|) - | |___________| 1 2 | - | J3 |o|o| TIMEOUT ______| - | ______________ |o|o| | | - | | | ___________________ | RJ | - | > EPROM SOCKET | | \ |------| - |J2 |______________| | | | | - ||o| | | |______| - ||o| ROM ENABLE | SMC | _________ | - | _____________ | 90C65 | |_________| _____| - | | | | | | |___ - | > RAM (2k) | | | | BNC |___| - | |_____________| | | |_____| - | |____________________| | - | ________ IRQ 2 3 4 5 7 ___________ | - ||________| |o|o|o|o|o| |___________| | - |________ J1|o|o|o|o|o| ______________| - | | - |_____________________________________________| - -Legend:: - - 90C65 ARCNET Chip - XTAL 20 MHz Crystal - SW1 1-5 Base Memory Address Select - 6-8 Base I/O Address Select - SW2 1-8 Node ID Select (ID0-ID7) - J1 IRQ Select - J2 ROM Enable - J3 Extra Timeout - LED1 Activity LED - BNC Coax connector (BUS ARCnet) - RJ Twisted Pair Connector (daisy chain) - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in SW2 are used to set the node ID. Each node attached to -the network must have an unique node ID which must not be 0. Switch 1 (ID0) -serves as the least significant bit (LSB). - -Setting one of the switches to Off means "1", On means "0". - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Label | Value - -------|-------|------- - 1 | ID0 | 1 - 2 | ID1 | 2 - 3 | ID2 | 4 - 4 | ID3 | 8 - 5 | ID4 | 16 - 6 | ID5 | 32 - 7 | ID6 | 64 - 8 | ID7 | 128 - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The last three switches in switch block SW1 are used to select one -of eight possible I/O Base addresses using the following table:: - - - Switch | Hex I/O - 6 7 8 | Address - ------------|-------- - ON ON ON | 260 (Manufacturer's default) - OFF ON ON | 290 - ON OFF ON | 2E0 - OFF OFF ON | 2F0 - ON ON OFF | 300 - OFF ON OFF | 350 - ON OFF OFF | 380 - OFF OFF OFF | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer (RAM) requires 2K. The base of this buffer can be -located in any of eight positions. The address of the Boot Prom is -memory base + 0x2000. - -Jumpers 3-5 of switch block SW1 select the Memory Base address. - -:: - - Switch | Hex RAM | Hex ROM - 1 2 3 4 5 | Address | Address *) - --------------------|---------|----------- - ON ON ON ON ON | C0000 | C2000 - ON ON OFF ON ON | C4000 | C6000 (Manufacturer's default) - ON ON ON OFF ON | CC000 | CE000 - ON ON OFF OFF ON | D0000 | D2000 - ON ON ON ON OFF | D4000 | D6000 - ON ON OFF ON OFF | D8000 | DA000 - ON ON ON OFF OFF | DC000 | DE000 - ON ON OFF OFF OFF | E0000 | E2000 - - *) To enable the Boot ROM short the jumper J2. - -The jumpers 1 and 2 probably add 0x0800 and 0x1000 to RAM address. - - -Setting the Interrupt Line -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Jumpers 1-5 of the jumper block J1 control the IRQ level. ON means -shorted, OFF means open:: - - Jumper | IRQ - 1 2 3 4 5 | - ---------------------------- - ON OFF OFF OFF OFF | 2 - OFF ON OFF OFF OFF | 3 - OFF OFF ON OFF OFF | 4 - OFF OFF OFF ON OFF | 5 - OFF OFF OFF OFF ON | 7 - - -Setting the Timeout Parameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The jumpers J3 are used to set the timeout parameters. These two -jumpers are normally left open. - -Thomas-Conrad -============= - -Model #500-6242-0097 REV A (8-bit card) ---------------------------------------- - - - from Lars Karlsson <100617.3473@compuserve.com> - -:: - - ________________________________________________________ - | ________ ________ |_____ - | |........| |........| | - | |________| |________| ___| - | SW 3 SW 1 | | - | Base I/O Base Addr. Station | | - | address | | - | ______ switch | | - | | | | | - | | | |___| - | | | ______ |___._ - | |______| |______| ____| BNC - | Jumper- _____| Connector - | Main chip block _ __| ' - | | | | RJ Connector - | |_| | with 110 Ohm - | |__ Terminator - | ___________ __| - | |...........| | RJ-jack - | |...........| _____ | (unused) - | |___________| |_____| |__ - | Boot PROM socket IRQ-jumpers |_ Diagnostic - |________ __ _| LED (red) - | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | |________| - | - | - -And here are the settings for some of the switches and jumpers on the cards. - -:: - - I/O - - 1 2 3 4 5 6 7 8 - - 2E0----- 0 0 0 1 0 0 0 1 - 2F0----- 0 0 0 1 0 0 0 0 - 300----- 0 0 0 0 1 1 1 1 - 350----- 0 0 0 0 1 1 1 0 - -"0" in the above example means switch is off "1" means that it is on. - -:: - - ShMem address. - - 1 2 3 4 5 6 7 8 - - CX00--0 0 1 1 | | | - DX00--0 0 1 0 | - X000--------- 1 1 | - X400--------- 1 0 | - X800--------- 0 1 | - XC00--------- 0 0 - ENHANCED----------- 1 - COMPATIBLE--------- 0 - -:: - - IRQ - - - 3 4 5 7 2 - . . . . . - . . . . . - - -There is a DIP-switch with 8 switches, used to set the shared memory address -to be used. The first 6 switches set the address, the 7th doesn't have any -function, and the 8th switch is used to select "compatible" or "enhanced". -When I got my two cards, one of them had this switch set to "enhanced". That -card didn't work at all, it wasn't even recognized by the driver. The other -card had this switch set to "compatible" and it behaved absolutely normally. I -guess that the switch on one of the cards, must have been changed accidentally -when the card was taken out of its former host. The question remains -unanswered, what is the purpose of the "enhanced" position? - -[Avery's note: "enhanced" probably either disables shared memory (use IO -ports instead) or disables IO ports (use memory addresses instead). This -varies by the type of card involved. I fail to see how either of these -enhance anything. Send me more detailed information about this mode, or -just use "compatible" mode instead.] - -Waterloo Microsystems Inc. ?? -============================= - -8-bit card (C) 1985 -------------------- - - from Robert Michael Best - -[Avery's note: these don't work with my driver for some reason. These cards -SEEM to have settings similar to the PDI508Plus, which is -software-configured and doesn't work with my driver either. The "Waterloo -chip" is a boot PROM, probably designed specifically for the University of -Waterloo. If you have any further information about this card, please -e-mail me.] - -The probe has not been able to detect the card on any of the J2 settings, -and I tried them again with the "Waterloo" chip removed. - -:: - - _____________________________________________________________________ - | \/ \/ ___ __ __ | - | C4 C4 |^| | M || ^ ||^| | - | -- -- |_| | 5 || || | C3 | - | \/ \/ C10 |___|| ||_| | - | C4 C4 _ _ | | ?? | - | -- -- | \/ || | | - | | || | | - | | || C1 | | - | | || | \/ _____| - | | C6 || | C9 | |___ - | | || | -- | BNC |___| - | | || | >C7| |_____| - | | || | | - | __ __ |____||_____| 1 2 3 6 | - || ^ | >C4| |o|o|o|o|o|o| J2 >C4| | - || | |o|o|o|o|o|o| | - || C2 | >C4| >C4| | - || | >C8| | - || | 2 3 4 5 6 7 IRQ >C4| | - ||_____| |o|o|o|o|o|o| J3 | - |_______ |o|o|o|o|o|o| _______________| - | | - |_____________________________________________| - - C1 -- "COM9026 - SMC 8638" - In a chip socket. - - C2 -- "@Copyright - Waterloo Microsystems Inc. - 1985" - In a chip Socket with info printed on a label covering a round window - showing the circuit inside. (The window indicates it is an EPROM chip.) - - C3 -- "COM9032 - SMC 8643" - In a chip socket. - - C4 -- "74LS" - 9 total no sockets. - - M5 -- "50006-136 - 20.000000 MHZ - MTQ-T1-S3 - 0 M-TRON 86-40" - Metallic case with 4 pins, no socket. - - C6 -- "MOSTEK@TC8643 - MK6116N-20 - MALAYSIA" - No socket. - - C7 -- No stamp or label but in a 20 pin chip socket. - - C8 -- "PAL10L8CN - 8623" - In a 20 pin socket. - - C9 -- "PAl16R4A-2CN - 8641" - In a 20 pin socket. - - C10 -- "M8640 - NMC - 9306N" - In an 8 pin socket. - - ?? -- Some components on a smaller board and attached with 20 pins all - along the side closest to the BNC connector. The are coated in a dark - resin. - -On the board there are two jumper banks labeled J2 and J3. The -manufacturer didn't put a J1 on the board. The two boards I have both -came with a jumper box for each bank. - -:: - - J2 -- Numbered 1 2 3 4 5 6. - 4 and 5 are not stamped due to solder points. - - J3 -- IRQ 2 3 4 5 6 7 - -The board itself has a maple leaf stamped just above the irq jumpers -and "-2 46-86" beside C2. Between C1 and C6 "ASS 'Y 300163" and "@1986 -CORMAN CUSTOM ELECTRONICS CORP." stamped just below the BNC connector. -Below that "MADE IN CANADA" - -No Name -======= - -8-bit cards, 16-bit cards -------------------------- - - - from Juergen Seifert - -I have named this ARCnet card "NONAME", since there is no name of any -manufacturer on the Installation manual nor on the shipping box. The only -hint to the existence of a manufacturer at all is written in copper, -it is "Made in Taiwan" - -This description has been written by Juergen Seifert -using information from the Original - - "ARCnet Installation Manual" - -:: - - ________________________________________________________________ - | |STAR| BUS| T/P| | - | |____|____|____| | - | _____________________ | - | | | | - | | | | - | | | | - | | SMC | | - | | | | - | | COM90C65 | | - | | | | - | | | | - | |__________-__________| | - | _____| - | _______________ | CN | - | | PROM | |_____| - | > SOCKET | | - | |_______________| 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 | - | _______________ _______________ | - | |o|o|o|o|o|o|o|o| | SW1 || SW2 || - | |o|o|o|o|o|o|o|o| |_______________||_______________|| - |___ 2 3 4 5 7 E E R Node ID IOB__|__MEM____| - | \ IRQ / T T O | - |__________________1_2_M______________________| - -Legend:: - - COM90C65: ARCnet Probe - S1 1-8: Node ID Select - S2 1-3: I/O Base Address Select - 4-6: Memory Base Address Select - 7-8: RAM Offset Select - ET1, ET2 Extended Timeout Select - ROM ROM Enable Select - CN RG62 Coax Connector - STAR| BUS | T/P Three fields for placing a sign (colored circle) - indicating the topology of the card - -Setting one of the switches to Off means "1", On means "0". - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in group SW1 are used to set the node ID. -Each node attached to the network must have an unique node ID which -must be different from 0. -Switch 8 serves as the least significant bit (LSB). - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Value - -------|------- - 8 | 1 - 7 | 2 - 6 | 4 - 5 | 8 - 4 | 16 - 3 | 32 - 2 | 64 - 1 | 128 - -Some Examples:: - - Switch | Hex | Decimal - 1 2 3 4 5 6 7 8 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed - 0 0 0 0 0 0 0 1 | 1 | 1 - 0 0 0 0 0 0 1 0 | 2 | 2 - 0 0 0 0 0 0 1 1 | 3 | 3 - . . . | | - 0 1 0 1 0 1 0 1 | 55 | 85 - . . . | | - 1 0 1 0 1 0 1 0 | AA | 170 - . . . | | - 1 1 1 1 1 1 0 1 | FD | 253 - 1 1 1 1 1 1 1 0 | FE | 254 - 1 1 1 1 1 1 1 1 | FF | 255 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first three switches in switch group SW2 are used to select one -of eight possible I/O Base addresses using the following table:: - - Switch | Hex I/O - 1 2 3 | Address - ------------|-------- - ON ON ON | 260 - ON ON OFF | 290 - ON OFF ON | 2E0 (Manufacturer's default) - ON OFF OFF | 2F0 - OFF ON ON | 300 - OFF ON OFF | 350 - OFF OFF ON | 380 - OFF OFF OFF | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer requires 2K of a 16K block of RAM. The base of this -16K block can be located in any of eight positions. -Switches 4-6 of switch group SW2 select the Base of the 16K block. -Within that 16K address space, the buffer may be assigned any one of four -positions, determined by the offset, switches 7 and 8 of group SW2. - -:: - - Switch | Hex RAM | Hex ROM - 4 5 6 7 8 | Address | Address *) - -----------|---------|----------- - 0 0 0 0 0 | C0000 | C2000 - 0 0 0 0 1 | C0800 | C2000 - 0 0 0 1 0 | C1000 | C2000 - 0 0 0 1 1 | C1800 | C2000 - | | - 0 0 1 0 0 | C4000 | C6000 - 0 0 1 0 1 | C4800 | C6000 - 0 0 1 1 0 | C5000 | C6000 - 0 0 1 1 1 | C5800 | C6000 - | | - 0 1 0 0 0 | CC000 | CE000 - 0 1 0 0 1 | CC800 | CE000 - 0 1 0 1 0 | CD000 | CE000 - 0 1 0 1 1 | CD800 | CE000 - | | - 0 1 1 0 0 | D0000 | D2000 (Manufacturer's default) - 0 1 1 0 1 | D0800 | D2000 - 0 1 1 1 0 | D1000 | D2000 - 0 1 1 1 1 | D1800 | D2000 - | | - 1 0 0 0 0 | D4000 | D6000 - 1 0 0 0 1 | D4800 | D6000 - 1 0 0 1 0 | D5000 | D6000 - 1 0 0 1 1 | D5800 | D6000 - | | - 1 0 1 0 0 | D8000 | DA000 - 1 0 1 0 1 | D8800 | DA000 - 1 0 1 1 0 | D9000 | DA000 - 1 0 1 1 1 | D9800 | DA000 - | | - 1 1 0 0 0 | DC000 | DE000 - 1 1 0 0 1 | DC800 | DE000 - 1 1 0 1 0 | DD000 | DE000 - 1 1 0 1 1 | DD800 | DE000 - | | - 1 1 1 0 0 | E0000 | E2000 - 1 1 1 0 1 | E0800 | E2000 - 1 1 1 1 0 | E1000 | E2000 - 1 1 1 1 1 | E1800 | E2000 - - *) To enable the 8K Boot PROM install the jumper ROM. - The default is jumper ROM not installed. - - -Setting Interrupt Request Lines (IRQ) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To select a hardware interrupt level set one (only one!) of the jumpers -IRQ2, IRQ3, IRQ4, IRQ5 or IRQ7. The manufacturer's default is IRQ2. - - -Setting the Timeouts -^^^^^^^^^^^^^^^^^^^^ - -The two jumpers labeled ET1 and ET2 are used to determine the timeout -parameters (response and reconfiguration time). Every node in a network -must be set to the same timeout values. - -:: - - ET1 ET2 | Response Time (us) | Reconfiguration Time (ms) - --------|--------------------|-------------------------- - Off Off | 78 | 840 (Default) - Off On | 285 | 1680 - On Off | 563 | 1680 - On On | 1130 | 1680 - -On means jumper installed, Off means jumper not installed - - -16-BIT ARCNET -------------- - -The manual of my 8-Bit NONAME ARCnet Card contains another description -of a 16-Bit Coax / Twisted Pair Card. This description is incomplete, -because there are missing two pages in the manual booklet. (The table -of contents reports pages ... 2-9, 2-11, 2-12, 3-1, ... but inside -the booklet there is a different way of counting ... 2-9, 2-10, A-1, -(empty page), 3-1, ..., 3-18, A-1 (again), A-2) -Also the picture of the board layout is not as good as the picture of -8-Bit card, because there isn't any letter like "SW1" written to the -picture. - -Should somebody have such a board, please feel free to complete this -description or to send a mail to me! - -This description has been written by Juergen Seifert -using information from the Original - - "ARCnet Installation Manual" - -:: - - ___________________________________________________________________ - < _________________ _________________ | - > | SW? || SW? | | - < |_________________||_________________| | - > ____________________ | - < | | | - > | | | - < | | | - > | | | - < | | | - > | | | - < | | | - > |____________________| | - < ____| - > ____________________ | | - < | | | J1 | - > | < | | - < |____________________| ? ? ? ? ? ? |____| - > |o|o|o|o|o|o| | - < |o|o|o|o|o|o| | - > | - < __ ___________| - > | | | - <____________| |_______________________________________| - - -Setting one of the switches to Off means "1", On means "0". - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in group SW2 are used to set the node ID. -Each node attached to the network must have an unique node ID which -must be different from 0. -Switch 8 serves as the least significant bit (LSB). - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Value - -------|------- - 8 | 1 - 7 | 2 - 6 | 4 - 5 | 8 - 4 | 16 - 3 | 32 - 2 | 64 - 1 | 128 - -Some Examples:: - - Switch | Hex | Decimal - 1 2 3 4 5 6 7 8 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed - 0 0 0 0 0 0 0 1 | 1 | 1 - 0 0 0 0 0 0 1 0 | 2 | 2 - 0 0 0 0 0 0 1 1 | 3 | 3 - . . . | | - 0 1 0 1 0 1 0 1 | 55 | 85 - . . . | | - 1 0 1 0 1 0 1 0 | AA | 170 - . . . | | - 1 1 1 1 1 1 0 1 | FD | 253 - 1 1 1 1 1 1 1 0 | FE | 254 - 1 1 1 1 1 1 1 1 | FF | 255 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first three switches in switch group SW1 are used to select one -of eight possible I/O Base addresses using the following table:: - - Switch | Hex I/O - 3 2 1 | Address - ------------|-------- - ON ON ON | 260 - ON ON OFF | 290 - ON OFF ON | 2E0 (Manufacturer's default) - ON OFF OFF | 2F0 - OFF ON ON | 300 - OFF ON OFF | 350 - OFF OFF ON | 380 - OFF OFF OFF | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer requires 2K of a 16K block of RAM. The base of this -16K block can be located in any of eight positions. -Switches 6-8 of switch group SW1 select the Base of the 16K block. -Within that 16K address space, the buffer may be assigned any one of four -positions, determined by the offset, switches 4 and 5 of group SW1:: - - Switch | Hex RAM | Hex ROM - 8 7 6 5 4 | Address | Address - -----------|---------|----------- - 0 0 0 0 0 | C0000 | C2000 - 0 0 0 0 1 | C0800 | C2000 - 0 0 0 1 0 | C1000 | C2000 - 0 0 0 1 1 | C1800 | C2000 - | | - 0 0 1 0 0 | C4000 | C6000 - 0 0 1 0 1 | C4800 | C6000 - 0 0 1 1 0 | C5000 | C6000 - 0 0 1 1 1 | C5800 | C6000 - | | - 0 1 0 0 0 | CC000 | CE000 - 0 1 0 0 1 | CC800 | CE000 - 0 1 0 1 0 | CD000 | CE000 - 0 1 0 1 1 | CD800 | CE000 - | | - 0 1 1 0 0 | D0000 | D2000 (Manufacturer's default) - 0 1 1 0 1 | D0800 | D2000 - 0 1 1 1 0 | D1000 | D2000 - 0 1 1 1 1 | D1800 | D2000 - | | - 1 0 0 0 0 | D4000 | D6000 - 1 0 0 0 1 | D4800 | D6000 - 1 0 0 1 0 | D5000 | D6000 - 1 0 0 1 1 | D5800 | D6000 - | | - 1 0 1 0 0 | D8000 | DA000 - 1 0 1 0 1 | D8800 | DA000 - 1 0 1 1 0 | D9000 | DA000 - 1 0 1 1 1 | D9800 | DA000 - | | - 1 1 0 0 0 | DC000 | DE000 - 1 1 0 0 1 | DC800 | DE000 - 1 1 0 1 0 | DD000 | DE000 - 1 1 0 1 1 | DD800 | DE000 - | | - 1 1 1 0 0 | E0000 | E2000 - 1 1 1 0 1 | E0800 | E2000 - 1 1 1 1 0 | E1000 | E2000 - 1 1 1 1 1 | E1800 | E2000 - - -Setting Interrupt Request Lines (IRQ) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -?????????????????????????????????????? - - -Setting the Timeouts -^^^^^^^^^^^^^^^^^^^^ - -?????????????????????????????????????? - - -8-bit cards ("Made in Taiwan R.O.C.") -------------------------------------- - - - from Vojtech Pavlik - -I have named this ARCnet card "NONAME", since I got only the card with -no manual at all and the only text identifying the manufacturer is -"MADE IN TAIWAN R.O.C" printed on the card. - -:: - - ____________________________________________________________ - | 1 2 3 4 5 6 7 8 | - | |o|o| JP1 o|o|o|o|o|o|o|o| ON | - | + o|o|o|o|o|o|o|o| ___| - | _____________ o|o|o|o|o|o|o|o| OFF _____ | | ID7 - | | | SW1 | | | | ID6 - | > RAM (2k) | ____________________ | H | | S | ID5 - | |_____________| | || y | | W | ID4 - | | || b | | 2 | ID3 - | | || r | | | ID2 - | | || i | | | ID1 - | | 90C65 || d | |___| ID0 - | SW3 | || | | - | |o|o|o|o|o|o|o|o| ON | || I | | - | |o|o|o|o|o|o|o|o| | || C | | - | |o|o|o|o|o|o|o|o| OFF |____________________|| | _____| - | 1 2 3 4 5 6 7 8 | | | |___ - | ______________ | | | BNC |___| - | | | |_____| |_____| - | > EPROM SOCKET | | - | |______________| | - | ______________| - | | - |_____________________________________________| - -Legend:: - - 90C65 ARCNET Chip - SW1 1-5: Base Memory Address Select - 6-8: Base I/O Address Select - SW2 1-8: Node ID Select (ID0-ID7) - SW3 1-5: IRQ Select - 6-7: Extra Timeout - 8 : ROM Enable - JP1 Led connector - BNC Coax connector - -Although the jumpers SW1 and SW3 are marked SW, not JP, they are jumpers, not -switches. - -Setting the jumpers to ON means connecting the upper two pins, off the bottom -two - or - in case of IRQ setting, connecting none of them at all. - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in SW2 are used to set the node ID. Each node attached -to the network must have an unique node ID which must not be 0. -Switch 1 (ID0) serves as the least significant bit (LSB). - -Setting one of the switches to Off means "1", On means "0". - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Label | Value - -------|-------|------- - 1 | ID0 | 1 - 2 | ID1 | 2 - 3 | ID2 | 4 - 4 | ID3 | 8 - 5 | ID4 | 16 - 6 | ID5 | 32 - 7 | ID6 | 64 - 8 | ID7 | 128 - -Some Examples:: - - Switch | Hex | Decimal - 8 7 6 5 4 3 2 1 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed - 0 0 0 0 0 0 0 1 | 1 | 1 - 0 0 0 0 0 0 1 0 | 2 | 2 - 0 0 0 0 0 0 1 1 | 3 | 3 - . . . | | - 0 1 0 1 0 1 0 1 | 55 | 85 - . . . | | - 1 0 1 0 1 0 1 0 | AA | 170 - . . . | | - 1 1 1 1 1 1 0 1 | FD | 253 - 1 1 1 1 1 1 1 0 | FE | 254 - 1 1 1 1 1 1 1 1 | FF | 255 - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The last three switches in switch block SW1 are used to select one -of eight possible I/O Base addresses using the following table:: - - - Switch | Hex I/O - 6 7 8 | Address - ------------|-------- - ON ON ON | 260 - OFF ON ON | 290 - ON OFF ON | 2E0 (Manufacturer's default) - OFF OFF ON | 2F0 - ON ON OFF | 300 - OFF ON OFF | 350 - ON OFF OFF | 380 - OFF OFF OFF | 3E0 - - -Setting the Base Memory (RAM) buffer Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer (RAM) requires 2K. The base of this buffer can be -located in any of eight positions. The address of the Boot Prom is -memory base + 0x2000. - -Jumpers 3-5 of jumper block SW1 select the Memory Base address. - -:: - - Switch | Hex RAM | Hex ROM - 1 2 3 4 5 | Address | Address *) - --------------------|---------|----------- - ON ON ON ON ON | C0000 | C2000 - ON ON OFF ON ON | C4000 | C6000 - ON ON ON OFF ON | CC000 | CE000 - ON ON OFF OFF ON | D0000 | D2000 (Manufacturer's default) - ON ON ON ON OFF | D4000 | D6000 - ON ON OFF ON OFF | D8000 | DA000 - ON ON ON OFF OFF | DC000 | DE000 - ON ON OFF OFF OFF | E0000 | E2000 - - *) To enable the Boot ROM set the jumper 8 of jumper block SW3 to position ON. - -The jumpers 1 and 2 probably add 0x0800, 0x1000 and 0x1800 to RAM adders. - -Setting the Interrupt Line -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Jumpers 1-5 of the jumper block SW3 control the IRQ level:: - - Jumper | IRQ - 1 2 3 4 5 | - ---------------------------- - ON OFF OFF OFF OFF | 2 - OFF ON OFF OFF OFF | 3 - OFF OFF ON OFF OFF | 4 - OFF OFF OFF ON OFF | 5 - OFF OFF OFF OFF ON | 7 - - -Setting the Timeout Parameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The jumpers 6-7 of the jumper block SW3 are used to determine the timeout -parameters. These two jumpers are normally left in the OFF position. - - - -(Generic Model 9058) --------------------- - - from Andrew J. Kroll - - Sorry this sat in my to-do box for so long, Andrew! (yikes - over a - year!) - -:: - - _____ - | < - | .---' - ________________________________________________________________ | | - | | SW2 | | | - | ___________ |_____________| | | - | | | 1 2 3 4 5 6 ___| | - | > 6116 RAM | _________ 8 | | | - | |___________| |20MHzXtal| 7 | | | - | |_________| __________ 6 | S | | - | 74LS373 | |- 5 | W | | - | _________ | E |- 4 | | | - | >_______| ______________|..... P |- 3 | 3 | | - | | | : O |- 2 | | | - | | | : X |- 1 |___| | - | ________________ | | : Y |- | | - | | SW1 | | SL90C65 | : |- | | - | |________________| | | : B |- | | - | 1 2 3 4 5 6 7 8 | | : O |- | | - | |_________o____|..../ A |- _______| | - | ____________________ | R |- | |------, - | | | | D |- | BNC | # | - | > 2764 PROM SOCKET | |__________|- |_______|------' - | |____________________| _________ | | - | >________| <- 74LS245 | | - | | | - |___ ______________| | - |H H H H H H H H H H H H H H H H H H H H H H H| | | - |U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U| | | - \| - -Legend:: - - SL90C65 ARCNET Controller / Transceiver /Logic - SW1 1-5: IRQ Select - 6: ET1 - 7: ET2 - 8: ROM ENABLE - SW2 1-3: Memory Buffer/PROM Address - 3-6: I/O Address Map - SW3 1-8: Node ID Select - BNC BNC RG62/U Connection - *I* have had success using RG59B/U with *NO* terminators! - What gives?! - -SW1: Timeouts, Interrupt and ROM -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To select a hardware interrupt level set one (only one!) of the dip switches -up (on) SW1...(switches 1-5) -IRQ3, IRQ4, IRQ5, IRQ7, IRQ2. The Manufacturer's default is IRQ2. - -The switches on SW1 labeled EXT1 (switch 6) and EXT2 (switch 7) -are used to determine the timeout parameters. These two dip switches -are normally left off (down). - - To enable the 8K Boot PROM position SW1 switch 8 on (UP) labeled ROM. - The default is jumper ROM not installed. - - -Setting the I/O Base Address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The last three switches in switch group SW2 are used to select one -of eight possible I/O Base addresses using the following table:: - - - Switch | Hex I/O - 4 5 6 | Address - -------|-------- - 0 0 0 | 260 - 0 0 1 | 290 - 0 1 0 | 2E0 (Manufacturer's default) - 0 1 1 | 2F0 - 1 0 0 | 300 - 1 0 1 | 350 - 1 1 0 | 380 - 1 1 1 | 3E0 - - -Setting the Base Memory Address (RAM & ROM) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The memory buffer requires 2K of a 16K block of RAM. The base of this -16K block can be located in any of eight positions. -Switches 1-3 of switch group SW2 select the Base of the 16K block. -(0 = DOWN, 1 = UP) -I could, however, only verify two settings... - - -:: - - Switch| Hex RAM | Hex ROM - 1 2 3 | Address | Address - ------|---------|----------- - 0 0 0 | E0000 | E2000 - 0 0 1 | D0000 | D2000 (Manufacturer's default) - 0 1 0 | ????? | ????? - 0 1 1 | ????? | ????? - 1 0 0 | ????? | ????? - 1 0 1 | ????? | ????? - 1 1 0 | ????? | ????? - 1 1 1 | ????? | ????? - - -Setting the Node ID -^^^^^^^^^^^^^^^^^^^ - -The eight switches in group SW3 are used to set the node ID. -Each node attached to the network must have an unique node ID which -must be different from 0. -Switch 1 serves as the least significant bit (LSB). -switches in the DOWN position are OFF (0) and in the UP position are ON (1) - -The node ID is the sum of the values of all switches set to "1" -These values are:: - - Switch | Value - -------|------- - 1 | 1 - 2 | 2 - 3 | 4 - 4 | 8 - 5 | 16 - 6 | 32 - 7 | 64 - 8 | 128 - -Some Examples:: - - Switch# | Hex | Decimal - 8 7 6 5 4 3 2 1 | Node ID | Node ID - ----------------|---------|--------- - 0 0 0 0 0 0 0 0 | not allowed <-. - 0 0 0 0 0 0 0 1 | 1 | 1 | - 0 0 0 0 0 0 1 0 | 2 | 2 | - 0 0 0 0 0 0 1 1 | 3 | 3 | - . . . | | | - 0 1 0 1 0 1 0 1 | 55 | 85 | - . . . | | + Don't use 0 or 255! - 1 0 1 0 1 0 1 0 | AA | 170 | - . . . | | | - 1 1 1 1 1 1 0 1 | FD | 253 | - 1 1 1 1 1 1 1 0 | FE | 254 | - 1 1 1 1 1 1 1 1 | FF | 255 <-' - - -Tiara -===== - -(model unknown) ---------------- - - - from Christoph Lameter - - -Here is information about my card as far as I could figure it out:: - - - ----------------------------------------------- tiara - Tiara LanCard of Tiara Computer Systems. - - +----------------------------------------------+ - ! ! Transmitter Unit ! ! - ! +------------------+ ------- - ! MEM Coax Connector - ! ROM 7654321 <- I/O ------- - ! : : +--------+ ! - ! : : ! 90C66LJ! +++ - ! : : ! ! !D Switch to set - ! : : ! ! !I the Nodenumber - ! : : +--------+ !P - ! !++ - ! 234567 <- IRQ ! - +------------!!!!!!!!!!!!!!!!!!!!!!!!--------+ - !!!!!!!!!!!!!!!!!!!!!!!! - -- 0 = Jumper Installed -- 1 = Open - -Top Jumper line Bit 7 = ROM Enable 654=Memory location 321=I/O - -Settings for Memory Location (Top Jumper Line) - -=== ================ -456 Address selected -=== ================ -000 C0000 -001 C4000 -010 CC000 -011 D0000 -100 D4000 -101 D8000 -110 DC000 -111 E0000 -=== ================ - -Settings for I/O Address (Top Jumper Line) - -=== ==== -123 Port -=== ==== -000 260 -001 290 -010 2E0 -011 2F0 -100 300 -101 350 -110 380 -111 3E0 -=== ==== - -Settings for IRQ Selection (Lower Jumper Line) - -====== ===== -234567 -====== ===== -011111 IRQ 2 -101111 IRQ 3 -110111 IRQ 4 -111011 IRQ 5 -111110 IRQ 7 -====== ===== - -Other Cards -=========== - -I have no information on other models of ARCnet cards at the moment. - -Thanks. diff --git a/Documentation/networking/arcnet.rst b/Documentation/networking/arcnet.rst index cd43a18ad149..4e541aa44aec 100644 --- a/Documentation/networking/arcnet.rst +++ b/Documentation/networking/arcnet.rst @@ -8,30 +8,10 @@ ARCnet .. note:: - See also arcnet-hardware.txt in this directory for jumper-setting + See also arcnet-hardware.rst in this directory for jumper-setting and cabling information if you're like many of us and didn't happen to get a manual with your ARCnet card. -Since no one seems to listen to me otherwise, perhaps a poem will get your -attention:: - - This driver's getting fat and beefy, - But my cat is still named Fifi. - -Hmm, I think I'm allowed to call that a poem, even though it's only two -lines. Hey, I'm in Computer Science, not English. Give me a break. - -The point is: I REALLY REALLY REALLY REALLY REALLY want to hear from you if -you test this and get it working. Or if you don't. Or anything. - -ARCnet 0.32 ALPHA first made it into the Linux kernel 1.1.80 - this was -nice, but after that even FEWER people started writing to me because they -didn't even have to install the patch. - -Come on, be a sport! Send me a success report! - -(hey, that was even better than my original poem... this is getting bad!) - ---- These are the ARCnet drivers for Linux. @@ -62,183 +42,47 @@ netdev@vger.kernel.org and make sure to Cc: maintainer listed in Other Drivers and Info ---------------------- -You can try my ARCNET page on the World Wide Web at: +You can try JoAnne Schmitz's ARCNET page on the World Wide Web at: - http://www.qis.net/~jschmitz/arcnet/ - -Also, SMC (one of the companies that makes ARCnet cards) has a WWW site you -might be interested in, which includes several drivers for various cards -including ARCnet. Try: - - http://www.smc.com/ - -Performance Technologies makes various network software that supports -ARCnet: - - http://www.perftech.com/ or ftp to ftp.perftech.com. - -Novell makes a networking stack for DOS which includes ARCnet drivers. Try -FTPing to ftp.novell.com. - -You can get the Crynwr packet driver collection (including arcether.com, the -one you'll want to use with ARCnet cards) from -oak.oakland.edu:/simtel/msdos/pktdrvr. It won't work perfectly on a 386+ -without patches, though, and also doesn't like several cards. Fixed -versions are available on my WWW page, or via e-mail if you don't have WWW -access. + https://www.qis.net/~jschmitz/arcnet/ -Installing the Driver ---------------------- +Supported Hardware +------------------ -All you will need to do in order to install the driver is:: +Only PCI and PCI Express devices based on the COM20020 chipset are supported. +This is the newest chipset from SMC with support for promiscuous mode (packet +sniffing), extra diagnostic information, etc. These devices use the com20020_pci +driver. - make config - (be sure to choose ARCnet in the network devices - and at least one chipset driver.) - make clean - make zImage +Support for older chipsets and ISA and PCMCIA devices was previously available +but has been removed. -If you obtained this ARCnet package as an upgrade to the ARCnet driver in -your current kernel, you will need to first copy arcnet.c over the one in -the linux/drivers/net directory. -You will know the driver is installed properly if you get some ARCnet -messages when you reboot into the new Linux kernel. +Configuring the Driver +---------------------- -There are four chipset options: +The COM20020 driver will be loaded automatically at boot if a supported card is +detected. - 1. Standard ARCnet COM90xx chipset. +If the com20020_pci driver was compiled as a loadable module, the options are:: -This is the normal ARCnet card, which you've probably got. This is the only -chipset driver which will autoprobe if not told where the card is. -It following options on the command line:: + node= backplane= clockp= clockm= + timeout= device= - com90xx=[[,[,]]][,] | +If the driver was compiled into the kernel, the same options can be specified on +the kernel command line by prefixing them with `com20020_pci.`, as in the +following example:: -If you load the chipset support as a module, the options are:: - - io= irq= shmem= device= - -To disable the autoprobe, just specify "com90xx=" on the kernel command line. -To specify the name alone, but allow autoprobe, just put "com90xx=" - - 2. ARCnet COM20020 chipset. - -This is the new chipset from SMC with support for promiscuous mode (packet -sniffing), extra diagnostic information, etc. Unfortunately, there is no -sensible method of autoprobing for these cards. You must specify the I/O -address on the kernel command line. - -The command line options are:: - - com20020=[,[,[,backplane[,CKP[,timeout]]]]][,name] - -If you load the chipset support as a module, the options are:: - - io= irq= node= backplane= clock= - timeout= device= + com20020_pci.device=eth1 The COM20020 chipset allows you to set the node ID in software, overriding the default which is still set in DIP switches on the card. If you don't have the -COM20020 data sheets, and you don't know what the other three options refer +COM20020 data sheets, and you don't know what the other options refer to, then they won't interest you - forget them. - 3. ARCnet COM90xx chipset in IO-mapped mode. - -This will also work with the normal ARCnet cards, but doesn't use the shared -memory. It performs less well than the above driver, but is provided in case -you have a card which doesn't support shared memory, or (strangely) in case -you have so many ARCnet cards in your machine that you run out of shmem slots. -If you don't give the IO address on the kernel command line, then the driver -will not find the card. - -The command line options are:: - - com90io=[,][,] - -If you load the chipset support as a module, the options are: - io= irq= device= - - 4. ARCnet RIM I cards. - -These are COM90xx chips which are _completely_ memory mapped. The support for -these is not tested. If you have one, please mail the author with a success -report. All options must be specified, except the device name. -Command line options:: - - arcrimi=,,[,] - -If you load the chipset support as a module, the options are:: - - shmem= irq= node= device= - - -Loadable Module Support ------------------------ - -Configure and rebuild Linux. When asked, answer 'm' to "Generic ARCnet -support" and to support for your ARCnet chipset if you want to use the -loadable module. You can also say 'y' to "Generic ARCnet support" and 'm' -to the chipset support if you wish. - -:: - - make config - make clean - make zImage - make modules - -If you're using a loadable module, you need to use insmod to load it, and -you can specify various characteristics of your card on the command -line. (In recent versions of the driver, autoprobing is much more reliable -and works as a module, so most of this is now unnecessary.) - -For example:: - - cd /usr/src/linux/modules - insmod arcnet.o - insmod com90xx.o - insmod com20020.o io=0x2e0 device=eth1 - - -Using the Driver ----------------- - -If you build your kernel with ARCnet COM90xx support included, it should -probe for your card automatically when you boot. If you use a different -chipset driver complied into the kernel, you must give the necessary options -on the kernel command line, as detailed above. - -Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be -available where you picked up this driver. Think of your ARCnet as a -souped-up (or down, as the case may be) Ethernet card. - -By the way, be sure to change all references from "eth0" to "arc0" in the -HOWTOs. Remember that ARCnet isn't a "true" Ethernet, and the device name -is DIFFERENT. - - -Multiple Cards in One Computer ------------------------------- - -Linux has pretty good support for this now, but since I've been busy, the -ARCnet driver has somewhat suffered in this respect. COM90xx support, if -compiled into the kernel, will (try to) autodetect all the installed cards. - -If you have other cards, with support compiled into the kernel, then you can -just repeat the options on the kernel command line, e.g.:: - - LILO: linux com20020=0x2e0 com20020=0x380 com90io=0x260 - -If you have the chipset support built as a loadable module, then you need to -do something like this:: - - insmod -o arc0 com90xx - insmod -o arc1 com20020 io=0x2e0 - insmod -o arc2 com90xx - -The ARCnet drivers will now sort out their names automatically. +Otherwise, ARCnet can be configured in a similar way to Ethernet, with the +exception that ARCnet interface names begin with `arc`. How do I get it to work with...? @@ -276,9 +120,8 @@ LAN Manager and Windows for Workgroups: are incompatible with the Internet standard. They try to pretend the cards are Ethernet, and confuse everyone else on the network. - However, v2.00 and higher of the Linux ARCnet driver supports this - protocol via the 'arc0e' device. See the section on "Multiprotocol - Support" for more information. + The Linux ARCnet driver supports this protocol via the 'arc0e' device. + See the section on "Multiprotocol Support" for more information. Using the freeware Samba server and clients for Linux, you can now interface quite nicely with TCP/IP-based WfWg or Lan Manager @@ -313,7 +156,7 @@ NetBSD/AmiTCP: Using Multiprotocol ARCnet -------------------------- -The ARCnet driver v2.10 ALPHA supports three protocols, each on its own +The ARCnet driver supports three protocols, each on its own "virtual network device": ====== =============================================================== @@ -505,7 +348,7 @@ can set up your network then: It works: what now? ------------------- -Send mail following :ref:`arcnet-netdev`. Describe your setup, preferably +:ref:`Send an email to netdev `. Describe your setup, preferably including driver version, kernel version, ARCnet card model, CPU type, number of systems on your network, and list of software in use. @@ -524,10 +367,6 @@ first! D_DURING displays 4-5 lines for each packet sent or received. D_TX, D_RX, and D_SKB actually DISPLAY each packet as it is sent or received, which is obviously quite big. -Starting with v2.40 ALPHA, the autoprobe routines have changed -significantly. In particular, they won't tell you why the card was not -found unless you turn on the D_INIT_REASONS debugging flag. - Once the driver is running, you can run the arcdump shell script (available from me or in the full ARCnet package, if you have it) as root to list the contents of the arcnet buffers at any time. To make any sense at all out of @@ -548,21 +387,13 @@ out which bytes are being used by a packet. You can change the debug level without recompiling the kernel by typing:: ifconfig arc0 down metric 1xxx - /etc/rc.d/rc.inet1 + ifconfig arc0 up where "xxx" is the debug level you want. For example, "metric 1015" would put you at debug level 15. Debug level 7 is currently the default. -Note that the debug level is (starting with v1.90 ALPHA) a binary -combination of different debug flags; so debug level 7 is really 1+2+4 or -D_NORMAL+D_EXTRA+D_INIT. To include D_DURING, you would add 16 to this, -resulting in debug level 23. +Note that the debug level is a binary combination of different debug flags; +debug level 7 is really 1+2+4 or D_NORMAL+D_EXTRA+D_INIT. To include D_DURING, +you would add 16 to this, resulting in debug level 23. If you don't understand that, you probably don't want to know anyway. -E-mail me about your problem. - - -I want to send money: what now? -------------------------------- - -Go take a nap or something. You'll feel better in the morning. diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index e700bf1d095c..33ca5afafdf6 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -619,6 +619,29 @@ min_links aggregator cannot be active without at least one available link, setting this option to 0 or to 1 has the exact same effect. +lacp_strict + + Specifies the fallback behavior of a bonding when LACP negotiation + fails on all slave links, i.e. when no slave is in the + Collecting_Distributing state, while at least `min_links` link still + reports carrier up. + + This option is only applicable to 802.3ad mode (mode 4). + + Valid values are: + + off or 0 + One interface of the bond is selected to be active, in order to + facilitate communication with peer devices that do not implement + LACP. + + on or 1 + Interfaces are only permitted to be made active if they have an + active LACP partner and have successfully reached + Collecting_Distributing state. + + The default value is 0 (off). + mode Specifies one of the bonding policies. The default is diff --git a/Documentation/networking/checksum-offloads.rst b/Documentation/networking/checksum-offloads.rst index 69b23cf6879e..d4ded890011b 100644 --- a/Documentation/networking/checksum-offloads.rst +++ b/Documentation/networking/checksum-offloads.rst @@ -19,17 +19,14 @@ The following technologies are described: Things that should be documented here but aren't yet: -* RX Checksum Offload * CHECKSUM_UNNECESSARY conversion TX Checksum Offload =================== -The interface for offloading a transmit checksum to a device is explained in -detail in comments near the top of include/linux/skbuff.h. - -In brief, it allows to request the device fill in a single ones-complement +In brief, Tx checksum offload allows to request the device fill in a single +ones-complement checksum defined by the sk_buff fields skb->csum_start and skb->csum_offset. The device should compute the 16-bit ones-complement checksum (i.e. the 'IP-style' checksum) from csum_start to the end of the packet, and fill in the @@ -45,9 +42,10 @@ encapsulation is used, the packet may have multiple checksum fields in different header layers, and the rest will have to be handled by another mechanism such as LCO or RCO. -CRC32c can also be offloaded using this interface, by means of filling -skb->csum_start and skb->csum_offset as described above, and setting -skb->csum_not_inet: see skbuff.h comment (section 'D') for more details. +SCTP CRC32c can also be offloaded using this interface, by means of filling +skb->csum_start and skb->csum_offset as described above, setting +skb->csum_not_inet, and advertising NETIF_F_SCTP_CRC. Drivers must not treat +ordinary IP checksum offload as SCTP CRC32c support. No offloading of the IP header checksum is performed; it is always done in software. This is OK because when we build the IP header, we obviously have it @@ -55,18 +53,15 @@ in cache, so summing it isn't expensive. It's also rather short. The requirements for GSO are more complicated, because when segmenting an encapsulated packet both the inner and outer checksums may need to be edited or -recomputed for each resulting segment. See the skbuff.h comment (section 'E') -for more details. +recomputed for each resulting segment. A driver declares its offload capabilities in netdev->hw_features; see -Documentation/networking/netdev-features.rst for more. Note that a device -which only advertises NETIF_F_IP[V6]_CSUM must still obey the csum_start and -csum_offset given in the SKB; if it tries to deduce these itself in hardware -(as some NICs do) the driver should check that the values in the SKB match -those which the hardware will deduce, and if not, fall back to checksumming in -software instead (with skb_csum_hwoffload_help() or one of the -skb_checksum_help() / skb_crc32c_csum_help functions, as mentioned in -include/linux/skbuff.h). +Documentation/networking/netdev-features.rst for more. NETIF_F_IP_CSUM and +NETIF_F_IPV6_CSUM are restricted legacy features and are being deprecated in +favor of NETIF_F_HW_CSUM. New devices should use NETIF_F_HW_CSUM to advertise +generic checksum offload. The skb_csum_hwoffload_help() helper can resolve +CHECKSUM_PARTIAL according to the device's advertised checksum capabilities, +falling back to software when needed. The stack should, for the most part, assume that checksum offload is supported by the underlying device. The only place that should check is @@ -108,11 +103,9 @@ LCO is performed by the stack when constructing an outer UDP header for an encapsulation such as VXLAN or GENEVE, in udp_set_csum(). Similarly for the IPv6 equivalents, in udp6_set_csum(). -It is also performed when constructing an IPv4 GRE header, in -net/ipv4/ip_gre.c:build_header(). It is *not* currently performed when -constructing an IPv6 GRE header; the GRE checksum is computed over the whole -packet in net/ipv6/ip6_gre.c:ip6gre_xmit2(), but it should be possible to use -LCO here as IPv6 GRE still uses an IP-style checksum. +It is also performed when constructing GRE headers with the shared +gre_build_header() helper in include/net/gre.h, which is used by both IPv4 and +IPv6 GRE. All of the LCO implementations use a helper function lco_csum(), in include/linux/skbuff.h. @@ -138,6 +131,28 @@ RCO is detailed in the following Internet-Drafts: * https://tools.ietf.org/html/draft-herbert-vxlan-rco-00 In Linux, RCO is implemented individually in each encapsulation protocol, and -most tunnel types have flags controlling its use. For instance, VXLAN has the -flag VXLAN_F_REMCSUM_TX (per struct vxlan_rdst) to indicate that RCO should be -used when transmitting to a given remote destination. +most tunnel types have flags controlling its use. For instance, VXLAN has the +configuration flag VXLAN_F_REMCSUM_TX to indicate that RCO should be used when +transmitting. + + +RX Checksum Offload +=================== + +RX checksum offload is controlled via NETIF_F_RXCSUM. When disabled the driver +must not set skb->ip_summed on ingress packets. As mentioned, IPv4 checksum +is not offloaded, the RXCSUM feature controls the offload of verification of +transport layer checksums. + +Note that packets with bad TCP/UDP checksums must still be passed +to the stack. skb->ip_summed of such packets can be set to ``CHECKSUM_COMPLETE`` +or left at ``CHECKSUM_NONE``. Drivers **must not discard** packets with +bad TCP/UDP checksum and must not configure the device to drop them. +Checksum validation is relatively inexpensive and having bad packets reflected +in SNMP counters is crucial for network monitoring. + +skb checksum documentation +========================== + +.. kernel-doc:: include/linux/skbuff.h + :doc: skb checksums diff --git a/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst b/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst deleted file mode 100644 index e5c283940ac5..000000000000 --- a/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst +++ /dev/null @@ -1,647 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -================================================ -Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters -================================================ - -.. note:: - - This document was contributed by Cirrus Logic for kernel 2.2.5. This version - has been updated for 2.3.48 by Andrew Morton. - - Still, this is too outdated! A major cleanup is needed here. - -Cirrus make a copy of this driver available at their website, as -described below. In general, you should use the driver version which -comes with your Linux distribution. - - -Linux Network Interface Driver ver. 2.00 - - -.. TABLE OF CONTENTS - - 1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS - 1.1 Product Overview - 1.2 Driver Description - 1.2.1 Driver Name - 1.2.2 File in the Driver Package - 1.3 System Requirements - 1.4 Licensing Information - - 2.0 ADAPTER INSTALLATION and CONFIGURATION - 2.1 CS8900-based Adapter Configuration - 2.2 CS8920-based Adapter Configuration - - 3.0 LOADING THE DRIVER AS A MODULE - - 4.0 COMPILING THE DRIVER - 4.1 Compiling the Driver as a Loadable Module - 4.2 Compiling the driver to support memory mode - 4.3 Compiling the driver to support Rx DMA - - 5.0 TESTING AND TROUBLESHOOTING - 5.1 Known Defects and Limitations - 5.2 Testing the Adapter - 5.2.1 Diagnostic Self-Test - 5.2.2 Diagnostic Network Test - 5.3 Using the Adapter's LEDs - 5.4 Resolving I/O Conflicts - - 6.0 TECHNICAL SUPPORT - 6.1 Contacting Cirrus Logic's Technical Support - 6.2 Information Required Before Contacting Technical Support - 6.3 Obtaining the Latest Driver Version - 6.4 Current maintainer - 6.5 Kernel boot parameters - - -1. Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters -=================================================== - - -1.1. Product Overview -===================== - -The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow -IEEE 802.3 standards and support half or full-duplex operation in ISA bus -computers on 10 Mbps Ethernet networks. The adapters are designed for operation -in 16-bit ISA or EISA bus expansion slots and are available in -10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5 -or fiber networks). - -CS8920-based adapters are similar to the CS8900-based adapter with additional -features for Plug and Play (PnP) support and Wakeup Frame recognition. As -such, the configuration procedures differ somewhat between the two types of -adapters. Refer to the "Adapter Configuration" section for details on -configuring both types of adapters. - - -1.2. Driver Description -======================= - -The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux -v2.3.48 or greater kernel. It can be compiled directly into the kernel -or loaded at run-time as a device driver module. - -1.2.1 Driver Name: cs89x0 - -1.2.2 Files in the Driver Archive: - -The files in the driver at Cirrus' website include: - - =================== ==================================================== - readme.txt this file - build batch file to compile cs89x0.c. - cs89x0.c driver C code - cs89x0.h driver header file - cs89x0.o pre-compiled module (for v2.2.5 kernel) - config/Config.in sample file to include cs89x0 driver in the kernel. - config/Makefile sample file to include cs89x0 driver in the kernel. - config/Space.c sample file to include cs89x0 driver in the kernel. - =================== ==================================================== - - - -1.3. System Requirements ------------------------- - -The following hardware is required: - - * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter - - * IBM or IBM-compatible PC with: - * An 80386 or higher processor - * 16 bytes of contiguous IO space available between 210h - 370h - * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920). - - * Appropriate cable (and connector for AUI, 10BASE-2) for your network - topology. - -The following software is required: - -* LINUX kernel version 2.3.48 or higher - - * CS8900/20 Setup Utility (DOS-based) - - * LINUX kernel sources for your kernel (if compiling into kernel) - - * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel - or a module) - - - -1.4. Licensing Information --------------------------- - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation, version 1. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -more details. - -For a full copy of the GNU General Public License, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - - -2. Adapter Installation and Configuration -========================================= - -Both the CS8900 and CS8920-based adapters can be configured using parameters -stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup -Utility if you want to change the adapter's configuration in EEPROM. - -When loading the driver as a module, you can specify many of the adapter's -configuration parameters on the command-line to override the EEPROM's settings -or for interface configuration when an EEPROM is not used. (CS8920-based -adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE. - -Since the CS8900/20 Setup Utility is a DOS-based application, you must install -and configure the adapter in a DOS-based system using the CS8900/20 Setup -Utility before installation in the target LINUX system. (Not required if -installing a CS8900-based adapter and the default configuration is acceptable.) - - -2.1. CS8900-based Adapter Configuration ---------------------------------------- - -CS8900-based adapters shipped from Cirrus Logic have been configured -with the following "default" settings:: - - Operation Mode: Memory Mode - IRQ: 10 - Base I/O Address: 300 - Memory Base Address: D0000 - Optimization: DOS Client - Transmission Mode: Half-duplex - BootProm: None - Media Type: Autodetect (3-media cards) or - 10BASE-T (10BASE-T only adapter) - -You should only change the default configuration settings if conflicts with -another adapter exists. To change the adapter's configuration, run the -CS8900/20 Setup Utility. - - -2.2. CS8920-based Adapter Configuration ---------------------------------------- - -CS8920-based adapters are shipped from Cirrus Logic configured as Plug -and Play (PnP) enabled. However, since the cs89x0 driver does NOT -support PnP, you must install the CS8920 adapter in a DOS-based PC and -run the CS8900/20 Setup Utility to disable PnP and configure the -adapter before installation in the target Linux system. Failure to do -this will leave the adapter inactive and the driver will be unable to -communicate with the adapter. - -:: - - **************************************************************** - * CS8920-BASED ADAPTERS: * - * * - * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT. * - * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST * - * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND * - * TO ACTIVATE THE ADAPTER. * - **************************************************************** - - - - -3. Loading the Driver as a Module -================================= - -If the driver is compiled as a loadable module, you can load the driver module -with the 'modprobe' command. Many of the adapter's configuration parameters can -be specified as command-line arguments to the load command. This facility -provides a means to override the EEPROM's settings or for interface -configuration when an EEPROM is not used. - -Example:: - - insmod cs89x0.o io=0x200 irq=0xA media=aui - -This example loads the module and configures the adapter to use an IO port base -address of 200h, interrupt 10, and use the AUI media connection. The following -configuration options are available on the command line:: - - io=### - specify IO address (200h-360h) - irq=## - specify interrupt level - use_dma=1 - Enable DMA - dma=# - specify dma channel (Driver is compiled to support - Rx DMA only) - dmasize=# (16 or 64) - DMA size 16K or 64K. Default value is set to 16. - media=rj45 - specify media type - or media=bnc - or media=aui - or media=auto - duplex=full - specify forced half/full/autonegotiate duplex - or duplex=half - or duplex=auto - debug=# - debug level (only available if the driver was compiled - for debugging) - -**Notes:** - -a) If an EEPROM is present, any specified command-line parameter - will override the corresponding configuration value stored in - EEPROM. - -b) The "io" parameter must be specified on the command-line. - -c) The driver's hardware probe routine is designed to avoid - writing to I/O space until it knows that there is a cs89x0 - card at the written addresses. This could cause problems - with device probing. To avoid this behaviour, add one - to the ``io=`` module parameter. This doesn't actually change - the I/O address, but it is a flag to tell the driver - to partially initialise the hardware before trying to - identify the card. This could be dangerous if you are - not sure that there is a cs89x0 card at the provided address. - - For example, to scan for an adapter located at IO base 0x300, - specify an IO address of 0x301. - -d) The "duplex=auto" parameter is only supported for the CS8920. - -e) The minimum command-line configuration required if an EEPROM is - not present is: - - io - irq - media type (no autodetect) - -f) The following additional parameters are CS89XX defaults (values - used with no EEPROM or command-line argument). - - * DMA Burst = enabled - * IOCHRDY Enabled = enabled - * UseSA = enabled - * CS8900 defaults to half-duplex if not specified on command-line - * CS8920 defaults to autoneg if not specified on command-line - * Use reset defaults for other config parameters - * dma_mode = 0 - -g) You can use ifconfig to set the adapter's Ethernet address. - -h) Many Linux distributions use the 'modprobe' command to load - modules. This program uses the '/etc/conf.modules' file to - determine configuration information which is passed to a driver - module when it is loaded. All the configuration options which are - described above may be placed within /etc/conf.modules. - - For example:: - - > cat /etc/conf.modules - ... - alias eth0 cs89x0 - options cs89x0 io=0x0200 dma=5 use_dma=1 - ... - - In this example we are telling the module system that the - ethernet driver for this machine should use the cs89x0 driver. We - are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma' - arguments to the driver when it is loaded. - -i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or - 7. You will probably find that other DMA channels will not work. - -j) The cs89x0 supports DMA for receiving only. DMA mode is - significantly more efficient. Flooding a 400 MHz Celeron machine - with large ping packets consumes 82% of its CPU capacity in non-DMA - mode. With DMA this is reduced to 45%. - -k) If your Linux kernel was compiled with inbuilt plug-and-play - support you will be able to find information about the cs89x0 card - with the command:: - - cat /proc/isapnp - -l) If during DMA operation you find erratic behavior or network data - corruption you should use your PC's BIOS to slow the EISA bus clock. - -m) If the cs89x0 driver is compiled directly into the kernel - (non-modular) then its I/O address is automatically determined by - ISA bus probing. The IRQ number, media options, etc are determined - from the card's EEPROM. - -n) If the cs89x0 driver is compiled directly into the kernel, DMA - mode may be selected by providing the kernel with a boot option - 'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7). - - Kernel boot options may be provided on the LILO command line:: - - LILO boot: linux cs89x0_dma=5 - - or they may be placed in /etc/lilo.conf:: - - image=/boot/bzImage-2.3.48 - append="cs89x0_dma=5" - label=linux - root=/dev/hda5 - read-only - - The DMA Rx buffer size is hardwired to 16 kbytes in this mode. - (64k mode is not available). - - -4. Compiling the Driver -======================= - -The cs89x0 driver can be compiled directly into the kernel or compiled into -a loadable device driver module. - -Just use the standard way to configure the driver and compile the Kernel. - - -4.1. Compiling the Driver to Support Rx DMA -------------------------------------------- - -The compile-time optionality for DMA was removed in the 2.3 kernel -series. DMA support is now unconditionally part of the driver. It is -enabled by the 'use_dma=1' module option. - - -5. Testing and Troubleshooting -============================== - -5.1. Known Defects and Limitations ----------------------------------- - -Refer to the RELEASE.TXT file distributed as part of this archive for a list of -known defects, driver limitations, and work arounds. - - -5.2. Testing the Adapter ------------------------- - -Once the adapter has been installed and configured, the diagnostic option of -the CS8900/20 Setup Utility can be used to test the functionality of the -adapter and its network connection. Use the diagnostics 'Self Test' option to -test the functionality of the adapter with the hardware configuration you have -assigned. You can use the diagnostics 'Network Test' to test the ability of the -adapter to communicate across the Ethernet with another PC equipped with a -CS8900/20-based adapter card (it must also be running the CS8900/20 Setup -Utility). - -.. note:: - - The Setup Utility's diagnostics are designed to run in a - DOS-only operating system environment. DO NOT run the diagnostics - from a DOS or command prompt session under Windows 95, Windows NT, - OS/2, or other operating system. - -To run the diagnostics tests on the CS8900/20 adapter: - - 1. Boot DOS on the PC and start the CS8900/20 Setup Utility. - - 2. The adapter's current configuration is displayed. Hit the ENTER key to - get to the main menu. - - 4. Select 'Diagnostics' (ALT-G) from the main menu. - * Select 'Self-Test' to test the adapter's basic functionality. - * Select 'Network Test' to test the network connection and cabling. - - -5.2.1. Diagnostic Self-test -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The diagnostic self-test checks the adapter's basic functionality as well as -its ability to communicate across the ISA bus based on the system resources -assigned during hardware configuration. The following tests are performed: - - * IO Register Read/Write Test - - The IO Register Read/Write test insures that the CS8900/20 can be - accessed in IO mode, and that the IO base address is correct. - - * Shared Memory Test - - The Shared Memory test insures the CS8900/20 can be accessed in memory - mode and that the range of memory addresses assigned does not conflict - with other devices in the system. - - * Interrupt Test - - The Interrupt test insures there are no conflicts with the assigned IRQ - signal. - - * EEPROM Test - - The EEPROM test insures the EEPROM can be read. - - * Chip RAM Test - - The Chip RAM test insures the 4K of memory internal to the CS8900/20 is - working properly. - - * Internal Loop-back Test - - The Internal Loop Back test insures the adapter's transmitter and - receiver are operating properly. If this test fails, make sure the - adapter's cable is connected to the network (check for LED activity for - example). - - * Boot PROM Test - - The Boot PROM test insures the Boot PROM is present, and can be read. - Failure indicates the Boot PROM was not successfully read due to a - hardware problem or due to a conflicts on the Boot PROM address - assignment. (Test only applies if the adapter is configured to use the - Boot PROM option.) - -Failure of a test item indicates a possible system resource conflict with -another device on the ISA bus. In this case, you should use the Manual Setup -option to reconfigure the adapter by selecting a different value for the system -resource that failed. - - -5.2.2. Diagnostic Network Test -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The Diagnostic Network Test verifies a working network connection by -transferring data between two CS8900/20 adapters installed in different PCs -on the same network. (Note: the diagnostic network test should not be run -between two nodes across a router.) - -This test requires that each of the two PCs have a CS8900/20-based adapter -installed and have the CS8900/20 Setup Utility running. The first PC is -configured as a Responder and the other PC is configured as an Initiator. -Once the Initiator is started, it sends data frames to the Responder which -returns the frames to the Initiator. - -The total number of frames received and transmitted are displayed on the -Initiator's display, along with a count of the number of frames received and -transmitted OK or in error. The test can be terminated anytime by the user at -either PC. - -To setup the Diagnostic Network Test: - - 1. Select a PC with a CS8900/20-based adapter and a known working network - connection to act as the Responder. Run the CS8900/20 Setup Utility - and select 'Diagnostics -> Network Test -> Responder' from the main - menu. Hit ENTER to start the Responder. - - 2. Return to the PC with the CS8900/20-based adapter you want to test and - start the CS8900/20 Setup Utility. - - 3. From the main menu, Select 'Diagnostic -> Network Test -> Initiator'. - Hit ENTER to start the test. - -You may stop the test on the Initiator at any time while allowing the Responder -to continue running. In this manner, you can move to additional PCs and test -them by starting the Initiator on another PC without having to stop/start the -Responder. - - - -5.3. Using the Adapter's LEDs ------------------------------ - -The 2 and 3-media adapters have two LEDs visible on the back end of the board -located near the 10Base-T connector. - -Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T -connection. (Only applies to 10Base-T. The green LED has no significance for -a 10Base-2 or AUI connection.) - -TX/RX LED: The yellow LED lights briefly each time the adapter transmits or -receives data. (The yellow LED will appear to "flicker" on a typical network.) - - -5.4. Resolving I/O Conflicts ----------------------------- - -An IO conflict occurs when two or more adapter use the same ISA resource (IO -address, memory address or IRQ). You can usually detect an IO conflict in one -of four ways after installing and or configuring the CS8900/20-based adapter: - - 1. The system does not boot properly (or at all). - - 2. The driver cannot communicate with the adapter, reporting an "Adapter - not found" error message. - - 3. You cannot connect to the network or the driver will not load. - - 4. If you have configured the adapter to run in memory mode but the driver - reports it is using IO mode when loading, this is an indication of a - memory address conflict. - -If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a -diagnostic self-test. Normally, the ISA resource in conflict will fail the -self-test. If so, reconfigure the adapter selecting another choice for the -resource in conflict. Run the diagnostics again to check for further IO -conflicts. - -In some cases, such as when the PC will not boot, it may be necessary to remove -the adapter and reconfigure it by installing it in another PC to run the -CS8900/20 Setup Utility. Once reinstalled in the target system, run the -diagnostics self-test to ensure the new configuration is free of conflicts -before loading the driver again. - -When manually configuring the adapter, keep in mind the typical ISA system -resource usage as indicated in the tables below. - -:: - - I/O Address Device IRQ Device - ----------- -------- --- -------- - 200-20F Game I/O adapter 3 COM2, Bus Mouse - 230-23F Bus Mouse 4 COM1 - 270-27F LPT3: third parallel port 5 LPT2 - 2F0-2FF COM2: second serial port 6 Floppy Disk controller - 320-32F Fixed disk controller 7 LPT1 - 8 Real-time Clock - 9 EGA/VGA display adapter - 12 Mouse (PS/2) - Memory Address Device 13 Math Coprocessor - -------------- --------------------- 14 Hard Disk controller - A000-BFFF EGA Graphics Adapter - A000-C7FF VGA Graphics Adapter - B000-BFFF Mono Graphics Adapter - B800-BFFF Color Graphics Adapter - E000-FFFF AT BIOS - - - - -6. Technical Support -==================== - -6.1. Contacting Cirrus Logic's Technical Support ------------------------------------------------- - -Cirrus Logic's CS89XX Technical Application Support can be reached at:: - - Telephone :(800) 888-5016 (from inside U.S. and Canada) - :(512) 442-7555 (from outside the U.S. and Canada) - Fax :(512) 912-3871 - Email :ethernet@crystal.cirrus.com - WWW :http://www.cirrus.com - - -6.2. Information Required before Contacting Technical Support -------------------------------------------------------------- - -Before contacting Cirrus Logic for technical support, be prepared to provide as -Much of the following information as possible. - -1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.) - -2.) Adapter configuration - - * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel - * Plug and Play enabled/disabled (CS8920-based adapters only) - * Configured for media auto-detect or specific media type (which type). - -3.) PC System's Configuration - - * Plug and Play system (yes/no) - * BIOS (make and version) - * System make and model - * CPU (type and speed) - * System RAM - * SCSI Adapter - -4.) Software - - * CS89XX driver and version - * Your network operating system and version - * Your system's OS version - * Version of all protocol support files - -5.) Any Error Message displayed. - - - -6.3 Obtaining the Latest Driver Version ---------------------------------------- - -You can obtain the latest CS89XX drivers and support software from Cirrus Logic's -Web site. You can also contact Cirrus Logic's Technical Support (email: -ethernet@crystal.cirrus.com) and request that you be registered for automatic -software-update notification. - -Cirrus Logic maintains a web page at http://www.cirrus.com with the -latest drivers and technical publications. - - -6.4. Current maintainer ------------------------ - -In February 2000 the maintenance of this driver was assumed by Andrew -Morton. - -6.5 Kernel module parameters ----------------------------- - -For use in embedded environments with no cs89x0 EEPROM, the kernel boot -parameter ``cs89x0_media=`` has been implemented. Usage is:: - - cs89x0_media=rj45 or - cs89x0_media=aui or - cs89x0_media=bnc diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst index 1d25be493ae9..786a23c84b90 100644 --- a/Documentation/networking/device_drivers/ethernet/index.rst +++ b/Documentation/networking/device_drivers/ethernet/index.rst @@ -19,7 +19,6 @@ Contents: amd/pds_vfio_pci aquantia/atlantic chelsio/cxgb - cirrus/cs89x0 dlink/dl2k davicom/dm9000 dec/dmfe diff --git a/Documentation/networking/devlink/devlink-health.rst b/Documentation/networking/devlink/devlink-health.rst index 4d10536377ab..bedac58a2f36 100644 --- a/Documentation/networking/devlink/devlink-health.rst +++ b/Documentation/networking/devlink/devlink-health.rst @@ -33,7 +33,9 @@ Device driver can provide specific callbacks for each "health reporter", e.g.: * Recovery procedures * Diagnostics procedures * Object dump procedures - * Out Of Box initial parameters + +Drivers also provide default values for generic reporter parameters when +creating a health reporter. Different parts of the driver can register different types of health reporters with different handlers. @@ -45,8 +47,9 @@ Once an error is reported, devlink health will perform the following actions: * A log is being send to the kernel trace events buffer * Health status and statistics are being updated for the reporter instance - * Object dump is being taken and saved at the reporter instance (as long as - auto-dump is set and there is no other dump which is already stored) + * Object dump is being taken and saved at the reporter instance. This is + best effort and skipped when recovery is aborted, auto-dump is disabled, + no dump callback is registered, or a dump is already stored. * Auto recovery attempt is being done. Depends on: - Auto-recovery configuration @@ -75,7 +78,8 @@ User Interface ============== User can access/change each reporter's parameters and driver specific callbacks -via ``devlink``, e.g per error type (per health reporter): +via ``devlink``, e.g. per error type (per health reporter). Reporters may be +registered for the whole devlink instance or for a specific devlink port. * Configure reporter's generic parameters (like: disable/enable auto recovery) * Invoke recovery procedure diff --git a/Documentation/networking/devlink/devlink-params.rst b/Documentation/networking/devlink/devlink-params.rst index ea17756dcda6..ca19ee3e63c8 100644 --- a/Documentation/networking/devlink/devlink-params.rst +++ b/Documentation/networking/devlink/devlink-params.rst @@ -122,7 +122,7 @@ own name. * - ``enable_iwarp`` - Boolean - Enable handling of iWARP traffic in the device. - * - ``internal_err_reset`` + * - ``internal_error_reset`` - Boolean - When enabled, the device driver will reset the device on internal errors. diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst index 5e397798a402..9374ebe70f48 100644 --- a/Documentation/networking/devlink/devlink-port.rst +++ b/Documentation/networking/devlink/devlink-port.rst @@ -38,7 +38,7 @@ Devlink port flavours are described below. - This indicates an eswitch port representing a port of PCI subfunction (SF). * - ``DEVLINK_PORT_FLAVOUR_VIRTUAL`` - - This indicates a virtual port for the PCI virtual function. + - Any virtual port facing the user. Devlink port can have a different type based on the link layer described below. @@ -134,6 +134,9 @@ Users may also set the IPsec crypto capability of the function using Users may also set the IPsec packet capability of the function using `devlink port function set ipsec_packet` command. +The ``migratable`` attribute may be set only on ports with +``DEVLINK_PORT_FLAVOUR_PCI_VF``. + Users may also set the maximum IO event queues of the function using `devlink port function set max_io_eqs` command. diff --git a/Documentation/networking/devlink/devlink-trap.rst b/Documentation/networking/devlink/devlink-trap.rst index 5885e21e2212..ac5bf9337198 100644 --- a/Documentation/networking/devlink/devlink-trap.rst +++ b/Documentation/networking/devlink/devlink-trap.rst @@ -516,9 +516,11 @@ Generic Packet Trap Groups Generic packet trap groups are used to aggregate logically related packet traps. These groups allow the user to batch operations such as setting the trap -action of all member traps. In addition, ``devlink-trap`` can report aggregated -per-group packets and bytes statistics, in case per-trap statistics are too -narrow. The description of these groups must be added to the following table: +action of all member drop traps whose action may legally change. Exception and +control traps remain unchanged. In addition, ``devlink-trap`` can report +aggregated per-group packets and bytes statistics, in case per-trap statistics +are too narrow. The description of these groups must be added to the following +table: .. list-table:: List of Generic Packet Trap Groups :widths: 10 90 diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index f7ba7dcf477d..32f70879ddd0 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -13,8 +13,8 @@ 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 all callbacks of devlink -netlink commands. +lock by calling ``devl_lock()``. It is also held across all callbacks of +devlink netlink commands. Drivers are encouraged to use the devlink instance lock for their own needs. @@ -33,11 +33,11 @@ sure to respect following rules: lock of both nested and parent instances at the same time, devlink instance lock of the parent instance should be taken first, only then instance lock of the nested instance could be taken. - - Driver should use object-specific helpers to setup the - nested relationship: + - Driver should use object-specific helpers to setup the nested relationship + before registering the nested devlink instance: - ``devl_nested_devlink_set()`` - called to setup devlink -> nested - devlink relationship (could be user for multiple nested instances. + devlink relationship (could be used for multiple nested instances). - ``devl_port_fn_devlink_set()`` - called to setup port function -> nested devlink relationship. - ``devlink_linecard_nested_dl_set()`` - called to setup linecard -> diff --git a/Documentation/networking/devlink/stmmac.rst b/Documentation/networking/devlink/stmmac.rst index 47e3ff10bc08..fbaa81ea782d 100644 --- a/Documentation/networking/devlink/stmmac.rst +++ b/Documentation/networking/devlink/stmmac.rst @@ -24,7 +24,7 @@ The ``stmmac`` driver implements the following driver-specific parameters. - runtime - Enable the Coarse timestamping mode, as defined in the DWMAC TRM. A detailed explanation of this timestamping mode can be found in the - Socfpga Functionnal Description [1]. + Socfpga Functional Description [1]. In Coarse mode, the ptp clock is expected to be fed by a high-precision clock that is externally adjusted, and the subsecond increment used for diff --git a/Documentation/networking/devmem.rst b/Documentation/networking/devmem.rst index a6cd7236bfbd..6a3f3c2ac19c 100644 --- a/Documentation/networking/devmem.rst +++ b/Documentation/networking/devmem.rst @@ -103,24 +103,22 @@ The user must bind a dmabuf to any number of RX queues on a given NIC using the netlink API:: /* Bind dmabuf to NIC RX queue 15 */ - struct netdev_queue *queues; - queues = malloc(sizeof(*queues) * 1); + struct netdev_queue_id *queues; - queues[0]._present.type = 1; - queues[0]._present.idx = 1; - queues[0].type = NETDEV_RX_QUEUE_TYPE_RX; - queues[0].idx = 15; + queues = netdev_queue_id_alloc(1); + netdev_queue_id_set_type(&queues[0], NETDEV_QUEUE_TYPE_RX); + netdev_queue_id_set_id(&queues[0], 15); *ys = ynl_sock_create(&ynl_netdev_family, &yerr); req = netdev_bind_rx_req_alloc(); netdev_bind_rx_req_set_ifindex(req, 1 /* ifindex */); - netdev_bind_rx_req_set_dmabuf_fd(req, dmabuf_fd); - __netdev_bind_rx_req_set_queues(req, queues, n_queue_index); + netdev_bind_rx_req_set_fd(req, dmabuf_fd); + __netdev_bind_rx_req_set_queues(req, queues, 1); rsp = netdev_bind_rx(*ys, req); - dmabuf_id = rsp->dmabuf_id; + dmabuf_id = rsp->id; The netlink API returns a dmabuf_id: a unique ID that refers to this dmabuf @@ -302,13 +300,12 @@ The user should create a msghdr where, * iov_base is set to the offset into the dmabuf to start sending from * iov_len is set to the number of bytes to be sent from the dmabuf -The user passes the dma-buf id to send from via the dmabuf_tx_cmsg.dmabuf_id. +The user passes the dma-buf id to send from as a u32 cmsg payload. The example below sends 1024 bytes from offset 100 into the dmabuf, and 2048 from offset 2000 into the dmabuf. The dmabuf to send from is tx_dmabuf_id:: - char ctrl_data[CMSG_SPACE(sizeof(struct dmabuf_tx_cmsg))]; - struct dmabuf_tx_cmsg ddmabuf; + char ctrl_data[CMSG_SPACE(sizeof(__u32))]; struct msghdr msg = {}; struct cmsghdr *cmsg; struct iovec iov[2]; @@ -327,11 +324,9 @@ from offset 2000 into the dmabuf. The dmabuf to send from is tx_dmabuf_id:: cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_DEVMEM_DMABUF; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct dmabuf_tx_cmsg)); + cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); - ddmabuf.dmabuf_id = tx_dmabuf_id; - - *((struct dmabuf_tx_cmsg *)CMSG_DATA(cmsg)) = ddmabuf; + *((__u32 *)CMSG_DATA(cmsg)) = tx_dmabuf_id; sendmsg(socket_fd, &msg, MSG_ZEROCOPY); diff --git a/Documentation/networking/driver.rst b/Documentation/networking/driver.rst index 4f5dfa9c022e..920c0ec98759 100644 --- a/Documentation/networking/driver.rst +++ b/Documentation/networking/driver.rst @@ -51,7 +51,7 @@ for a driver implementing scatter-gather this means: { u32 used = READ_ONCE(dr->prod) - READ_ONCE(dr->cons); - return dr->tx_ring_size - (used & bp->tx_ring_mask); + return dr->tx_ring_size - (used & dr->tx_ring_mask); } static netdev_tx_t drv_hard_start_xmit(struct sk_buff *skb, @@ -69,7 +69,7 @@ for a driver implementing scatter-gather this means: //... /* This should be a very rare race - log it. */ if (drv_tx_avail(dr) <= skb_shinfo(skb)->nr_frags + 1) { - netif_stop_queue(dev); + netif_tx_stop_queue(txq); netdev_warn(dev, "Tx Ring full when queue awake!\n"); return NETDEV_TX_BUSY; } @@ -103,6 +103,9 @@ Lockless queue stop / wake helper macros .. kernel-doc:: include/net/netdev_queues.h :doc: Lockless queue stopping / waking helpers. +The standard macros like netif_txq_maybe_stop(), netif_txq_try_stop() etc. +are well tested, prefer them over local synchronization schemes. + No exclusive ownership ---------------------- @@ -125,3 +128,16 @@ to be freed up. If you return NETDEV_TX_BUSY from the ndo_start_xmit method, you must not keep any reference to that SKB and you must not attempt to free it up. + +Error message reporting +======================= + +A number of driver configuration interfaces pass a Netlink extended ACK +(``extack``) object to the driver (either directly as an argument or +as a member of a parameter struct). The drivers should try to report +most errors via the ``extack`` object. System level exceptions, +indicating that system or device is misbehaving or is in bad state, +should continue to be reported to system logs. + +Messages should be passed **either** via ``extack`` **or** to system logs. +Drivers should not try to report the same information to both. diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst index fd3c254ced1d..42a99f5dfa2e 100644 --- a/Documentation/networking/dsa/dsa.rst +++ b/Documentation/networking/dsa/dsa.rst @@ -509,7 +509,7 @@ Device Tree ----------- DSA features a standardized binding which is documented in -``Documentation/devicetree/bindings/net/dsa/dsa.txt``. PHY/MDIO library helper +``Documentation/devicetree/bindings/net/dsa/dsa.yaml``. PHY/MDIO library helper functions such as ``of_get_phy_mode()``, ``of_phy_connect()`` are also used to query per-port PHY specific details: interface connection, MDIO bus location, etc. diff --git a/Documentation/networking/dsa/lan9303.rst b/Documentation/networking/dsa/lan9303.rst index ab81b4e0139e..776572be265e 100644 --- a/Documentation/networking/dsa/lan9303.rst +++ b/Documentation/networking/dsa/lan9303.rst @@ -12,7 +12,7 @@ Driver details The driver is implemented as a DSA driver, see ``Documentation/networking/dsa/dsa.rst``. -See ``Documentation/devicetree/bindings/net/dsa/lan9303.txt`` for device tree +See ``Documentation/devicetree/bindings/net/dsa/smsc,lan9303.yaml`` for device tree binding. The LAN9303 can be managed both via MDIO and I2C, both supported by this driver. diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 2e3a746fcc6d..208f46967ee5 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -489,7 +489,7 @@ tcp_ecn - INTEGER tcp_ecn_option - INTEGER Control Accurate ECN (AccECN) option sending when AccECN has been successfully negotiated during handshake. Send logic inhibits - sending AccECN options regarless of this setting when no AccECN + sending AccECN options regardless of this setting when no AccECN option has been seen for the reverse direction. Possible values are: @@ -2444,7 +2444,11 @@ fib_multipath_hash_policy - INTEGER Possible values: - - 0 - Layer 3 (source and destination addresses plus flow label) + - 0 - Layer 3 (source and destination addresses plus flow label). + For IPv6 TCP, the local ECMP path is selected from the socket + txhash rather than the flow label, and may change after a TCP + rehash event (such as a retransmission timeout) to recover from + path failure. The on-wire flow label is unaffected. - 1 - Layer 4 (standard 5-tuple) - 2 - Layer 3 or inner Layer 3 if present - 3 - Custom multipath hash. Fields used for multipath hash calculation diff --git a/Documentation/networking/ipvs-sysctl.rst b/Documentation/networking/ipvs-sysctl.rst index a556439f8be7..fe36f4fcd3a0 100644 --- a/Documentation/networking/ipvs-sysctl.rst +++ b/Documentation/networking/ipvs-sysctl.rst @@ -56,6 +56,50 @@ conn_lfactor - INTEGER -4: grow if load goes above 6% (buckets = nodes * 16) 2: grow if load goes above 400% (buckets = nodes / 4) +conn_max - INTEGER + Limit for number of connections, per netns. + + Controls the soft and hard limit for number of connections. + Initially, the platform specific limit is assigned for init_net. + The value can be changed and later the soft limit propagated + to other networking namespaces. + + Privileged admin can change both limits up to the value of the + platform limit while the unprivileged admin can change only the + soft limit up to the value of the hard limit. + + For setups using conntrack=1 (CONFIG_IP_VS_NFCT for + Netfilter connection tracking) the connections can be + limited also by nf_conntrack_max. + + Limits for init_net: + + ======================= =============== ============= + \ soft limit hard limit + ======================= =============== ============= + create netns platform platform + priv admin 0 .. platform 0 .. platform + ======================= =============== ============= + + Limits for new netns: + + ======================= =============== ============= + \ soft limit hard limit + ======================= =============== ============= + create netns init_net:soft init_net:soft + priv admin 0 .. platform 0 .. platform + unpriv admin 0 .. hard N/A + ======================= =============== ============= + + Limits per platform: + + - 1,073,741,824 (2^30 for 64-bit) + - 16,777,216 (2^24 for 32-bit) + + Possible values: 0 .. platform limit + + Default: platform limit + conn_reuse_mode - INTEGER 1 - default diff --git a/Documentation/networking/mptcp-sysctl.rst b/Documentation/networking/mptcp-sysctl.rst index 1eb6af26b4a7..b9b5f58e0625 100644 --- a/Documentation/networking/mptcp-sysctl.rst +++ b/Documentation/networking/mptcp-sysctl.rst @@ -21,6 +21,19 @@ add_addr_timeout - INTEGER (seconds) Default: 120 +add_addr_v6_port_drop_ts - BOOLEAN + Control whether preparing an ADD_ADDR with an IPv6 address and a port + should drop the TCP timestamps option to have enough option space to + send the signal. + + If there is not enough option space, and the TCP timestamps option + cannot be dropped, the signal cannot be sent. Note that dropping the TCP + timestamps option for one packet of the connection could disrupt some + middleboxes: even if it should be unlikely, they could drop the packet + or block the connection. This is a per-namespace sysctl. + + Default: 1 (enabled) + allow_join_initial_addr_port - BOOLEAN Allow peers to send join requests to the IP address and port number used by the initial subflow if the value is 1. This controls a flag that is diff --git a/Documentation/networking/napi.rst b/Documentation/networking/napi.rst index 4e008efebb35..c719924f36ce 100644 --- a/Documentation/networking/napi.rst +++ b/Documentation/networking/napi.rst @@ -49,7 +49,11 @@ instance to be released. The control APIs are not idempotent. Control API calls are safe against concurrent use of datapath APIs but an incorrect sequence of control API calls may result in crashes, deadlocks, or race conditions. For example, -calling napi_disable() multiple times in a row will deadlock. +calling napi_disable() multiple times in a row will hang waiting for +ownership of the NAPI instance to be released. + +Drivers using the netdev instance lock may need to use the ``_locked()`` +variants of the control APIs when that lock is already held. Datapath API ------------ @@ -190,7 +194,8 @@ User API ======== User interactions with NAPI depend on NAPI instance ID. The instance IDs -are only visible to the user thru the ``SO_INCOMING_NAPI_ID`` socket option. +are visible to the user through the ``SO_INCOMING_NAPI_ID`` socket option +and the netdev Netlink API. Users can query NAPI IDs for a device or device queue using netlink. This can be done programmatically in a user application or by using a script included in @@ -371,7 +376,7 @@ To use this mechanism: the application has stalled. This value should be chosen so that it covers the amount of time the user application needs to process data from its call to epoll_wait, noting that applications can control how much data - they retrieve by setting ``max_events`` when calling epoll_wait. + they retrieve by setting ``maxevents`` when calling epoll_wait. 2. The sysfs parameter or per-NAPI config parameters ``gro_flush_timeout`` and ``napi_defer_hard_irqs`` can be set to low values. They will be used diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst index 1c19bb7705df..512f6d6fa3d8 100644 --- a/Documentation/networking/net_cachelines/net_device.rst +++ b/Documentation/networking/net_cachelines/net_device.rst @@ -10,7 +10,7 @@ Type Name fastpath_tx_acce =================================== =========================== =================== =================== =================================================================================== unsigned_long:32 priv_flags read_mostly __dev_queue_xmit(tx) unsigned_long:1 lltx read_mostly HARD_TX_LOCK,HARD_TX_TRYLOCK,HARD_TX_UNLOCK(tx) -unsigned long:1 netmem_tx:1; read_mostly +unsigned_long:2 netmem_tx:2; read_mostly char name[16] struct netdev_name_node* name_node struct dev_ifalias* ifalias @@ -90,8 +90,6 @@ struct inet6_dev* ip6_ptr read_mostly struct vlan_info* vlan_info struct dsa_port* dsa_ptr struct tipc_bearer* tipc_ptr -void* atalk_ptr -void* ax25_ptr struct wireless_dev* ieee80211_ptr struct wpan_dev* ieee802154_ptr struct mpls_dev* mpls_ptr diff --git a/Documentation/networking/net_cachelines/tcp_sock.rst b/Documentation/networking/net_cachelines/tcp_sock.rst index fecf61166a54..0f6088c4ab8b 100644 --- a/Documentation/networking/net_cachelines/tcp_sock.rst +++ b/Documentation/networking/net_cachelines/tcp_sock.rst @@ -13,16 +13,16 @@ u16 tcp_header_len read_mostly read_m u16 gso_segs read_mostly tcp_xmit_size_goal __be32 pred_flags read_write read_mostly tcp_select_window(tx);tcp_rcv_established(rx) u64 bytes_received read_write tcp_rcv_nxt_update(rx) -u32 segs_in read_write tcp_v6_rcv(rx) +u32 segs_in read_write read_write tcp_segs_in(),tcp_v6_rcv(rx),tcp_v4_rcv() u32 data_segs_in read_write tcp_v6_rcv(rx) u32 rcv_nxt read_mostly read_write tcp_cleanup_rbuf,tcp_send_ack,tcp_inq_hint,tcp_transmit_skb,tcp_receive_window(tx);tcp_v6_do_rcv,tcp_rcv_established,tcp_data_queue,tcp_receive_window,tcp_rcv_nxt_update(write)(rx) u32 copied_seq read_mostly tcp_cleanup_rbuf,tcp_rcv_space_adjust,tcp_inq_hint u32 rcv_wup read_write __tcp_cleanup_rbuf,tcp_receive_window,tcp_receive_established u32 snd_nxt read_write read_mostly tcp_rate_check_app_limited,__tcp_transmit_skb,tcp_event_new_data_sent(write)(tx);tcp_rcv_established,tcp_ack,tcp_clean_rtx_queue(rx) -u32 segs_out read_write __tcp_transmit_skb +u32 segs_out read_write read_write __tcp_transmit_skb u32 data_segs_out read_write __tcp_transmit_skb,tcp_update_skb_after_send u64 bytes_sent read_write __tcp_transmit_skb -u64 bytes_acked read_write tcp_snd_una_update/tcp_ack +u64 bytes_acked read_write tcp_snd_una_update/tcp_ack u32 dsack_dups u32 snd_una read_mostly read_write tcp_wnd_end,tcp_urg_mode,tcp_minshall_check,tcp_cwnd_validate(tx);tcp_ack,tcp_may_update_window,tcp_clean_rtx_queue(write),tcp_ack_tstamp(rx) u32 snd_sml read_write tcp_minshall_check,tcp_minshall_update @@ -99,8 +99,8 @@ u32 snd_cwnd_stamp u32 prior_cwnd u32 prr_delivered u32 prr_out read_mostly read_mostly tcp_rate_skb_sent,tcp_newly_delivered(tx);tcp_ack,tcp_rate_gen,tcp_clean_rtx_queue(rx) -u32 delivered read_mostly read_write tcp_rate_skb_sent, tcp_newly_delivered(tx);tcp_ack, tcp_rate_gen, tcp_clean_rtx_queue (rx) -u32 delivered_ce read_mostly read_write tcp_rate_skb_sent(tx);tcp_rate_gen(rx) +u32 delivered read_write tcp_rate_skb_sent, tcp_newly_delivered(tx);tcp_ack, tcp_rate_gen, tcp_clean_rtx_queue (rx) +u32 delivered_ce read_write tcp_rate_skb_sent(tx);tcp_rate_gen(rx) u32 received_ce read_mostly read_write u32[3] received_ecn_bytes read_mostly read_write u8:4 received_ce_pending read_mostly read_write diff --git a/Documentation/networking/netdev-features.rst b/Documentation/networking/netdev-features.rst index 02bd7536fc0c..f9e1f3921f53 100644 --- a/Documentation/networking/netdev-features.rst +++ b/Documentation/networking/netdev-features.rst @@ -18,29 +18,38 @@ that relieve an OS of various tasks like generating and checking checksums, splitting packets, classifying them. Those capabilities and their state are commonly referred to as netdev features in Linux kernel world. -There are currently three sets of features relevant to the driver, and -one used internally by network core: +There are currently three main sets of features on each netdevice, +first and second are initialized by the driver: 1. netdev->hw_features set contains features whose state may possibly be changed (enabled or disabled) for a particular device by user's - request. This set should be initialized in ndo_init callback and not - changed later. + request. Drivers normally initialize this set before registration or + in the ndo_init callback. Changes after registration should be made + very carefully as other parts of the code may assume hw_features are + static. At the very least changes must be made under rtnl_lock and + the netdev instance lock, and followed by netdev_update_features(). 2. netdev->features set contains features which are currently enabled for a device. This should be changed only by network core or in error paths of ndo_set_features callback. - 3. netdev->vlan_features set contains features whose state is inherited - by child VLAN devices (limits netdev->features set). This is currently - used for all VLAN devices whether tags are stripped or inserted in - hardware or software. - - 4. netdev->wanted_features set contains feature set requested by user. + 3. netdev->wanted_features set contains feature set requested by user. This set is filtered by ndo_fix_features callback whenever it or some device-specific conditions change. This set is internal to networking core and should not be referenced in drivers. +On top of those three main sets, each netdev has: + 1. Sets which control features inherited by child devices (VLAN, MPLS, + hw_enc for L3/L4 tunnels). These sets allow the driver to limit which + netdev->features are propagated, in case HW cannot perform the offloads + with the extra headers present. + + 2. netdev->mangleid_features, TSO features which are supported only when + IP ID field can be mangled (constant instead of incrementing) during TSO. + + 3. netdev->gso_partial_features, additional TSO features which HW can + support via NETIF_F_GSO_PARTIAL. Part II: Controlling enabled features ===================================== @@ -62,11 +71,22 @@ ndo_*_features callbacks are called with rtnl_lock held. Missing callbacks are treated as always returning success. A driver that wants to trigger recalculation must do so by calling -netdev_update_features() while holding rtnl_lock. This should not be done -from ndo_*_features callbacks. netdev->features should not be modified by -driver except by means of ndo_fix_features callback. +netdev_update_features() while holding rtnl_lock. If the device uses the +netdev instance lock, that lock must be held as well. This should not be +done from ndo_*_features callbacks. netdev->features should not be modified +by driver except by means of ndo_fix_features callback. +For "ops locked" drivers (see Documentation/networking/netdevices.rst), +ethtool callbacks that may end up invoking netdev_update_features() must +opt back into rtnl_lock by setting the matching ETHTOOL_OP_NEEDS_RTNL_* +bit in ``ethtool_ops::op_needs_rtnl``. The ethtool core then keeps +rtnl_lock held across those SET callbacks so the contract above still +holds. +ndo_features_check is called for each skb before that skb is passed to +ndo_start_xmit. Driver may perform any non-trivial checks (e.g. exact +header geometry / length) and withdraw features like HW_CSUM or TSO, +requesting the networking stack to fall back to the software implementation. Part III: Implementation hints ============================== @@ -83,8 +103,9 @@ stateless). It can be called multiple times between successive ndo_set_features calls. Callback must not alter features contained in NETIF_F_SOFT_FEATURES or -NETIF_F_NEVER_CHANGE sets. The exception is NETIF_F_VLAN_CHALLENGED but -care must be taken as the change won't affect already configured VLANs. +NETIF_F_NEVER_CHANGE, except that NETIF_F_VLAN_CHALLENGED may be changed. +Care must be taken as changes to NETIF_F_VLAN_CHALLENGED won't affect already +configured VLANs. * ndo_set_features: @@ -186,10 +207,14 @@ Redundancy) frames from one port to another in hardware. * hsr-dup-offload This should be set for devices which duplicate outgoing HSR (High-availability -Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically -frames in hardware. +Seamless Redundancy) or PRP (Parallel Redundancy Protocol) frames +automatically in hardware. -* netmem-tx +Part V: Related device flags +============================ -This should be set for devices which support netmem TX. See -Documentation/networking/netmem.rst +* netdev->netmem_tx + +This is not a netdev feature bit. Drivers support netmem TX by setting +netdev->netmem_tx to one of the values in enum netmem_tx_mode. +See Documentation/networking/netmem.rst. diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst index 93e06e8d51a9..fde601acd1d2 100644 --- a/Documentation/networking/netdevices.rst +++ b/Documentation/networking/netdevices.rst @@ -21,13 +21,14 @@ by free_netdev(). This is required to handle the pathological case cleanly alloc_netdev_mqs() / alloc_netdev() reserve extra space for driver private data which gets freed when the network device is freed. If separately allocated data is attached to the network device -(netdev_priv()) then it is up to the module exit handler to free that. +(extra pointers stored in the device private struct) then it is up +to the module exit handler to free that. There are two groups of APIs for registering struct net_device. First group can be used in normal contexts where ``rtnl_lock`` is not already held: register_netdev(), unregister_netdev(). Second group can be used when ``rtnl_lock`` is already held: -register_netdevice(), unregister_netdevice(), free_netdevice(). +register_netdevice(), unregister_netdevice(), free_netdev(). Simple drivers -------------- @@ -58,6 +59,7 @@ the register_netdev(), and unregister_netdev() functions: goto err_undo; /* net_device is visible to the user! */ + return 0; err_undo: /* ... undo the device setup ... */ @@ -73,7 +75,7 @@ the register_netdev(), and unregister_netdev() functions: Note that after calling register_netdev() the device is visible in the system. Users can open it and start sending / receiving traffic immediately, -or run any other callback, so all initialization must be done prior to +or run any other callback, so all initialization must be **complete** prior to registration. unregister_netdev() closes the device and waits for all users to be done @@ -157,7 +159,7 @@ register_netdevice() fails. The callback may be invoked with or without There is no explicit constructor callback, driver "constructs" the private netdev state after allocating it and before registration. -Setting struct net_device.needs_free_netdev makes core call free_netdevice() +Setting struct net_device.needs_free_netdev makes core call free_netdev() automatically after unregister_netdevice() when all references to the device are gone. It only takes effect after a successful call to register_netdevice() so if register_netdevice() fails driver is responsible for calling @@ -256,7 +258,7 @@ ndo_eth_ioctl: lock if the driver implements queue management or shaper API. Context: process -ndo_get_stats: +ndo_get_stats / ndo_get_stats64: Synchronization: RCU (can be called concurrently with the stats update path). Context: atomic (can't sleep under RCU) @@ -264,12 +266,9 @@ ndo_get_stats: ndo_start_xmit: Synchronization: __netif_tx_lock spinlock. - When the driver sets dev->lltx this will be - called without holding netif_tx_lock. In this case the driver - has to lock by itself when needed. - The locking there should also properly protect against - set_rx_mode. WARNING: use of dev->lltx is deprecated. - Don't use it for new drivers. + When the driver sets dev->lltx this will be called without holding + netif_tx_lock. dev->lltx is meant for software drivers only, since + they often have no per-queue state. Context: Process with BHs disabled or BH (timer), will be called with interrupts disabled by netconsole. @@ -304,11 +303,15 @@ ndo_change_rx_flags: lock if the driver implements queue management or shaper API. ndo_setup_tc: - ``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT locks - (i.e. no ``rtnl_lock`` and no device instance lock). The rest of - ``tc_setup_type`` types run under netdev instance lock if the driver + Locking depends on ``tc_setup_type``. For most types the callback + is invoked under ``rtnl_lock`` and netdev instance lock if the driver implements queue management or shaper API. + For ``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` ``rtnl_lock`` may or + may not be held, and the netdev instance lock is not held. + ``TC_SETUP_BLOCK`` runs under ``block->cb_lock`` and ``TC_SETUP_FT`` + runs under ``flowtable->flow_block_lock``. + Most ndo callbacks not specified in the list above are running under ``rtnl_lock``. In addition, netdev instance lock is taken as well if the driver implements queue management or shaper API. @@ -348,10 +351,6 @@ virtual and the physical device. To prevent deadlocks, the virtual device's lock must always be acquired before the physical device's (see ``netdev_nl_queue_create_doit``). -In the future, there will be an option for individual -drivers to opt out of using ``rtnl_lock`` and instead perform their control -operations directly under the netdev instance lock. - Device drivers are encouraged to rely on the instance lock where possible. For the (mostly software) drivers that need to interact with the core stack, @@ -372,9 +371,16 @@ the instance lock. struct ethtool_ops ------------------ -Similarly to ``ndos`` the instance lock is only held for select drivers. -For "ops locked" drivers all ethtool ops without exceptions should -be called under the instance lock. +For non-"ops locked" drivers ethtool_ops are executed under ``rtnl_lock``. + +For "ops locked" drivers, ``ethtool_ops``, unlike ``ndos``, run under +the instance lock **only**. Drivers may request that ``rtnl_lock`` +is held around specific operations (both SET and GET) by setting +appropriate bits in ``ethtool_ops::op_needs_rtnl`` (if the necessary +``ETHTOOL_OP_NEEDS_RTNL_*`` bit doesn't exist, just add it). +Commonly used core helpers which force drivers to selectively opt-in to +``rtnl_lock`` protection include ``netdev_update_features()``, +``netif_set_real_num_tx_queues()``, and phylink helpers. struct netdev_stat_ops ---------------------- @@ -412,6 +418,7 @@ The following netdev notifiers are always run under the instance lock: For devices with locked ops, currently only the following notifiers are running under the lock: * ``NETDEV_CHANGE`` +* ``NETDEV_CHANGENAME`` * ``NETDEV_REGISTER`` * ``NETDEV_UP`` diff --git a/Documentation/networking/netmem.rst b/Documentation/networking/netmem.rst index b63aded46337..217869d1108d 100644 --- a/Documentation/networking/netmem.rst +++ b/Documentation/networking/netmem.rst @@ -95,4 +95,10 @@ Driver TX Requirements netdev@, or reach out to the maintainers and/or almasrymina@google.com for help adding the netmem API. -2. Driver should declare support by setting `netdev->netmem_tx = true` +2. Driver should declare support by setting `netdev->netmem_tx` to the + appropriate mode: + + - `NETMEM_TX_DMA`: for physical devices that perform DMA. + + - `NETMEM_TX_NO_DMA`: for virtual or passthrough devices that do + not DMA, but still support handling of netmem-backed skbs. diff --git a/Documentation/networking/page_pool.rst b/Documentation/networking/page_pool.rst index 9d958128a57c..817f8b78d246 100644 --- a/Documentation/networking/page_pool.rst +++ b/Documentation/networking/page_pool.rst @@ -43,18 +43,32 @@ Architecture overview Monitoring ========== -Information about page pools on the system can be accessed via the netdev -genetlink family (see Documentation/netlink/specs/netdev.yaml). +Information about allocated page pools, their memory use, recycling statistics +etc. can be accessed via the netdev genetlink family +(see Documentation/netlink/specs/netdev.yaml). + +Statistics +---------- + +.. kernel-doc:: include/net/page_pool/types.h + :identifiers: struct page_pool_recycle_stats + struct page_pool_alloc_stats + struct page_pool_stats API interface ============= -The number of pools created **must** match the number of hardware queues +The number of pools created **must** match the number of NAPI contexts / queues unless hardware restrictions make that impossible. This would otherwise beat the purpose of page pool, which is allocate pages fast from cache without locking. This lockless guarantee naturally comes from running under a NAPI softirq. The protection doesn't strictly have to be NAPI, any guarantee that allocating a page will cause no race conditions is enough. +If ``params.napi`` is set, the NAPI instance must be the sole consumer +context for pages allocated from the pool. In other words, when running in +that NAPI context, the page pool may safely access consumer-side resources +**without any additional locking**. + .. kernel-doc:: net/core/page_pool.c :identifiers: page_pool_create @@ -69,7 +83,7 @@ a page will cause no race conditions is enough. page_pool_get_dma_addr page_pool_get_dma_dir .. kernel-doc:: net/core/page_pool.c - :identifiers: page_pool_put_page_bulk page_pool_get_stats + :identifiers: page_pool_put_page_bulk DMA sync -------- @@ -98,29 +112,12 @@ If in doubt set ``offset`` to 0, ``max_len`` to ``PAGE_SIZE`` and pass -1 as ``dma_sync_size``. That combination of arguments is always correct. -Note that the syncing parameters are for the entire page. -This is important to remember when using fragments (``PP_FLAG_PAGE_FRAG``), -where allocated buffers may be smaller than a full page. +Note that the syncing parameters are for the **entire page**, even if +the driver allocates fragments (e.g. via ``page_pool_dev_alloc_frag()``). Unless the driver author really understands page pool internals it's recommended to always use ``offset = 0``, ``max_len = PAGE_SIZE`` with fragmented page pools. -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. - -Older drivers expose page pool statistics via ethtool or debugfs. -The same statistics are accessible via the netlink netdev family -in a driver-independent fashion. - -.. kernel-doc:: include/net/page_pool/types.h - :identifiers: struct page_pool_recycle_stats - struct page_pool_alloc_stats - struct page_pool_stats - Coding examples =============== @@ -140,7 +137,7 @@ Registration pp_params.pool_size = DESC_NUM; pp_params.nid = NUMA_NO_NODE; pp_params.dev = priv->dev; - pp_params.napi = napi; /* only if locking is tied to NAPI */ + pp_params.napi = napi; /* only if this NAPI is the sole consumer, see above */ pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; page_pool = page_pool_create(&pp_params); @@ -174,21 +171,6 @@ 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/segmentation-offloads.rst b/Documentation/networking/segmentation-offloads.rst index 72f69b22b28c..25a8b7eca847 100644 --- a/Documentation/networking/segmentation-offloads.rst +++ b/Documentation/networking/segmentation-offloads.rst @@ -14,10 +14,13 @@ to take advantage of segmentation offload capabilities of various NICs. The following technologies are described: * TCP Segmentation Offload - TSO * UDP Fragmentation Offload - UFO + * UDP Segmentation Offload - USO * IPIP, SIT, GRE, and UDP Tunnel Offloads * Generic Segmentation Offload - GSO * Generic Receive Offload - GRO * Partial Generic Segmentation Offload - GSO_PARTIAL + * ESP Segmentation Offload + * Fraglist Generic Segmentation Offload - GSO_FRAGLIST * SCTP acceleration with GSO - GSO_BY_FRAGS @@ -38,7 +41,8 @@ In order to support TCP segmentation offload it is necessary to populate the network and transport header offsets of the skbuff so that the device drivers will be able determine the offsets of the IP or IPv6 header and the TCP header. In addition as CHECKSUM_PARTIAL is required csum_start should -also point to the TCP header of the packet. +also point to the TCP header of the packet, or to the inner transport header +for encapsulated TSO. For IPv4 segmentation we support one of two types in terms of the IP ID. The default behavior is to increment the IP ID with every segment. If the @@ -57,6 +61,10 @@ DF bit is not set on the outer header, in which case the device driver must guarantee that the IP ID field is incremented in the outer header with every segment. +SKB_GSO_TCP_ACCECN is a modifier used with TCP segmentation offload for +AccECN packets where the CWR bit must not be cleared during segmentation. +Devices advertise support for this using NETIF_F_GSO_ACCECN. + UDP Fragmentation Offload ========================= @@ -71,6 +79,16 @@ still receive them from tuntap and similar devices. Offload of UDP-based tunnel protocols is still supported. +UDP Segmentation Offload +======================== + +UDP segmentation offload allows a device to segment a large UDP packet into +multiple UDP datagrams. Unlike UFO, these are not IP fragments. The payload +size of each datagram is specified in skb_shinfo()->gso_size and the GSO type +is SKB_GSO_UDP_L4. Devices advertise support for this using +NETIF_F_GSO_UDP_L4. + + IPIP, SIT, GRE, UDP Tunnel, and Remote Checksum Offloads ======================================================== @@ -154,6 +172,23 @@ that the IPv4 ID field is incremented in the case that a given header does not have the DF bit set. +ESP Segmentation Offload +======================== + +ESP segmentation offload uses SKB_GSO_ESP to mark packets that require +IPsec ESP segmentation. This type is set by the XFRM output path for GSO +packets handled by ESP hardware offload. + + +Fraglist Generic Segmentation Offload +===================================== + +Fraglist GSO uses SKB_GSO_FRAGLIST to mark packets whose segments are +already arranged as a list of skbs. The segmentation path splits the skb +based on that list rather than by creating segments of skb_shinfo()->gso_size +bytes from the linear and page-fragment data. + + SCTP acceleration with GSO =========================== diff --git a/Documentation/networking/skbuff.rst b/Documentation/networking/skbuff.rst index 5b74275a73a3..94681523e345 100644 --- a/Documentation/networking/skbuff.rst +++ b/Documentation/networking/skbuff.rst @@ -29,9 +29,3 @@ dataref and headerless skbs .. kernel-doc:: include/linux/skbuff.h :doc: dataref and headerless skbs - -Checksum information --------------------- - -.. kernel-doc:: include/linux/skbuff.h - :doc: skb checksums diff --git a/Documentation/networking/smc-sysctl.rst b/Documentation/networking/smc-sysctl.rst index a8b4f357174e..93cc6244f6c8 100644 --- a/Documentation/networking/smc-sysctl.rst +++ b/Documentation/networking/smc-sysctl.rst @@ -86,7 +86,7 @@ smcr_max_send_wr - INTEGER Please be aware that all the buffers need to be allocated as a physically continuous array in which each element is a single buffer and has the size of SMC_WR_BUF_SIZE (48) bytes. If the allocation fails, we keep retrying - with half of the buffer count until it is ether successful or (unlikely) + with half of the buffer count until it is either successful or (unlikely) we dip below the old hard coded value which is 16 where we give up much like before having this control. @@ -100,14 +100,14 @@ smcr_max_recv_wr - INTEGER depending on the workload it can be a bottleneck in a sense that threads have to wait for work request buffers to become available. Before the introduction of this control the maximal number of work request buffers - available on the receive path used to be hard coded to 16. With this control + available on the receive path used to be hard coded to 48. With this control it becomes configurable. The acceptable range is between 2 and 2048. Please be aware that all the buffers need to be allocated as a physically continuous array in which each element is a single buffer and has the size of SMC_WR_BUF_SIZE (48) bytes. If the allocation fails, we keep retrying - with half of the buffer count until it is ether successful or (unlikely) - we dip below the old hard coded value which is 16 where we give up much + with half of the buffer count until it is either successful or (unlikely) + we dip below the old hard coded value which is 48 where we give up much like before having this control. Default: 48 diff --git a/Documentation/networking/statistics.rst b/Documentation/networking/statistics.rst index 66b0ef941457..824ebc549383 100644 --- a/Documentation/networking/statistics.rst +++ b/Documentation/networking/statistics.rst @@ -231,8 +231,19 @@ Kernel-internal data structures ------------------------------- The following structures are internal to the kernel, their members are -translated to netlink attributes when dumped. Drivers must not overwrite -the statistics they don't report with 0. +translated to netlink attributes when dumped. Fields are pre-initialized +to ``ETHTOOL_STAT_NOT_SET`` (by ``ethtool_stats_init()``); drivers must +leave fields they do not report at that value rather than overwriting +them with 0. -- ethtool_pause_stats() -- ethtool_fec_stats() +- ``struct ethtool_eth_ctrl_stats`` +- ``struct ethtool_eth_mac_stats`` +- ``struct ethtool_eth_phy_stats`` +- ``struct ethtool_fec_hist`` +- ``struct ethtool_fec_stats`` +- ``struct ethtool_link_ext_stats`` +- ``struct ethtool_mm_stats`` +- ``struct ethtool_pause_stats`` +- ``struct ethtool_phy_stats`` +- ``struct ethtool_rmon_stats`` +- ``struct ethtool_ts_stats`` diff --git a/Documentation/networking/strparser.rst b/Documentation/networking/strparser.rst index 8dc6bb04c710..372106b61e65 100644 --- a/Documentation/networking/strparser.rst +++ b/Documentation/networking/strparser.rst @@ -40,8 +40,8 @@ Functions :: - strp_init(struct strparser *strp, struct sock *sk, - const struct strp_callbacks *cb) + int strp_init(struct strparser *strp, struct sock *sk, + const struct strp_callbacks *cb) Called to initialize a stream parser. strp is a struct of type strparser that is allocated by the upper layer. sk is the TCP @@ -95,7 +95,7 @@ Functions void strp_data_ready(struct strparser *strp); - The upper layer calls strp_tcp_data_ready when data is ready on + The upper layer calls strp_data_ready when data is ready on the lower socket for strparser to process. This should be called from a data_ready callback that is set on the socket. Note that maximum messages size is the limit of the receive socket @@ -123,9 +123,9 @@ There are seven callbacks: should parse the sk_buff as containing the headers for the next application layer message in the stream. - The skb->cb in the input skb is a struct strp_msg. Only - the offset field is relevant in parse_msg and gives the offset - where the message starts in the skb. + The strparser metadata in the input skb can be accessed with + strp_msg(skb). Only the offset field is relevant in parse_msg and + gives the offset where the message starts in the skb. The return values of this function are: @@ -176,11 +176,11 @@ There are seven callbacks: received in rcv_msg (see strp_pause above). This callback must be set. - The skb->cb in the input skb is a struct strp_msg. This - struct contains two fields: offset and full_len. Offset is - where the message starts in the skb, and full_len is the - the length of the message. skb->len - offset may be greater - than full_len since strparser does not trim the skb. + The strparser metadata in the input skb can be accessed with + strp_msg(skb). This struct contains two fields: offset and full_len. + Offset is where the message starts in the skb, and full_len is + the length of the message. skb->len - offset may be greater than + full_len since strparser does not trim the skb. :: diff --git a/Documentation/networking/tcp_ao.rst b/Documentation/networking/tcp_ao.rst index d5b6d0df63c3..55304037aa81 100644 --- a/Documentation/networking/tcp_ao.rst +++ b/Documentation/networking/tcp_ao.rst @@ -7,9 +7,9 @@ TCP Authentication Option Linux implementation (RFC5925) TCP Authentication Option (TCP-AO) provides a TCP extension aimed at verifying segments between trusted peers. It adds a new TCP header option with a Message Authentication Code (MAC). MACs are produced from the content -of a TCP segment using a hashing function with a password known to both peers. +of a TCP segment using a key known to both peers. The intent of TCP-AO is to deprecate TCP-MD5 providing better security, -key rotation and support for a variety of hashing algorithms. +key rotation and support for a variety of MAC algorithms. 1. Introduction =============== @@ -19,16 +19,18 @@ key rotation and support for a variety of hashing algorithms. +----------------------+------------------------+-----------------------+ | | TCP-MD5 | TCP-AO | +======================+========================+=======================+ - |Supported hashing |MD5 |Must support HMAC-SHA1 | - |algorithms |(cryptographically weak)|(chosen-prefix attacks)| - | | |and CMAC-AES-128 (only | - | | |side-channel attacks). | - | | |May support any hashing| - | | |algorithm. | + |Supported MAC |MD5 of data and key |HMAC-SHA-1-96 and | + |algorithms |(cryptographically weak)|AES-128-CMAC-96. | + | | |Implementations are | + | | |permitted to support | + | | |additional algorithms. | +----------------------+------------------------+-----------------------+ - |Length of MACs (bytes)|16 |Typically 12-16. | - | | |Other variants that fit| - | | |TCP header permitted. | + |Length of MACs (bytes)|16 |12 for HMAC-SHA-1-96 | + | | |and AES-128-CMAC-96. | + | | |Implementations are | + | | |permitted to support | + | | |any MAC length that | + | | |fits in the TCP header.| +----------------------+------------------------+-----------------------+ |Number of keys per |1 |Many | |TCP connection | | | @@ -296,6 +298,20 @@ userspace manage TCP-AO on a per-socket basis. In order to add/delete MKTs It is not allowed to add a key on an established non-TCP-AO connection as well as to remove the last key from TCP-AO connection. +``TCP_AO_ADD_KEY`` allows the MAC algorithm and MAC length to be selected. +Linux supports the mandatory-to-implement algorithms HMAC-SHA-1-96 and +AES-128-CMAC-96. In addition, as Linux extensions, it supports: + +- HMAC-SHA256. Linux uses HMAC-SHA256 in the same way as HMAC-SHA1; this + includes omitting an explicit entropy extraction step. To work around the + missing entropy extraction, users should provide keys with full entropy. The + implementation is interoperable with other implementations of HMAC-SHA256 for + TCP-AO only when they have implemented the key derivation the same way (and + also the same MAC length is selected on each side). + +- Any MAC length for any of the supported MAC algorithms, provided it fits in + the TCP header and is at least 4 bytes. + ``setsockopt(TCP_AO_DEL_KEY)`` command may specify ``tcp_ao_del::current_key`` + ``tcp_ao_del::set_current`` and/or ``tcp_ao_del::rnext`` + ``tcp_ao_del::set_rnext`` which makes such delete "forced": it diff --git a/Documentation/networking/tls-offload.rst b/Documentation/networking/tls-offload.rst index c173f537bf4d..e5802bcd4d22 100644 --- a/Documentation/networking/tls-offload.rst +++ b/Documentation/networking/tls-offload.rst @@ -13,7 +13,7 @@ Layer Protocol (ULP) and install the cryptographic connection state. For details regarding the user-facing interface refer to the TLS documentation in :ref:`Documentation/networking/tls.rst `. -``ktls`` can operate in three modes: +``ktls`` can operate in two modes: * Software crypto mode (``TLS_SW``) - CPU handles the cryptography. In most basic cases only crypto operations synchronous with the CPU @@ -26,11 +26,6 @@ documentation in :ref:`Documentation/networking/tls.rst `. This mode integrates best with the kernel stack and is described in detail in the remaining part of this document (``ethtool`` flags ``tls-hw-tx-offload`` and ``tls-hw-rx-offload``). - * Full TCP NIC offload mode (``TLS_HW_RECORD``) - mode of operation where - NIC driver and firmware replace the kernel networking stack - with its own TCP handling, it is not usable in production environments - making use of the Linux networking stack for example any firewalling - abilities or QoS and packet scheduling (``ethtool`` flag ``tls-hw-record``). The operation mode is selected automatically based on device configuration, offload opt-in or opt-out on per-connection basis is not currently supported. @@ -104,6 +99,29 @@ at the end of kernel structures (see :c:member:`driver_state` members in ``include/net/tls.h``) to avoid additional allocations and pointer dereferences. +When the offloaded connection is destroyed the core calls +the :c:member:`tls_dev_del` callback so the driver can release per-direction +state: + +.. code-block:: c + + void (*tls_dev_del)(struct net_device *netdev, + struct tls_context *ctx, + enum tls_offload_ctx_dir direction); + +``tls_dev_del`` is mandatory whenever ``tls_dev_add`` is provided. + +The third TLS device callback is :c:member:`tls_dev_resync`, called by the core +to synchronize the TCP stream with the record boundaries: + +.. code-block:: c + + int (*tls_dev_resync)(struct net_device *netdev, + struct sock *sk, u32 seq, u8 *rcd_sn, + enum tls_offload_ctx_dir direction); + +See the `Resync handling`_ section for details. + TX -- @@ -255,9 +273,9 @@ Following helper should be used to test if resync is complete: bool tls_offload_tx_resync_pending(struct sock *sk) Next time ``ktls`` pushes a record it will first send its TCP sequence number -and TLS record number to the driver. Stack will also make sure that -the new record will start on a segment boundary (like it does when -the connection is initially added). +and TLS record number to the driver via the ``tls_dev_resync`` callback. +The stack will also make sure that the new record will start on a segment +boundary (like it does when the connection is initially added). RX -- @@ -349,9 +367,10 @@ all TLS record headers that have been logged since the resync request started. The kernel confirms the guessed location was correct and tells the device -the record sequence number. Meanwhile, the device had been parsing -and counting all records since the just-confirmed one, it adds the number -of records it had seen to the record number provided by the kernel. +the record sequence number via the ``tls_dev_resync`` callback. Meanwhile, +the device had been parsing and counting all records since the just-confirmed +one, it adds the number of records it had seen to the record number provided +by the kernel. At this point the device is in sync and can resume decryption at next segment boundary. @@ -375,12 +394,19 @@ schedules resynchronization after it has received two completely encrypted records. The stack waits for the socket to drain and informs the device about -the next expected record number and its TCP sequence number. If the +the next expected record number and its TCP sequence number via the +``tls_dev_resync`` callback. If the records continue to be received fully encrypted stack retries the synchronization with an exponential back off (first after 2 encrypted records, then after 4 records, after 8, after 16... up until every 128 records). +Rekey +===== + +Offload does not currently support TLS 1.3, therefore key rotation +is not a concern for offloaded connections at this point. + Error handling ============== diff --git a/Documentation/networking/xdp-rx-metadata.rst b/Documentation/networking/xdp-rx-metadata.rst index ce96f4c99505..efdf5eeb49e7 100644 --- a/Documentation/networking/xdp-rx-metadata.rst +++ b/Documentation/networking/xdp-rx-metadata.rst @@ -36,7 +36,7 @@ metadata available in which case the driver returns ``-ENODATA``. Not all kfuncs have to be implemented by the device driver; when not implemented, the default ones that return ``-EOPNOTSUPP`` will be used -to indicate the device driver have not implemented this kfunc. +to indicate the device driver has not implemented this kfunc. Within an XDP frame, the metadata layout (accessed via ``xdp_buff``) is diff --git a/Documentation/networking/xfrm/index.rst b/Documentation/networking/xfrm/index.rst index 7d866da836fe..90191848f8db 100644 --- a/Documentation/networking/xfrm/index.rst +++ b/Documentation/networking/xfrm/index.rst @@ -9,5 +9,6 @@ XFRM Framework xfrm_device xfrm_proc + xfrm_migrate_state xfrm_sync xfrm_sysctl diff --git a/Documentation/networking/xfrm/xfrm_migrate_state.rst b/Documentation/networking/xfrm/xfrm_migrate_state.rst new file mode 100644 index 000000000000..9d53cb22b007 --- /dev/null +++ b/Documentation/networking/xfrm/xfrm_migrate_state.rst @@ -0,0 +1,274 @@ +.. SPDX-License-Identifier: GPL-2.0 + +===================== +XFRM SA Migrate State +===================== + +Overview +======== + +``XFRM_MSG_MIGRATE_STATE`` migrates a single SA, looked up using SPI and +mark, without involving policies. Unlike ``XFRM_MSG_MIGRATE``, which couples +SA and policy migration and allows migrating multiple SAs in one call, this +interface identifies the SA unambiguously via SPI and supports changing +the reqid, addresses, encapsulation, selector, and offload. + +Because IKE daemons can manage policies independently of +the kernel, this interface allows precise per-SA migration without +requiring policy involvement. Optional netlink attributes follow an +omit-to-inherit model: omitting an attribute preserves the value from +the old SA. The ``flags`` field controls two exceptions: hardware offload +is inherited by default and can be suppressed with +``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` or overridden with ``XFRMA_OFFLOAD_DEV``; +the new selector is taken from ``new_sel`` by default and can instead be +derived from the new addresses with ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL``. + +SA Identification +================= + +The struct is defined in ``include/uapi/linux/xfrm.h``. The SA is looked +up using ``xfrm_state_lookup()`` with ``id.spi``, +``id.daddr``, ``id.proto``, ``id.family``, and +``old_mark.v & old_mark.m`` as the mark key:: + + struct xfrm_user_migrate_state { + struct xfrm_usersa_id id; /* spi, daddr, proto, family */ + xfrm_address_t new_daddr; + xfrm_address_t new_saddr; + struct xfrm_mark old_mark; /* SA lookup: key = v & m */ + struct xfrm_selector new_sel; /* new selector (see Flags) */ + __u32 new_reqid; + __u32 flags; /* XFRM_MIGRATE_STATE_* */ + __u16 new_family; + __u16 reserved; /* must be zero */ + }; + +The ``reserved`` field must be set to zero; the kernel rejects any +other value with ``-EINVAL``. + +Supported Attributes +==================== + +The following fields in ``xfrm_user_migrate_state`` are always explicit +and are not inherited from the existing SA. Passing zero is not equivalent +to "keep unchanged" — zero is used as-is: + +- ``new_daddr`` - new destination address +- ``new_saddr`` - new source address +- ``new_family`` - new address family +- ``new_reqid`` - new reqid (0 = no reqid) +- ``new_sel`` - new selector; used when ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` is + not set (see `Flags`_ below) +- ``flags`` - bitmask of ``XFRM_MIGRATE_STATE_*`` flags (see `Flags`_ below) + +The following netlink attributes are also accepted. Omitting an attribute +inherits the value from the existing SA (omit-to-inherit). + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Attribute + - Description + * - ``XFRMA_MARK`` + - Mark on the migrated SA (``struct xfrm_mark``). Absent inherits + ``old_mark``. To use no mark on the new SA, send ``XFRMA_MARK`` + with ``{0, 0}``. + * - ``XFRMA_ENCAP`` + - UDP encapsulation template; only ``UDP_ENCAP_ESPINUDP`` is supported. + Set ``encap_type=0`` to remove encap. + * - ``XFRMA_OFFLOAD_DEV`` + - Hardware offload configuration (``struct xfrm_user_offload``). Absent + copies offload from the existing SA. When + ``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` is set in ``flags``, the new SA has + no offload; this flag is mutually exclusive with ``XFRMA_OFFLOAD_DEV`` + and sending both returns ``-EINVAL``. + * - ``XFRMA_SET_MARK`` + - Output mark on the migrated SA; pair with ``XFRMA_SET_MARK_MASK``. + Send 0 to clear. + * - ``XFRMA_NAT_KEEPALIVE_INTERVAL`` + - NAT keepalive interval in seconds. Requires encap. Send 0 to clear. + Automatically cleared when encap is removed; setting a non-zero + value without encap returns ``-EINVAL``. + * - ``XFRMA_MTIMER_THRESH`` + - Mapping maxage threshold. Only valid on input SAs; setting on an + output SA returns ``-EINVAL``. Requires encap. Send 0 to clear. + Automatically cleared when encap is removed; setting a non-zero + value without encap returns ``-EINVAL``. + +The following SA properties are immutable and cannot be changed via +``XFRM_MSG_MIGRATE_STATE``: algorithms (``XFRMA_ALG_*``), replay state, +direction (``XFRMA_SA_DIR``), and security context (``XFRMA_SEC_CTX``). + +Flags +===== + +The ``flags`` field in ``xfrm_user_migrate_state`` controls optional +migration behaviour. Unknown flag bits are rejected with ``-EINVAL``; the +extended ACK message identifies the unrecognised bits (e.g. ``"Unknown flags: +0x4"``). Userspace can use ``XFRM_MIGRATE_STATE_KNOWN_FLAGS`` (defined in +````) to validate flags before sending; note that this constant +reflects the flags known to the header version userspace was compiled against, +which may differ from what the running kernel accepts. + +.. list-table:: + :widths: 40 60 + :header-rows: 1 + + * - Flag + - Description + * - ``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` + - When set, the new SA has no hardware offload even when + ``XFRMA_OFFLOAD_DEV`` is absent. Without this flag, omitting + ``XFRMA_OFFLOAD_DEV`` copies the existing offload to the new SA. + Mutually exclusive with ``XFRMA_OFFLOAD_DEV``; sending both + returns ``-EINVAL``. + * - ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` + - When set, the kernel validates that the existing SA selector is a + single-host entry matching the SA addresses (``prefixlen_s == + prefixlen_d`` equal to 32 for IPv4 or 128 for IPv6, and addresses + matching ``id.daddr`` and ``props.saddr``). If the check passes, + the new selector is derived from ``new_daddr`` and ``new_saddr`` + with the single-host mask for ``new_family``. A mismatch returns + ``-EINVAL``. When this flag is not set, ``new_sel`` is used as-is + for the migrated SA. + +Migration Steps +=============== + +Outgoing SA +----------- + +To prevent cleartext traffic leaks, install a block policy before +migrating: + +#. Install a block policy to drop traffic on the affected selector. +#. Remove the old policy. +#. Call ``XFRM_MSG_MIGRATE_STATE`` for each SA. +#. Reinstall the policies. +#. Remove the block policy. + +If AES-GCM is in use, the block policy also prevents IV reuse during +the migration window. For other AEADs this step is not required for +IV safety, but skipping it allows a brief cleartext window. + +Incoming SA +----------- + +No block policy is needed. ``XFRM_MSG_MIGRATE_STATE`` atomically +transfers the sequence number and replay window from the old SA to +the new SA, so the new SA continues replay protection without a gap. +Call ``XFRM_MSG_MIGRATE_STATE`` for each SA directly. + +When accepting incoming traffic, be liberal during the migration +window: packets sent by the remote peer before it completed its own +migration may arrive out of order or slightly late. Dropping them +unnecessarily causes packet loss. A generous replay window reduces +the impact of reordering during migration. + +Block Policy and IV Safety +-------------------------- + +AES-GCM IV uniqueness is critical: reusing a (key, IV) pair allows +an attacker to recover the authentication subkey and forge +authentication tags, breaking both confidentiality and integrity. +This concern applies to outgoing SAs only — the remote peer controls +IV generation on incoming traffic. + +``XFRM_MSG_MIGRATE_STATE`` atomically copies the sequence number and +replay window from the old SA to the new SA and deletes the old SA. +The block policy serves two purposes: it prevents cleartext traffic +leaks during the migration window, and for AES-GCM it prevents IV +reuse by ensuring no outgoing packets are sent under the same key. +The atomic copy of the sequence number and replay window complements +this — together they eliminate both risks during migration. +The atomic copy also serves incoming SAs, ensuring replay protection +continues without a gap across the migration. + +Feature Detection +================= + +Userspace can probe for kernel support by sending a minimal +``XFRM_MSG_MIGRATE_STATE`` message with a non-zero non-existent SPI: + +- ``-EINVAL``: kernel predates ``XFRM_MSG_MIGRATE_STATE``; message type + is out of range +- ``-ENOPROTOOPT``: message type is known but ``CONFIG_XFRM_MIGRATE`` + is not enabled +- ``-ESRCH``: supported (SPI not found) + +Userspace Notification on Success +================================= + +On successful migration the kernel multicasts an +``XFRM_MSG_MIGRATE_STATE`` message to the ``XFRMNLGRP_MIGRATE`` group. +The fixed header is ``struct xfrm_user_migrate_state`` copied from the +request, followed by the same set of netlink attributes that are +accepted as input, with the differences noted below. + +Differences from the request +----------------------------- + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - Field / Attribute + - Difference + * - ``new_sel`` + - Contains the actual selector of the newly installed SA, not the + ``new_sel`` from the request. When + ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` is set the kernel derives the + selector from ``new_daddr`` / ``new_saddr``; the caller's + ``new_sel`` field is ignored in that case. The notification + always carries the real selector of the new SA. + * - ``XFRMA_SA_DIR`` + - Present in the notification (set from the direction of the new + SA) but **not accepted as input** — direction is immutable. + * - ``flags`` + - Echoed back as-is. ``XFRM_MIGRATE_STATE_CLEAR_OFFLOAD`` and + ``XFRM_MIGRATE_STATE_UPDATE_H2H_SEL`` describe the request that was + made, not a property of the resulting SA. + +Attributes in the notification +------------------------------- + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Attribute + - Description + * - ``XFRMA_ENCAP`` + - UDP encapsulation template, if configured on the new SA. + * - ``XFRMA_OFFLOAD_DEV`` + - Hardware offload configuration, if active on the new SA. + * - ``XFRMA_MARK`` + - Mark on the new SA, if set. + * - ``XFRMA_SET_MARK`` + - Output mark on the new SA, if set. + * - ``XFRMA_SET_MARK_MASK`` + - Output mark mask, present together with ``XFRMA_SET_MARK``. + * - ``XFRMA_MTIMER_THRESH`` + - Mapping maxage threshold, if non-zero. + * - ``XFRMA_NAT_KEEPALIVE_INTERVAL`` + - NAT keepalive interval, if non-zero. + * - ``XFRMA_SA_DIR`` + - Direction of the new SA. + +Error Handling +============== + +If the target SA tuple (new daddr, SPI, proto, new family) is already +occupied, the operation returns ``-EEXIST`` before the migration begins. +The old SA remains intact and the operation is safe to retry after +resolving the conflict. + +If the target SA is deleted before the migration completes, the operation +returns ``-ESRCH``. No new SA is installed. Userspace should verify the +current SA state before retrying. + +If the multicast notification (``XFRMNLGRP_MIGRATE``) fails to send, +the migration itself has already completed successfully and the new SA +is installed. The operation returns success, 0, with an extack warning, +but listeners will not receive the migration event. diff --git a/Documentation/networking/xsk-tx-metadata.rst b/Documentation/networking/xsk-tx-metadata.rst index df53a10ccac3..f240930ca1df 100644 --- a/Documentation/networking/xsk-tx-metadata.rst +++ b/Documentation/networking/xsk-tx-metadata.rst @@ -14,9 +14,9 @@ General Design The headroom for the metadata is reserved via ``tx_metadata_len`` and ``XDP_UMEM_TX_METADATA_LEN`` flag in ``struct xdp_umem_reg``. The metadata length is therefore the same for every socket that shares the same umem. -The metadata layout is a fixed UAPI, refer to ``union xsk_tx_metadata`` in +The metadata layout is a fixed UAPI, refer to ``struct xsk_tx_metadata`` in ``include/uapi/linux/if_xdp.h``. Thus, generally, the ``tx_metadata_len`` -field above should contain ``sizeof(union xsk_tx_metadata)``. +field above should contain ``sizeof(struct xsk_tx_metadata)``. Note that in the original implementation the ``XDP_UMEM_TX_METADATA_LEN`` flag was not required. Applications might attempt to create a umem @@ -45,15 +45,16 @@ the metadata area is ignored by the kernel as well. The flags field enables the particular offload: - ``XDP_TXMD_FLAGS_TIMESTAMP``: requests the device to put transmission - timestamp into ``tx_timestamp`` field of ``union xsk_tx_metadata``. + timestamp into ``completion.tx_timestamp`` field of + ``struct xsk_tx_metadata``. - ``XDP_TXMD_FLAGS_CHECKSUM``: requests the device to calculate L4 - checksum. ``csum_start`` specifies byte offset of where the checksumming - should start and ``csum_offset`` specifies byte offset where the - device should store the computed checksum. + checksum. ``request.csum_start`` specifies byte offset of where the + checksumming should start and ``request.csum_offset`` specifies byte offset + where the device should store the computed checksum. - ``XDP_TXMD_FLAGS_LAUNCH_TIME``: requests the device to schedule the packet for transmission at a pre-determined time called launch time. The - value of launch time is indicated by ``launch_time`` field of - ``union xsk_tx_metadata``. + value of launch time is indicated by ``request.launch_time`` field of + ``struct xsk_tx_metadata``. Besides the flags above, in order to trigger the offloads, the first packet's ``struct xdp_desc`` descriptor should set ``XDP_TX_METADATA`` @@ -63,9 +64,9 @@ only the first chunk should carry the metadata. Software TX Checksum ==================== -For development and testing purposes its possible to pass +For development and testing purposes it's possible to pass ``XDP_UMEM_TX_SW_CSUM`` flag to ``XDP_UMEM_REG`` UMEM registration call. -In this case, when running in ``XDK_COPY`` mode, the TX checksum +In this case, when running in ``XDP_COPY`` mode, the TX checksum is calculated on the CPU. Do not enable this option in production because it will negatively affect performance. @@ -118,7 +119,7 @@ schedule with a 1-second cycle time, with all Tx Queues open at all times. The value of the launch time that is programmed in the Advanced Transmit Context Descriptor is a relative offset to the starting time of the Qbv -transmission window of the queue. The Frst flag of the descriptor can be +transmission window of the queue. The First flag of the descriptor can be set to schedule the packet for the next Qbv cycle. Therefore, the horizon of the launch time for i225 and i226 is the ending time of the next cycle of the Qbv transmission window of the queue. For example, when the Qbv @@ -129,9 +130,10 @@ running. Querying Device Capabilities ============================ -Every devices exports its offloads capabilities via netlink netdev family. -Refer to ``xsk-flags`` features bitmask in -``Documentation/netlink/specs/netdev.yaml``. +Every device exports its offload capabilities via the Netlink netdev family. +Query the ``xsk-features`` attribute in +``Documentation/netlink/specs/netdev.yaml``. Its bits are defined by the +``xsk-flags`` enum. - ``tx-timestamp``: device supports ``XDP_TXMD_FLAGS_TIMESTAMP`` - ``tx-checksum``: device supports ``XDP_TXMD_FLAGS_CHECKSUM`` diff --git a/Documentation/translations/zh_CN/networking/netmem.rst b/Documentation/translations/zh_CN/networking/netmem.rst index fe351a240f02..320f3eacf51b 100644 --- a/Documentation/translations/zh_CN/networking/netmem.rst +++ b/Documentation/translations/zh_CN/networking/netmem.rst @@ -89,4 +89,9 @@ dma-mapping API 去处理。 使用某个还不存在的 netmem API,你可以自行添加并提交到 netdev@,也可以联系维护 人员或者发送邮件至 almasrymina@google.com 寻求帮助。 -2. 驱动程序应通过设置 netdev->netmem_tx = true 来表明自身支持 netmem 功能。 +2. 驱动程序应将 `netdev->netmem_tx` 设置为适当的模式: + + - `NETMEM_TX_DMA`:适用于执行 DMA 的物理设备。 + + - `NETMEM_TX_NO_DMA`:适用于不执行 DMA 的虚拟或透传设备,但仍支持 + 处理 netmem 支持的 skb。 diff --git a/Documentation/userspace-api/netlink/intro.rst b/Documentation/userspace-api/netlink/intro.rst index aacffade8f84..ca60abe94e3d 100644 --- a/Documentation/userspace-api/netlink/intro.rst +++ b/Documentation/userspace-api/netlink/intro.rst @@ -526,8 +526,8 @@ of the recvmsg() system call, *not* a Netlink header). Upon truncation the remaining part of the message is discarded. -Netlink expects that the user buffer will be at least 8kB or a page -size of the CPU architecture, whichever is bigger. Particular Netlink +Netlink expects that the user buffer will be at most 8kB or a page +size of the CPU architecture, whichever is smaller. Particular Netlink families may, however, require a larger buffer. 32kB buffer is recommended for most efficient handling of dumps (larger buffer fits more dumped objects and therefore fewer recvmsg() calls are needed). diff --git a/MAINTAINERS b/MAINTAINERS index 3d6db8cb608f..12bd898fcc4b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -809,6 +809,14 @@ S: Maintained F: Documentation/i2c/busses/i2c-ali1563.rst F: drivers/i2c/busses/i2c-ali1563.c +ALIBABA ELASTIC ETHERNET ADAPTER DRIVER +M: Xuan Zhuo +M: Wen Gu +R: Philo Lu +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/alibaba/eea + ALIBABA ELASTIC RDMA DRIVER M: Cheng Xu M: Kai Shen @@ -1966,10 +1974,9 @@ F: drivers/hwmon/applesmc.c APPLETALK NETWORK LAYER L: netdev@vger.kernel.org -S: Odd fixes -F: include/linux/atalk.h +S: Obsolete F: include/uapi/linux/atalk.h -F: net/appletalk/ +F: include/uapi/linux/if_ltalk.h APPLIED MICRO (APM) X-GENE DEVICE TREE SUPPORT M: Khuong Dinh @@ -4455,7 +4462,7 @@ W: https://www.open-mesh.org/ Q: https://patchwork.open-mesh.org/project/batman/list/ B: https://www.open-mesh.org/projects/batman-adv/issues C: ircs://irc.hackint.org/batadv -T: git https://git.open-mesh.org/linux-merge.git +T: git https://git.open-mesh.org/batadv.git F: Documentation/networking/batman-adv.rst F: include/uapi/linux/batadv_packet.h F: include/uapi/linux/batman_adv.h @@ -15379,7 +15386,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git F: Documentation/networking/mac80211-injection.rst F: Documentation/networking/mac80211_hwsim/mac80211_hwsim.rst -F: drivers/net/wireless/virtual/mac80211_hwsim.[ch] +F: drivers/net/wireless/virtual/mac80211_hwsim*.[ch] F: include/net/mac80211.h F: net/mac80211/ @@ -18546,7 +18553,6 @@ F: include/linux/fddidevice.h F: include/linux/if_* F: include/linux/inetdevice.h F: include/linux/netdev* -F: include/linux/platform_data/wiznet.h F: include/uapi/linux/cn_proc.h F: include/uapi/linux/ethtool_netlink* F: include/uapi/linux/if_* @@ -18694,6 +18700,7 @@ F: net/ F: samples/pktgen/ F: tools/net/ F: tools/testing/selftests/net/ +X: Documentation/networking/batman-adv.rst X: Documentation/networking/mac80211-injection.rst X: Documentation/networking/mac80211_hwsim/ X: Documentation/networking/regulatory.rst @@ -18704,6 +18711,7 @@ X: include/net/iw_handler.h X: include/net/mac80211.h X: include/net/wext.h X: net/9p/ +X: net/batman-adv/ X: net/bluetooth/ X: net/can/ X: net/ceph/ @@ -18936,7 +18944,7 @@ F: drivers/net/net_failover.c F: include/net/net_failover.h NFC SUBSYSTEM -M: David Heidelberg +M: David Heidelberg L: oe-linux-nfc@lists.linux.dev S: Maintained T: git https://codeberg.org/linux-nfc/linux.git @@ -19361,6 +19369,17 @@ F: Documentation/devicetree/bindings/clock/*imx* F: drivers/clk/imx/ F: include/dt-bindings/clock/*imx* +NXP NETC ETHERNET SWITCH DRIVER +M: Wei Fang +R: Clark Wang +L: imx@lists.linux.dev +L: netdev@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml +F: drivers/net/dsa/netc/ +F: include/linux/dsa/tag_netc.h +F: net/dsa/tag_netc.c + NXP NETC TIMER PTP CLOCK DRIVER M: Wei Fang M: Clark Wang @@ -20197,6 +20216,7 @@ M: Christian Lamparter L: linux-wireless@vger.kernel.org S: Maintained W: https://wireless.wiki.kernel.org/en/users/Drivers/p54 +F: Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml F: drivers/net/wireless/intersil/ PACKET SOCKETS diff --git a/arch/arm/boot/dts/ti/omap/omap2.dtsi b/arch/arm/boot/dts/ti/omap/omap2.dtsi index afabb36a8ac1..fdc1790adf43 100644 --- a/arch/arm/boot/dts/ti/omap/omap2.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap2.dtsi @@ -129,6 +129,8 @@ i2c2: i2c@48072000 { }; mcspi1: spi@48098000 { + #address-cells = <1>; + #size-cells = <0>; compatible = "ti,omap2-mcspi"; ti,hwmods = "mcspi1"; reg = <0x48098000 0x100>; @@ -140,6 +142,8 @@ mcspi1: spi@48098000 { }; mcspi2: spi@4809a000 { + #address-cells = <1>; + #size-cells = <0>; compatible = "ti,omap2-mcspi"; ti,hwmods = "mcspi2"; reg = <0x4809a000 0x100>; diff --git a/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi b/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi index 63b0b4921e4e..fe9dd8bbfc85 100644 --- a/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi @@ -109,3 +109,15 @@ partition@5 { }; }; }; + +&mcspi2 { + status = "okay"; + + wifi@0 { + reg = <0>; + compatible = "st,stlc4560"; + spi-max-frequency = <48000000>; + interrupts-extended = <&gpio3 23 IRQ_TYPE_EDGE_RISING>; + powerdown-gpios = <&gpio4 1 GPIO_ACTIVE_LOW>; /* gpio 97 */ + }; +}; diff --git a/arch/arm/configs/am200epdkit_defconfig b/arch/arm/configs/am200epdkit_defconfig index 2367b1685c1c..d8198592fe1b 100644 --- a/arch/arm/configs/am200epdkit_defconfig +++ b/arch/arm/configs/am200epdkit_defconfig @@ -86,7 +86,6 @@ CONFIG_DEBUG_KERNEL=y CONFIG_CRYPTO=y CONFIG_CRYPTO_CBC=m CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_ARC4=m # CONFIG_DEBUG_BUGVERBOSE is not set # CONFIG_DEBUG_PREEMPT is not set diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig index e98c35df675e..9743b0b7ec61 100644 --- a/arch/arm/configs/dove_defconfig +++ b/arch/arm/configs/dove_defconfig @@ -118,7 +118,6 @@ CONFIG_CRYPTO_BLOWFISH=y CONFIG_CRYPTO_TEA=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_SHA1=y diff --git a/arch/arm/configs/ixp4xx_defconfig b/arch/arm/configs/ixp4xx_defconfig index 01d72580bcc5..418ef909572b 100644 --- a/arch/arm/configs/ixp4xx_defconfig +++ b/arch/arm/configs/ixp4xx_defconfig @@ -55,7 +55,6 @@ CONFIG_ATM=y CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig index 59b020e66a0b..95afc972047e 100644 --- a/arch/arm/configs/multi_v5_defconfig +++ b/arch/arm/configs/multi_v5_defconfig @@ -286,7 +286,6 @@ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=y CONFIG_NLS_UTF8=y CONFIG_CRYPTO_CBC=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_DEV_MARVELL_CESA=y CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y diff --git a/arch/arm/configs/mv78xx0_defconfig b/arch/arm/configs/mv78xx0_defconfig index d3a26efe766c..a652ccd1358b 100644 --- a/arch/arm/configs/mv78xx0_defconfig +++ b/arch/arm/configs/mv78xx0_defconfig @@ -111,7 +111,6 @@ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=y CONFIG_CRYPTO_CBC=m CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_MAGIC_SYSRQ=y diff --git a/arch/arm/configs/mvebu_v5_defconfig b/arch/arm/configs/mvebu_v5_defconfig index d1742a7cae6a..4cf77df183b3 100644 --- a/arch/arm/configs/mvebu_v5_defconfig +++ b/arch/arm/configs/mvebu_v5_defconfig @@ -185,7 +185,6 @@ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=y CONFIG_NLS_UTF8=y CONFIG_CRYPTO_CBC=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_DEV_MARVELL_CESA=y CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig index c6155f101fc9..7bf58e8a5ab5 100644 --- a/arch/arm/configs/omap1_defconfig +++ b/arch/arm/configs/omap1_defconfig @@ -215,7 +215,6 @@ CONFIG_NLS_UTF8=y CONFIG_DEBUG_KERNEL=y CONFIG_SECURITY=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_LZO=y CONFIG_FONTS=y diff --git a/arch/arm/configs/orion5x_defconfig b/arch/arm/configs/orion5x_defconfig index 002c9145026b..f5be2e26d9ae 100644 --- a/arch/arm/configs/orion5x_defconfig +++ b/arch/arm/configs/orion5x_defconfig @@ -134,7 +134,6 @@ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=y CONFIG_CRYPTO_CBC=m CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_DEV_MARVELL_CESA=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index c51ae373ca88..b86bb9bfbc99 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -77,9 +77,6 @@ CONFIG_BT_HCIUART_BCSP=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m CONFIG_BT_HCIBTUART=m CONFIG_BT_HCIVHCI=m CONFIG_BT_MRVL=m @@ -640,14 +637,12 @@ CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig index f116a01c3f5f..8f8a058294fb 100644 --- a/arch/arm/configs/spitz_defconfig +++ b/arch/arm/configs/spitz_defconfig @@ -62,9 +62,6 @@ CONFIG_BT_HCIUART_BCSP=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m CONFIG_BT_HCIBTUART=m CONFIG_BT_HCIVHCI=m CONFIG_PCCARD=y diff --git a/arch/arm/configs/wpcm450_defconfig b/arch/arm/configs/wpcm450_defconfig index cd4b3e70ff68..67b64a378166 100644 --- a/arch/arm/configs/wpcm450_defconfig +++ b/arch/arm/configs/wpcm450_defconfig @@ -181,7 +181,6 @@ CONFIG_FORTIFY_SOURCE=y CONFIG_CRYPTO_RSA=y CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_CBC=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_CMAC=y diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index 969265d5d5c6..d9acd32c5457 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -106,21 +105,6 @@ static void __init n8x0_usb_init(void) {} #endif /*CONFIG_USB_MUSB_TUSB6010 */ - -static struct omap2_mcspi_device_config p54spi_mcspi_config = { - .turbo_mode = 0, -}; - -static struct spi_board_info n800_spi_board_info[] __initdata = { - { - .modalias = "p54spi", - .bus_num = 2, - .chip_select = 0, - .max_speed_hz = 48000000, - .controller_data = &p54spi_mcspi_config, - }, -}; - #if defined(CONFIG_MENELAUS) && IS_ENABLED(CONFIG_MMC_OMAP) /* @@ -524,7 +508,5 @@ omap_late_initcall(n8x0_late_initcall); void * __init n8x0_legacy_init(void) { board_check_revision(); - spi_register_board_info(n800_spi_board_info, - ARRAY_SIZE(n800_spi_board_info)); return &mmc1_data; } diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig index 82897236863f..7c8f01513ed2 100644 --- a/arch/loongarch/configs/loongson32_defconfig +++ b/arch/loongarch/configs/loongson32_defconfig @@ -383,9 +383,6 @@ CONFIG_BT_HCIUART_AG6XX=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m CONFIG_BT_HCIVHCI=m CONFIG_BT_MRVL=m CONFIG_BT_ATH3K=m diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig index a94e88bd7ec5..8e3906d3bd70 100644 --- a/arch/loongarch/configs/loongson64_defconfig +++ b/arch/loongarch/configs/loongson64_defconfig @@ -399,9 +399,6 @@ CONFIG_BT_HCIUART_AG6XX=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m CONFIG_BT_HCIVHCI=m CONFIG_BT_MRVL=m CONFIG_BT_ATH3K=m diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig index 1a4f0739c6bb..5ed08e0fdf88 100644 --- a/arch/m68k/configs/amiga_defconfig +++ b/arch/m68k/configs/amiga_defconfig @@ -236,7 +236,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -521,7 +520,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -533,7 +531,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig index dacce863e37f..4690fcd5b2f5 100644 --- a/arch/m68k/configs/apollo_defconfig +++ b/arch/m68k/configs/apollo_defconfig @@ -232,7 +232,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -476,7 +475,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -488,7 +486,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig index ee0349fc3d9e..5b3f9db297c8 100644 --- a/arch/m68k/configs/atari_defconfig +++ b/arch/m68k/configs/atari_defconfig @@ -239,7 +239,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -499,7 +498,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -511,7 +509,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig index f1d0acf2afba..65370825c726 100644 --- a/arch/m68k/configs/bvme6000_defconfig +++ b/arch/m68k/configs/bvme6000_defconfig @@ -229,7 +229,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -468,7 +467,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -480,7 +478,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig index d808468f803b..31a58e0b8654 100644 --- a/arch/m68k/configs/hp300_defconfig +++ b/arch/m68k/configs/hp300_defconfig @@ -231,7 +231,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -478,7 +477,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -490,7 +488,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig index e594b644c0a6..22b815d0909e 100644 --- a/arch/m68k/configs/mac_defconfig +++ b/arch/m68k/configs/mac_defconfig @@ -230,7 +230,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -497,7 +496,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -509,7 +507,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig index 0c1af1894605..615e2dcad3d3 100644 --- a/arch/m68k/configs/multi_defconfig +++ b/arch/m68k/configs/multi_defconfig @@ -250,7 +250,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -584,7 +583,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -596,7 +594,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig index a17e771b4760..990149141820 100644 --- a/arch/m68k/configs/mvme147_defconfig +++ b/arch/m68k/configs/mvme147_defconfig @@ -228,7 +228,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -468,7 +467,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -480,7 +478,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig index 34e48a518ccc..12686bf3ff73 100644 --- a/arch/m68k/configs/mvme16x_defconfig +++ b/arch/m68k/configs/mvme16x_defconfig @@ -229,7 +229,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -469,7 +468,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -481,7 +479,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig index 8294c1533bda..25a971b9819d 100644 --- a/arch/m68k/configs/q40_defconfig +++ b/arch/m68k/configs/q40_defconfig @@ -230,7 +230,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -487,7 +486,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -499,7 +497,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig index 53fa21deb7bd..e070628b006e 100644 --- a/arch/m68k/configs/sun3_defconfig +++ b/arch/m68k/configs/sun3_defconfig @@ -225,7 +225,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -466,7 +465,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -478,7 +476,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig index 6c58d82276e8..9fc2943dc4ce 100644 --- a/arch/m68k/configs/sun3x_defconfig +++ b/arch/m68k/configs/sun3x_defconfig @@ -226,7 +226,6 @@ CONFIG_RDS=m CONFIG_RDS_TCP=m CONFIG_L2TP=m CONFIG_BRIDGE=m -CONFIG_ATALK=m CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m # CONFIG_BATMAN_ADV_BATMAN_V is not set @@ -466,7 +465,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -478,7 +476,6 @@ CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CMAC=m diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig index aa63ada62e28..611dc0dd392d 100644 --- a/arch/mips/configs/bigsur_defconfig +++ b/arch/mips/configs/bigsur_defconfig @@ -207,7 +207,6 @@ CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m @@ -219,7 +218,6 @@ CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/mips/configs/decstation_64_defconfig b/arch/mips/configs/decstation_64_defconfig index 509515cf4751..8afb8ac3ed78 100644 --- a/arch/mips/configs/decstation_64_defconfig +++ b/arch/mips/configs/decstation_64_defconfig @@ -174,7 +174,6 @@ CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_OFB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m @@ -189,7 +188,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/mips/configs/decstation_defconfig b/arch/mips/configs/decstation_defconfig index 3a17c080292f..29915a002db1 100644 --- a/arch/mips/configs/decstation_defconfig +++ b/arch/mips/configs/decstation_defconfig @@ -169,7 +169,6 @@ CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_OFB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m @@ -184,7 +183,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/mips/configs/decstation_r4k_defconfig b/arch/mips/configs/decstation_r4k_defconfig index 7f5823663702..7eef7a1bdc30 100644 --- a/arch/mips/configs/decstation_r4k_defconfig +++ b/arch/mips/configs/decstation_r4k_defconfig @@ -169,7 +169,6 @@ CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_OFB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m @@ -184,7 +183,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/mips/configs/fuloong2e_defconfig b/arch/mips/configs/fuloong2e_defconfig index b6fe3c962464..405799a9ed2a 100644 --- a/arch/mips/configs/fuloong2e_defconfig +++ b/arch/mips/configs/fuloong2e_defconfig @@ -209,7 +209,6 @@ CONFIG_NLS_UTF8=y CONFIG_CRYPTO_AUTHENC=m CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_CTS=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_RMD160=m diff --git a/arch/mips/configs/gpr_defconfig b/arch/mips/configs/gpr_defconfig index ed1a8f80f96e..7834569a8314 100644 --- a/arch/mips/configs/gpr_defconfig +++ b/arch/mips/configs/gpr_defconfig @@ -91,7 +91,6 @@ CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_LLC2=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -249,7 +248,6 @@ CONFIG_NLS_CODEPAGE_850=y CONFIG_NLS_ISO8859_1=y CONFIG_CRYPTO_AUTHENC=m CONFIG_CRYPTO_BENCHMARK=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index e123848f94ab..822cc1ed64c2 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -306,7 +306,6 @@ CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m @@ -317,7 +316,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig index fea0ccee6948..d108fd7b752b 100644 --- a/arch/mips/configs/ip27_defconfig +++ b/arch/mips/configs/ip27_defconfig @@ -296,7 +296,6 @@ CONFIG_SECURITYFS=y CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m @@ -309,7 +308,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/mips/configs/ip30_defconfig b/arch/mips/configs/ip30_defconfig index 718f3060d9fa..028286029877 100644 --- a/arch/mips/configs/ip30_defconfig +++ b/arch/mips/configs/ip30_defconfig @@ -166,12 +166,10 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD4=m diff --git a/arch/mips/configs/ip32_defconfig b/arch/mips/configs/ip32_defconfig index 9020c309dcda..5ddbaa0aafaf 100644 --- a/arch/mips/configs/ip32_defconfig +++ b/arch/mips/configs/ip32_defconfig @@ -154,7 +154,6 @@ CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y @@ -169,7 +168,6 @@ CONFIG_CRYPTO_CAMELLIA=y CONFIG_CRYPTO_CAST5=y CONFIG_CRYPTO_CAST6=y CONFIG_CRYPTO_DES=y -CONFIG_CRYPTO_FCRYPT=y CONFIG_CRYPTO_KHAZAD=y CONFIG_CRYPTO_SERPENT=y CONFIG_CRYPTO_TEA=y diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig index b9f3e1641105..326f30748030 100644 --- a/arch/mips/configs/lemote2f_defconfig +++ b/arch/mips/configs/lemote2f_defconfig @@ -301,11 +301,9 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig index 81704ec67f09..89d7c47ca7f0 100644 --- a/arch/mips/configs/malta_defconfig +++ b/arch/mips/configs/malta_defconfig @@ -170,7 +170,6 @@ CONFIG_IP_SCTP=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -388,7 +387,6 @@ CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_MD4=m @@ -399,7 +397,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TEA=m diff --git a/arch/mips/configs/malta_kvm_defconfig b/arch/mips/configs/malta_kvm_defconfig index 82a97f58bce1..39c69af12adc 100644 --- a/arch/mips/configs/malta_kvm_defconfig +++ b/arch/mips/configs/malta_kvm_defconfig @@ -174,7 +174,6 @@ CONFIG_IP_SCTP=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -395,7 +394,6 @@ CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_MD4=m @@ -406,7 +404,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TEA=m diff --git a/arch/mips/configs/malta_qemu_32r6_defconfig b/arch/mips/configs/malta_qemu_32r6_defconfig index 5687e10c1bc8..5feb1dc43bae 100644 --- a/arch/mips/configs/malta_qemu_32r6_defconfig +++ b/arch/mips/configs/malta_qemu_32r6_defconfig @@ -46,7 +46,6 @@ CONFIG_INET6_IPCOMP=m CONFIG_IPV6_TUNNEL=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -163,7 +162,6 @@ CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/mips/configs/maltaaprp_defconfig b/arch/mips/configs/maltaaprp_defconfig index abd22bff517b..a69fb992b82a 100644 --- a/arch/mips/configs/maltaaprp_defconfig +++ b/arch/mips/configs/maltaaprp_defconfig @@ -48,7 +48,6 @@ CONFIG_INET6_IPCOMP=m CONFIG_IPV6_TUNNEL=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -164,7 +163,6 @@ CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/mips/configs/maltasmvp_defconfig b/arch/mips/configs/maltasmvp_defconfig index 3fb3def8112d..689edb56c055 100644 --- a/arch/mips/configs/maltasmvp_defconfig +++ b/arch/mips/configs/maltasmvp_defconfig @@ -49,7 +49,6 @@ CONFIG_INET6_IPCOMP=m CONFIG_IPV6_TUNNEL=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -165,7 +164,6 @@ CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/mips/configs/maltasmvp_eva_defconfig b/arch/mips/configs/maltasmvp_eva_defconfig index 92e026912f68..a56db606b2d8 100644 --- a/arch/mips/configs/maltasmvp_eva_defconfig +++ b/arch/mips/configs/maltasmvp_eva_defconfig @@ -50,7 +50,6 @@ CONFIG_INET6_IPCOMP=m CONFIG_IPV6_TUNNEL=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -167,7 +166,6 @@ CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/mips/configs/maltaup_defconfig b/arch/mips/configs/maltaup_defconfig index 7a675fd3321f..ee10762ab593 100644 --- a/arch/mips/configs/maltaup_defconfig +++ b/arch/mips/configs/maltaup_defconfig @@ -47,7 +47,6 @@ CONFIG_INET6_IPCOMP=m CONFIG_IPV6_TUNNEL=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -163,7 +162,6 @@ CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/mips/configs/maltaup_xpa_defconfig b/arch/mips/configs/maltaup_xpa_defconfig index 0f9ef20744f9..0e207bb0f794 100644 --- a/arch/mips/configs/maltaup_xpa_defconfig +++ b/arch/mips/configs/maltaup_xpa_defconfig @@ -171,7 +171,6 @@ CONFIG_IP_SCTP=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -394,7 +393,6 @@ CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_MD4=m @@ -405,7 +403,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TEA=m diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 3629afbe5d75..46b40784a828 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -136,7 +136,6 @@ CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_LLC2=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -184,9 +183,6 @@ CONFIG_BT_HCIUART_BCSP=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m CONFIG_BT_HCIVHCI=m CONFIG_CONNECTOR=m CONFIG_MTD=y @@ -221,12 +217,8 @@ CONFIG_ARCNET_1201=m CONFIG_ARCNET_1051=m CONFIG_ARCNET_RAW=m CONFIG_ARCNET_CAP=m -CONFIG_ARCNET_COM90xx=m -CONFIG_ARCNET_COM90xxIO=m -CONFIG_ARCNET_RIM_I=m CONFIG_ARCNET_COM20020=m CONFIG_ARCNET_COM20020_PCI=m -CONFIG_ARCNET_COM20020_CS=m CONFIG_VORTEX=m CONFIG_TYPHOON=m CONFIG_ADAPTEC_STARFIRE=m @@ -626,7 +618,6 @@ CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_NLS_UTF8=m CONFIG_CRYPTO_BENCHMARK=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index ad9fbd0cbb38..291c6644035d 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -372,7 +372,6 @@ CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_NLS_UTF8=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_WP512=m @@ -380,7 +379,6 @@ CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TEA=m diff --git a/arch/mips/configs/sb1250_swarm_defconfig b/arch/mips/configs/sb1250_swarm_defconfig index 4a25b8d3e507..a5b66b9f6d39 100644 --- a/arch/mips/configs/sb1250_swarm_defconfig +++ b/arch/mips/configs/sb1250_swarm_defconfig @@ -80,7 +80,6 @@ CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_AUTHENC=m CONFIG_CRYPTO_CBC=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m @@ -92,7 +91,6 @@ CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_DEFLATE=m diff --git a/arch/parisc/configs/generic-64bit_defconfig b/arch/parisc/configs/generic-64bit_defconfig index 0c4d54df9cf0..406f8174a1d2 100644 --- a/arch/parisc/configs/generic-64bit_defconfig +++ b/arch/parisc/configs/generic-64bit_defconfig @@ -281,9 +281,7 @@ CONFIG_NLS_ASCII=m CONFIG_NLS_ISO8859_1=m CONFIG_NLS_ISO8859_2=m CONFIG_NLS_UTF8=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DEFLATE=m diff --git a/arch/powerpc/configs/44x/akebono_defconfig b/arch/powerpc/configs/44x/akebono_defconfig index 11ad5ed3cc90..bdb0e6ece6ec 100644 --- a/arch/powerpc/configs/44x/akebono_defconfig +++ b/arch/powerpc/configs/44x/akebono_defconfig @@ -124,7 +124,6 @@ CONFIG_XMON_DEFAULT=y CONFIG_PPC_EARLY_DEBUG=y CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW=0x00010000 CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH=0x33f -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/44x/bamboo_defconfig b/arch/powerpc/configs/44x/bamboo_defconfig index acbce718eaa8..bfffea3a54b2 100644 --- a/arch/powerpc/configs/44x/bamboo_defconfig +++ b/arch/powerpc/configs/44x/bamboo_defconfig @@ -46,6 +46,5 @@ CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/44x/currituck_defconfig b/arch/powerpc/configs/44x/currituck_defconfig index 7283b7d4a1a5..6d6ec5d569b1 100644 --- a/arch/powerpc/configs/44x/currituck_defconfig +++ b/arch/powerpc/configs/44x/currituck_defconfig @@ -83,7 +83,6 @@ CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW=0x10000000 CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH=0x200 CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/44x/ebony_defconfig b/arch/powerpc/configs/44x/ebony_defconfig index 93d2a4e64af9..0d6f9bdf8ad3 100644 --- a/arch/powerpc/configs/44x/ebony_defconfig +++ b/arch/powerpc/configs/44x/ebony_defconfig @@ -52,7 +52,6 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DETECT_HUNG_TASK=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/44x/eiger_defconfig b/arch/powerpc/configs/44x/eiger_defconfig index 509300f400e2..48ab405ab80b 100644 --- a/arch/powerpc/configs/44x/eiger_defconfig +++ b/arch/powerpc/configs/44x/eiger_defconfig @@ -79,7 +79,6 @@ CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CTS=y CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_XTS=y CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y diff --git a/arch/powerpc/configs/44x/fsp2_defconfig b/arch/powerpc/configs/44x/fsp2_defconfig index 5492537f4c6c..b8b21fa15a07 100644 --- a/arch/powerpc/configs/44x/fsp2_defconfig +++ b/arch/powerpc/configs/44x/fsp2_defconfig @@ -115,7 +115,6 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DETECT_HUNG_TASK=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/44x/icon_defconfig b/arch/powerpc/configs/44x/icon_defconfig index fb9a15573546..4f7cd127dc77 100644 --- a/arch/powerpc/configs/44x/icon_defconfig +++ b/arch/powerpc/configs/44x/icon_defconfig @@ -82,6 +82,5 @@ CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/44x/iss476-smp_defconfig b/arch/powerpc/configs/44x/iss476-smp_defconfig index 0f6380e1e612..5188ec5406fd 100644 --- a/arch/powerpc/configs/44x/iss476-smp_defconfig +++ b/arch/powerpc/configs/44x/iss476-smp_defconfig @@ -62,7 +62,6 @@ CONFIG_DETECT_HUNG_TASK=y CONFIG_PPC_EARLY_DEBUG=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/44x/katmai_defconfig b/arch/powerpc/configs/44x/katmai_defconfig index 1a0f1c3e0ee9..59622bd1327d 100644 --- a/arch/powerpc/configs/44x/katmai_defconfig +++ b/arch/powerpc/configs/44x/katmai_defconfig @@ -51,6 +51,5 @@ CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/44x/rainier_defconfig b/arch/powerpc/configs/44x/rainier_defconfig index 6dd67de06a0b..22d10c33f374 100644 --- a/arch/powerpc/configs/44x/rainier_defconfig +++ b/arch/powerpc/configs/44x/rainier_defconfig @@ -57,6 +57,5 @@ CONFIG_PPC_EARLY_DEBUG=y CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW=0xef600300 CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/44x/redwood_defconfig b/arch/powerpc/configs/44x/redwood_defconfig index e28d76416537..1e883938ca11 100644 --- a/arch/powerpc/configs/44x/redwood_defconfig +++ b/arch/powerpc/configs/44x/redwood_defconfig @@ -78,7 +78,6 @@ CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CTS=y CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_XTS=y CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y diff --git a/arch/powerpc/configs/44x/sequoia_defconfig b/arch/powerpc/configs/44x/sequoia_defconfig index b4984eab43eb..ce8912b406eb 100644 --- a/arch/powerpc/configs/44x/sequoia_defconfig +++ b/arch/powerpc/configs/44x/sequoia_defconfig @@ -58,6 +58,5 @@ CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/44x/taishan_defconfig b/arch/powerpc/configs/44x/taishan_defconfig index 3ea5932ab852..8263b3b7d0a1 100644 --- a/arch/powerpc/configs/44x/taishan_defconfig +++ b/arch/powerpc/configs/44x/taishan_defconfig @@ -52,6 +52,5 @@ CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/52xx/cm5200_defconfig b/arch/powerpc/configs/52xx/cm5200_defconfig index 2412a6bf7ee6..ddf1280fe295 100644 --- a/arch/powerpc/configs/52xx/cm5200_defconfig +++ b/arch/powerpc/configs/52xx/cm5200_defconfig @@ -75,4 +75,3 @@ CONFIG_PRINTK_TIME=y CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y diff --git a/arch/powerpc/configs/52xx/motionpro_defconfig b/arch/powerpc/configs/52xx/motionpro_defconfig index 6186ead1e105..d7165dbed529 100644 --- a/arch/powerpc/configs/52xx/motionpro_defconfig +++ b/arch/powerpc/configs/52xx/motionpro_defconfig @@ -88,4 +88,3 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y diff --git a/arch/powerpc/configs/52xx/tqm5200_defconfig b/arch/powerpc/configs/52xx/tqm5200_defconfig index 688f703d8e22..1d2d68b0f137 100644 --- a/arch/powerpc/configs/52xx/tqm5200_defconfig +++ b/arch/powerpc/configs/52xx/tqm5200_defconfig @@ -89,4 +89,3 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_BUGVERBOSE is not set CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y diff --git a/arch/powerpc/configs/83xx/asp8347_defconfig b/arch/powerpc/configs/83xx/asp8347_defconfig index 10192410b33c..07e00c8d6023 100644 --- a/arch/powerpc/configs/83xx/asp8347_defconfig +++ b/arch/powerpc/configs/83xx/asp8347_defconfig @@ -68,4 +68,3 @@ CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig b/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig index 16a42e2267fb..140dd429278e 100644 --- a/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig @@ -83,4 +83,3 @@ CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_DETECT_HUNG_TASK=y -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig b/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig index 80d40ae668eb..7616771f072c 100644 --- a/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig @@ -82,4 +82,3 @@ CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_DETECT_HUNG_TASK=y -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig b/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig index b99caba8724a..e670d16e6fd7 100644 --- a/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig @@ -74,4 +74,3 @@ CONFIG_NLS_CODEPAGE_932=y CONFIG_NLS_ISO8859_8=y CONFIG_NLS_ISO8859_1=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/83xx/mpc834x_itx_defconfig b/arch/powerpc/configs/83xx/mpc834x_itx_defconfig index 11163052fdba..fcf91b52af2d 100644 --- a/arch/powerpc/configs/83xx/mpc834x_itx_defconfig +++ b/arch/powerpc/configs/83xx/mpc834x_itx_defconfig @@ -80,4 +80,3 @@ CONFIG_TMPFS=y CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig b/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig index 312d39e4242c..7d060b6f49ca 100644 --- a/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig +++ b/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig @@ -72,4 +72,3 @@ CONFIG_TMPFS=y CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig b/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig index ac27f99faab8..4567345aea9a 100644 --- a/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig @@ -76,4 +76,3 @@ CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m diff --git a/arch/powerpc/configs/amigaone_defconfig b/arch/powerpc/configs/amigaone_defconfig index 7a515390646b..11dfd964465a 100644 --- a/arch/powerpc/configs/amigaone_defconfig +++ b/arch/powerpc/configs/amigaone_defconfig @@ -111,5 +111,4 @@ CONFIG_DEBUG_MUTEXES=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y CONFIG_CRYPTO_CBC=m -CONFIG_CRYPTO_PCBC=m # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig index 7a31b52e92e1..b5ed8945ec33 100644 --- a/arch/powerpc/configs/cell_defconfig +++ b/arch/powerpc/configs/cell_defconfig @@ -197,5 +197,4 @@ CONFIG_DEBUG_MUTEXES=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y diff --git a/arch/powerpc/configs/chrp32_defconfig b/arch/powerpc/configs/chrp32_defconfig index 66eae5b7e16c..36662f293587 100644 --- a/arch/powerpc/configs/chrp32_defconfig +++ b/arch/powerpc/configs/chrp32_defconfig @@ -115,5 +115,4 @@ CONFIG_DEBUG_MUTEXES=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y CONFIG_CRYPTO_CBC=m -CONFIG_CRYPTO_PCBC=m # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/ep8248e_defconfig b/arch/powerpc/configs/ep8248e_defconfig index 0d8d3f41f194..c3167726706d 100644 --- a/arch/powerpc/configs/ep8248e_defconfig +++ b/arch/powerpc/configs/ep8248e_defconfig @@ -64,7 +64,6 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_BDI_SWITCH=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/fsl-emb-nonhw.config b/arch/powerpc/configs/fsl-emb-nonhw.config index 2f81bc2d819e..391c99117ee0 100644 --- a/arch/powerpc/configs/fsl-emb-nonhw.config +++ b/arch/powerpc/configs/fsl-emb-nonhw.config @@ -19,7 +19,6 @@ CONFIG_CPUSETS=y CONFIG_CRAMFS=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_NULL=y -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_SHA512=y CONFIG_DEBUG_FS=y diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 5ca1676e6058..04afb1594890 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -235,7 +235,6 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_MUTEXES=y CONFIG_BOOTX_TEXT=y CONFIG_CRYPTO_BENCHMARK=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/powerpc/configs/linkstation_defconfig b/arch/powerpc/configs/linkstation_defconfig index 31f84d08b6ef..e1c1b00b0c81 100644 --- a/arch/powerpc/configs/linkstation_defconfig +++ b/arch/powerpc/configs/linkstation_defconfig @@ -128,7 +128,6 @@ CONFIG_NLS_UTF8=m CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/powerpc/configs/mgcoge_defconfig b/arch/powerpc/configs/mgcoge_defconfig index f65001e7877f..a31e1184f912 100644 --- a/arch/powerpc/configs/mgcoge_defconfig +++ b/arch/powerpc/configs/mgcoge_defconfig @@ -78,5 +78,4 @@ CONFIG_MAGIC_SYSRQ=y # CONFIG_SCHED_DEBUG is not set CONFIG_BDI_SWITCH=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig index a815d9e5e3e8..d603d8e93958 100644 --- a/arch/powerpc/configs/mpc83xx_defconfig +++ b/arch/powerpc/configs/mpc83xx_defconfig @@ -98,6 +98,5 @@ CONFIG_NFS_FS=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_TALITOS=y diff --git a/arch/powerpc/configs/mvme5100_defconfig b/arch/powerpc/configs/mvme5100_defconfig index c82754c14e15..3918768d7cd2 100644 --- a/arch/powerpc/configs/mvme5100_defconfig +++ b/arch/powerpc/configs/mvme5100_defconfig @@ -113,7 +113,6 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=20 CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/powerpc/configs/pmac32_defconfig b/arch/powerpc/configs/pmac32_defconfig index ae45f70b29f0..728c8cabfb83 100644 --- a/arch/powerpc/configs/pmac32_defconfig +++ b/arch/powerpc/configs/pmac32_defconfig @@ -281,7 +281,6 @@ CONFIG_DETECT_HUNG_TASK=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y CONFIG_BOOTX_TEXT=y -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index 5d32c2767a65..2b9b46704180 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -314,7 +314,6 @@ CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y CONFIG_CRYPTO_BENCHMARK=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_WP512=m diff --git a/arch/powerpc/configs/ppc44x_defconfig b/arch/powerpc/configs/ppc44x_defconfig index 41c930f74ed4..0dc537f6aff3 100644 --- a/arch/powerpc/configs/ppc44x_defconfig +++ b/arch/powerpc/configs/ppc44x_defconfig @@ -93,6 +93,5 @@ CONFIG_NLS_ISO8859_1=m CONFIG_MAGIC_SYSRQ=y CONFIG_DETECT_HUNG_TASK=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y # CONFIG_CRYPTO_HW is not set CONFIG_VIRTUALIZATION=y diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 6316ca4df25d..f795b74602ec 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -378,7 +378,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TWOFISH=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_LZO=m diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index 20cc17dce94d..f365b79c7802 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -220,7 +220,6 @@ CONFIG_XMON=y CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index eda1fec7ffd9..f528cab1c6cf 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -228,7 +228,6 @@ CONFIG_ATM=m CONFIG_ATM_BR2684=m CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m CONFIG_IPDDP_ENCAP=y @@ -282,9 +281,6 @@ CONFIG_BT_HCIUART_BCSP=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m CONFIG_BT_HCIVHCI=m CONFIG_CFG80211=m CONFIG_MAC80211=m @@ -1052,7 +1048,6 @@ CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m @@ -1064,7 +1059,6 @@ CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 7cfae0b7b2f3..22cfe85b7db7 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -145,7 +145,6 @@ CONFIG_CIFS=m CONFIG_NLS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_LZO=m CONFIG_PRINTK_TIME=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 730c90b4a876..54637be87fb7 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -125,7 +125,6 @@ CONFIG_UNIX=y CONFIG_UNIX_DIAG=m CONFIG_TLS=m CONFIG_TLS_DEVICE=y -CONFIG_TLS_TOE=y CONFIG_XFRM_USER=m CONFIG_NET_KEY=m CONFIG_SMC=m @@ -780,7 +779,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -791,7 +789,6 @@ CONFIG_CRYPTO_ADIANTUM=m CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SEQIV=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index dd5fc1426c88..5f5114a253cf 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -116,7 +116,6 @@ CONFIG_UNIX=y CONFIG_UNIX_DIAG=m CONFIG_TLS=m CONFIG_TLS_DEVICE=y -CONFIG_TLS_TOE=y CONFIG_XFRM_USER=m CONFIG_NET_KEY=m CONFIG_SMC=m @@ -764,7 +763,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_DES=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m @@ -775,7 +773,6 @@ CONFIG_CRYPTO_ADIANTUM=m CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SEQIV=y diff --git a/arch/sh/configs/hp6xx_defconfig b/arch/sh/configs/hp6xx_defconfig index b6116a203a27..bdc476dcfa43 100644 --- a/arch/sh/configs/hp6xx_defconfig +++ b/arch/sh/configs/hp6xx_defconfig @@ -51,6 +51,5 @@ CONFIG_NLS_CODEPAGE_850=y CONFIG_CRYPTO=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_HW is not set diff --git a/arch/sh/configs/landisk_defconfig b/arch/sh/configs/landisk_defconfig index 4c39a23e6e0d..2b7e10c9acee 100644 --- a/arch/sh/configs/landisk_defconfig +++ b/arch/sh/configs/landisk_defconfig @@ -24,7 +24,6 @@ CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_PNP=y # CONFIG_IPV6 is not set CONFIG_NETFILTER=y -CONFIG_ATALK=m CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_ATA=y diff --git a/arch/sh/configs/r7780mp_defconfig b/arch/sh/configs/r7780mp_defconfig index af954f75444b..7b46f62fe7db 100644 --- a/arch/sh/configs/r7780mp_defconfig +++ b/arch/sh/configs/r7780mp_defconfig @@ -101,5 +101,4 @@ CONFIG_DETECT_HUNG_TASK=y # CONFIG_DEBUG_PREEMPT is not set CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y diff --git a/arch/sh/configs/r7785rp_defconfig b/arch/sh/configs/r7785rp_defconfig index a66dd6d74cf1..6d2461a85f19 100644 --- a/arch/sh/configs/r7785rp_defconfig +++ b/arch/sh/configs/r7785rp_defconfig @@ -99,5 +99,4 @@ CONFIG_SH_STANDARD_BIOS=y CONFIG_DEBUG_STACK_USAGE=y CONFIG_4KSTACKS=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y diff --git a/arch/sh/configs/se7712_defconfig b/arch/sh/configs/se7712_defconfig index dee1d88f6a7d..8d8a311c60b1 100644 --- a/arch/sh/configs/se7712_defconfig +++ b/arch/sh/configs/se7712_defconfig @@ -93,4 +93,3 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_FRAME_POINTER=y CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m diff --git a/arch/sh/configs/sh2007_defconfig b/arch/sh/configs/sh2007_defconfig index 5d9080499485..3060bcd9cc5f 100644 --- a/arch/sh/configs/sh2007_defconfig +++ b/arch/sh/configs/sh2007_defconfig @@ -165,7 +165,6 @@ CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_AUTHENC=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_LRW=y -CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_XTS=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=y @@ -182,7 +181,6 @@ CONFIG_CRYPTO_BLOWFISH=y CONFIG_CRYPTO_CAMELLIA=y CONFIG_CRYPTO_CAST5=y CONFIG_CRYPTO_CAST6=y -CONFIG_CRYPTO_FCRYPT=y CONFIG_CRYPTO_KHAZAD=y CONFIG_CRYPTO_SEED=y CONFIG_CRYPTO_SERPENT=y diff --git a/arch/sparc/configs/sparc32_defconfig b/arch/sparc/configs/sparc32_defconfig index 48d834acafb4..d5579217fb4c 100644 --- a/arch/sparc/configs/sparc32_defconfig +++ b/arch/sparc/configs/sparc32_defconfig @@ -80,7 +80,6 @@ CONFIG_KGDB=y CONFIG_KGDB_TESTS=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig index 632081a262ba..3763108c3bd4 100644 --- a/arch/sparc/configs/sparc64_defconfig +++ b/arch/sparc/configs/sparc64_defconfig @@ -206,7 +206,6 @@ CONFIG_KEYS=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_LRW=m -CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y @@ -220,7 +219,6 @@ CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m diff --git a/crypto/Kconfig b/crypto/Kconfig index c3d7a20d5cb1..f1e372195273 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -464,15 +464,6 @@ config CRYPTO_DES Triple DES EDE (Encrypt/Decrypt/Encrypt) (FIPS 46-3, ISO/IEC 18033-3) cipher algorithms -config CRYPTO_FCRYPT - tristate "FCrypt" - select CRYPTO_ALGAPI - select CRYPTO_SKCIPHER - help - FCrypt algorithm used by RxRPC - - See https://ota.polyonymo.us/fcrypt-paper.txt - config CRYPTO_KHAZAD tristate "Khazad" depends on CRYPTO_USER_API_ENABLE_OBSOLETE @@ -712,15 +703,6 @@ config CRYPTO_LRW See https://people.csail.mit.edu/rivest/pubs/LRW02.pdf -config CRYPTO_PCBC - tristate "PCBC (Propagating Cipher Block Chaining)" - select CRYPTO_SKCIPHER - select CRYPTO_MANAGER - help - PCBC (Propagating Cipher Block Chaining) mode - - This block cipher mode is required for RxRPC. - config CRYPTO_XCTR tristate select CRYPTO_SKCIPHER diff --git a/crypto/Makefile b/crypto/Makefile index 0798fd7aea0d..8386d55a9755 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -89,7 +89,6 @@ CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/b obj-$(CONFIG_CRYPTO_BLAKE2B) += blake2b.o obj-$(CONFIG_CRYPTO_ECB) += ecb.o obj-$(CONFIG_CRYPTO_CBC) += cbc.o -obj-$(CONFIG_CRYPTO_PCBC) += pcbc.o obj-$(CONFIG_CRYPTO_CTS) += cts.o obj-$(CONFIG_CRYPTO_LRW) += lrw.o obj-$(CONFIG_CRYPTO_XTS) += xts.o @@ -124,7 +123,6 @@ endif obj-$(CONFIG_CRYPTO_PCRYPT) += pcrypt.o obj-$(CONFIG_CRYPTO_CRYPTD) += cryptd.o obj-$(CONFIG_CRYPTO_DES) += des_generic.o -obj-$(CONFIG_CRYPTO_FCRYPT) += fcrypt.o obj-$(CONFIG_CRYPTO_BLOWFISH) += blowfish_generic.o obj-$(CONFIG_CRYPTO_BLOWFISH_COMMON) += blowfish_common.o obj-$(CONFIG_CRYPTO_TWOFISH) += twofish_generic.o diff --git a/crypto/ahash.c b/crypto/ahash.c index dd56b0e45c0d..d86c78f8d550 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -860,76 +860,6 @@ bool crypto_hash_alg_has_setkey(struct hash_alg_common *halg) } EXPORT_SYMBOL_GPL(crypto_hash_alg_has_setkey); -struct crypto_ahash *crypto_clone_ahash(struct crypto_ahash *hash) -{ - struct hash_alg_common *halg = crypto_hash_alg_common(hash); - struct crypto_tfm *tfm = crypto_ahash_tfm(hash); - struct crypto_ahash *fb = NULL; - struct crypto_ahash *nhash; - struct ahash_alg *alg; - int err; - - if (!crypto_hash_alg_has_setkey(halg)) { - tfm = crypto_tfm_get(tfm); - if (IS_ERR(tfm)) - return ERR_CAST(tfm); - - return hash; - } - - nhash = crypto_clone_tfm(&crypto_ahash_type, tfm); - - if (IS_ERR(nhash)) - return nhash; - - nhash->reqsize = hash->reqsize; - nhash->statesize = hash->statesize; - - if (likely(hash->using_shash)) { - struct crypto_shash **nctx = crypto_ahash_ctx(nhash); - struct crypto_shash *shash; - - shash = crypto_clone_shash(ahash_to_shash(hash)); - if (IS_ERR(shash)) { - err = PTR_ERR(shash); - goto out_free_nhash; - } - crypto_ahash_tfm(nhash)->exit = crypto_exit_ahash_using_shash; - nhash->using_shash = true; - *nctx = shash; - return nhash; - } - - if (crypto_ahash_need_fallback(hash)) { - fb = crypto_clone_ahash(crypto_ahash_fb(hash)); - err = PTR_ERR(fb); - if (IS_ERR(fb)) - goto out_free_nhash; - - crypto_ahash_tfm(nhash)->fb = crypto_ahash_tfm(fb); - } - - err = -ENOSYS; - alg = crypto_ahash_alg(hash); - if (!alg->clone_tfm) - goto out_free_fb; - - err = alg->clone_tfm(nhash, hash); - if (err) - goto out_free_fb; - - crypto_ahash_tfm(nhash)->exit = crypto_ahash_exit_tfm; - - return nhash; - -out_free_fb: - crypto_free_ahash(fb); -out_free_nhash: - crypto_free_ahash(nhash); - return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(crypto_clone_ahash); - static int ahash_default_export_core(struct ahash_request *req, void *out) { return -ENOSYS; diff --git a/crypto/api.c b/crypto/api.c index 74e17d5049c9..4349c2caa23a 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -405,20 +405,19 @@ void crypto_shoot_alg(struct crypto_alg *alg) } EXPORT_SYMBOL_GPL(crypto_shoot_alg); -struct crypto_tfm *__crypto_alloc_tfmgfp(struct crypto_alg *alg, u32 type, - u32 mask, gfp_t gfp) +struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, + u32 mask) { struct crypto_tfm *tfm; unsigned int tfm_size; int err = -ENOMEM; tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask); - tfm = kzalloc(tfm_size, gfp); + tfm = kzalloc(tfm_size, GFP_KERNEL); if (tfm == NULL) goto out_err; tfm->__crt_alg = alg; - refcount_set(&tfm->refcnt, 1); if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm))) goto cra_init_failed; @@ -435,13 +434,6 @@ struct crypto_tfm *__crypto_alloc_tfmgfp(struct crypto_alg *alg, u32 type, out: return tfm; } -EXPORT_SYMBOL_GPL(__crypto_alloc_tfmgfp); - -struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, - u32 mask) -{ - return __crypto_alloc_tfmgfp(alg, type, mask, GFP_KERNEL); -} EXPORT_SYMBOL_GPL(__crypto_alloc_tfm); /* @@ -500,43 +492,23 @@ struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_alloc_base); -static void *crypto_alloc_tfmmem(struct crypto_alg *alg, - const struct crypto_type *frontend, int node, - gfp_t gfp) -{ - struct crypto_tfm *tfm; - unsigned int tfmsize; - unsigned int total; - char *mem; - - tfmsize = frontend->tfmsize; - total = tfmsize + sizeof(*tfm) + frontend->extsize(alg); - - mem = kzalloc_node(total, gfp, node); - if (mem == NULL) - return ERR_PTR(-ENOMEM); - - tfm = (struct crypto_tfm *)(mem + tfmsize); - tfm->__crt_alg = alg; - tfm->node = node; - refcount_set(&tfm->refcnt, 1); - - return mem; -} - void *crypto_create_tfm_node(struct crypto_alg *alg, const struct crypto_type *frontend, int node) { struct crypto_tfm *tfm; + size_t size; char *mem; int err; - mem = crypto_alloc_tfmmem(alg, frontend, node, GFP_KERNEL); - if (IS_ERR(mem)) - goto out; + size = frontend->tfmsize + sizeof(*tfm) + frontend->extsize(alg); + mem = kzalloc_node(size, GFP_KERNEL, node); + if (!mem) + return ERR_PTR(-ENOMEM); tfm = (struct crypto_tfm *)(mem + frontend->tfmsize); + tfm->__crt_alg = alg; + tfm->node = node; tfm->fb = tfm; err = frontend->init_tfm(tfm); @@ -560,32 +532,6 @@ void *crypto_create_tfm_node(struct crypto_alg *alg, } EXPORT_SYMBOL_GPL(crypto_create_tfm_node); -void *crypto_clone_tfm(const struct crypto_type *frontend, - struct crypto_tfm *otfm) -{ - struct crypto_alg *alg = otfm->__crt_alg; - struct crypto_tfm *tfm; - char *mem; - - mem = ERR_PTR(-ESTALE); - if (unlikely(!crypto_mod_get(alg))) - goto out; - - mem = crypto_alloc_tfmmem(alg, frontend, otfm->node, GFP_ATOMIC); - if (IS_ERR(mem)) { - crypto_mod_put(alg); - goto out; - } - - tfm = (struct crypto_tfm *)(mem + frontend->tfmsize); - tfm->crt_flags = otfm->crt_flags; - tfm->fb = tfm; - -out: - return mem; -} -EXPORT_SYMBOL_GPL(crypto_clone_tfm); - struct crypto_alg *crypto_find_alg(const char *alg_name, const struct crypto_type *frontend, u32 type, u32 mask) @@ -675,8 +621,6 @@ void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm) if (IS_ERR_OR_NULL(mem)) return; - if (!refcount_dec_and_test(&tfm->refcnt)) - return; alg = tfm->__crt_alg; if (!tfm->exit && alg->cra_exit) diff --git a/crypto/cipher.c b/crypto/cipher.c index 1fe62bf79656..c9dab656a622 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -89,31 +89,3 @@ void crypto_cipher_decrypt_one(struct crypto_cipher *tfm, cipher_crypt_one(tfm, dst, src, false); } EXPORT_SYMBOL_NS_GPL(crypto_cipher_decrypt_one, "CRYPTO_INTERNAL"); - -struct crypto_cipher *crypto_clone_cipher(struct crypto_cipher *cipher) -{ - struct crypto_tfm *tfm = crypto_cipher_tfm(cipher); - struct crypto_alg *alg = tfm->__crt_alg; - struct crypto_cipher *ncipher; - struct crypto_tfm *ntfm; - - if (alg->cra_init) - return ERR_PTR(-ENOSYS); - - if (unlikely(!crypto_mod_get(alg))) - return ERR_PTR(-ESTALE); - - ntfm = __crypto_alloc_tfmgfp(alg, CRYPTO_ALG_TYPE_CIPHER, - CRYPTO_ALG_TYPE_MASK, GFP_ATOMIC); - if (IS_ERR(ntfm)) { - crypto_mod_put(alg); - return ERR_CAST(ntfm); - } - - ntfm->crt_flags = tfm->crt_flags; - - ncipher = __crypto_cipher_cast(ntfm); - - return ncipher; -} -EXPORT_SYMBOL_GPL(crypto_clone_cipher); diff --git a/crypto/cmac.c b/crypto/cmac.c index 1b03964abe00..83e58937f013 100644 --- a/crypto/cmac.c +++ b/crypto/cmac.c @@ -151,21 +151,6 @@ static int cmac_init_tfm(struct crypto_shash *tfm) return 0; } -static int cmac_clone_tfm(struct crypto_shash *tfm, struct crypto_shash *otfm) -{ - struct cmac_tfm_ctx *octx = crypto_shash_ctx(otfm); - struct cmac_tfm_ctx *ctx = crypto_shash_ctx(tfm); - struct crypto_cipher *cipher; - - cipher = crypto_clone_cipher(octx->child); - if (IS_ERR(cipher)) - return PTR_ERR(cipher); - - ctx->child = cipher; - - return 0; -} - static void cmac_exit_tfm(struct crypto_shash *tfm) { struct cmac_tfm_ctx *ctx = crypto_shash_ctx(tfm); @@ -222,7 +207,6 @@ static int cmac_create(struct crypto_template *tmpl, struct rtattr **tb) inst->alg.finup = crypto_cmac_digest_finup; inst->alg.setkey = crypto_cmac_digest_setkey; inst->alg.init_tfm = cmac_init_tfm; - inst->alg.clone_tfm = cmac_clone_tfm; inst->alg.exit_tfm = cmac_exit_tfm; inst->free = shash_free_singlespawn_instance; diff --git a/crypto/cryptd.c b/crypto/cryptd.c index aba9fe0f23b4..d8dbf07ab426 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -452,21 +452,6 @@ static int cryptd_hash_init_tfm(struct crypto_ahash *tfm) return 0; } -static int cryptd_hash_clone_tfm(struct crypto_ahash *ntfm, - struct crypto_ahash *tfm) -{ - struct cryptd_hash_ctx *nctx = crypto_ahash_ctx(ntfm); - struct cryptd_hash_ctx *ctx = crypto_ahash_ctx(tfm); - struct crypto_shash *hash; - - hash = crypto_clone_shash(ctx->child); - if (IS_ERR(hash)) - return PTR_ERR(hash); - - nctx->child = hash; - return 0; -} - static void cryptd_hash_exit_tfm(struct crypto_ahash *tfm) { struct cryptd_hash_ctx *ctx = crypto_ahash_ctx(tfm); @@ -700,7 +685,6 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx); inst->alg.init_tfm = cryptd_hash_init_tfm; - inst->alg.clone_tfm = cryptd_hash_clone_tfm; inst->alg.exit_tfm = cryptd_hash_exit_tfm; inst->alg.init = cryptd_hash_init_enqueue; diff --git a/crypto/hmac.c b/crypto/hmac.c index 148af460ae97..807e08b252c5 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -158,20 +158,6 @@ static int hmac_init_tfm(struct crypto_shash *parent) return 0; } -static int hmac_clone_tfm(struct crypto_shash *dst, struct crypto_shash *src) -{ - struct hmac_ctx *sctx = crypto_shash_ctx(src); - struct hmac_ctx *dctx = crypto_shash_ctx(dst); - struct crypto_shash *hash; - - hash = crypto_clone_shash(sctx->hash); - if (IS_ERR(hash)) - return PTR_ERR(hash); - - dctx->hash = hash; - return 0; -} - static void hmac_exit_tfm(struct crypto_shash *parent) { struct hmac_ctx *tctx = crypto_shash_ctx(parent); @@ -235,7 +221,6 @@ static int __hmac_create_shash(struct crypto_template *tmpl, inst->alg.import_core = hmac_import_core; inst->alg.setkey = hmac_setkey; inst->alg.init_tfm = hmac_init_tfm; - inst->alg.clone_tfm = hmac_clone_tfm; inst->alg.exit_tfm = hmac_exit_tfm; inst->free = shash_free_singlespawn_instance; @@ -423,21 +408,6 @@ static int hmac_init_ahash_tfm(struct crypto_ahash *parent) return 0; } -static int hmac_clone_ahash_tfm(struct crypto_ahash *dst, - struct crypto_ahash *src) -{ - struct ahash_hmac_ctx *sctx = crypto_ahash_ctx(src); - struct ahash_hmac_ctx *dctx = crypto_ahash_ctx(dst); - struct crypto_ahash *hash; - - hash = crypto_clone_ahash(sctx->hash); - if (IS_ERR(hash)) - return PTR_ERR(hash); - - dctx->hash = hash; - return 0; -} - static void hmac_exit_ahash_tfm(struct crypto_ahash *parent) { struct ahash_hmac_ctx *tctx = crypto_ahash_ctx(parent); @@ -503,7 +473,6 @@ static int hmac_create_ahash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.import_core = hmac_import_core_ahash; inst->alg.setkey = hmac_setkey_ahash; inst->alg.init_tfm = hmac_init_ahash_tfm; - inst->alg.clone_tfm = hmac_clone_ahash_tfm; inst->alg.exit_tfm = hmac_exit_ahash_tfm; inst->free = ahash_free_singlespawn_instance; diff --git a/crypto/internal.h b/crypto/internal.h index 8fbe0226d48e..b0a10986f61e 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -120,14 +119,10 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, struct crypto_alg *nalg); void crypto_remove_final(struct list_head *list); void crypto_shoot_alg(struct crypto_alg *alg); -struct crypto_tfm *__crypto_alloc_tfmgfp(struct crypto_alg *alg, u32 type, - u32 mask, gfp_t gfp); struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, u32 mask); void *crypto_create_tfm_node(struct crypto_alg *alg, const struct crypto_type *frontend, int node); -void *crypto_clone_tfm(const struct crypto_type *frontend, - struct crypto_tfm *otfm); static inline void *crypto_create_tfm(struct crypto_alg *alg, const struct crypto_type *frontend) @@ -211,10 +206,5 @@ static inline int crypto_is_test_larval(struct crypto_larval *larval) return larval->alg.cra_driver_name[0]; } -static inline struct crypto_tfm *crypto_tfm_get(struct crypto_tfm *tfm) -{ - return refcount_inc_not_zero(&tfm->refcnt) ? tfm : ERR_PTR(-EOVERFLOW); -} - #endif /* _CRYPTO_INTERNAL_H */ diff --git a/crypto/pcbc.c b/crypto/pcbc.c deleted file mode 100644 index d092717ea4fc..000000000000 --- a/crypto/pcbc.c +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * PCBC: Propagating Cipher Block Chaining mode - * - * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * Derived from cbc.c - * - Copyright (c) 2006 Herbert Xu - */ - -#include -#include -#include -#include -#include -#include -#include - -static int crypto_pcbc_encrypt_segment(struct skcipher_request *req, - struct skcipher_walk *walk, - struct crypto_cipher *tfm) -{ - int bsize = crypto_cipher_blocksize(tfm); - const u8 *src = walk->src.virt.addr; - unsigned int nbytes = walk->nbytes; - u8 *dst = walk->dst.virt.addr; - u8 * const iv = walk->iv; - - do { - crypto_xor(iv, src, bsize); - crypto_cipher_encrypt_one(tfm, dst, iv); - crypto_xor_cpy(iv, dst, src, bsize); - - src += bsize; - dst += bsize; - } while ((nbytes -= bsize) >= bsize); - - return nbytes; -} - -static int crypto_pcbc_encrypt_inplace(struct skcipher_request *req, - struct skcipher_walk *walk, - struct crypto_cipher *tfm) -{ - int bsize = crypto_cipher_blocksize(tfm); - unsigned int nbytes = walk->nbytes; - u8 *dst = walk->dst.virt.addr; - u8 * const iv = walk->iv; - u8 tmpbuf[MAX_CIPHER_BLOCKSIZE]; - - do { - memcpy(tmpbuf, dst, bsize); - crypto_xor(iv, dst, bsize); - crypto_cipher_encrypt_one(tfm, dst, iv); - crypto_xor_cpy(iv, tmpbuf, dst, bsize); - - dst += bsize; - } while ((nbytes -= bsize) >= bsize); - - return nbytes; -} - -static int crypto_pcbc_encrypt(struct skcipher_request *req) -{ - struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); - struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); - struct skcipher_walk walk; - unsigned int nbytes; - int err; - - err = skcipher_walk_virt(&walk, req, false); - - while (walk.nbytes) { - if (walk.src.virt.addr == walk.dst.virt.addr) - nbytes = crypto_pcbc_encrypt_inplace(req, &walk, - cipher); - else - nbytes = crypto_pcbc_encrypt_segment(req, &walk, - cipher); - err = skcipher_walk_done(&walk, nbytes); - } - - return err; -} - -static int crypto_pcbc_decrypt_segment(struct skcipher_request *req, - struct skcipher_walk *walk, - struct crypto_cipher *tfm) -{ - int bsize = crypto_cipher_blocksize(tfm); - const u8 *src = walk->src.virt.addr; - unsigned int nbytes = walk->nbytes; - u8 *dst = walk->dst.virt.addr; - u8 * const iv = walk->iv; - - do { - crypto_cipher_decrypt_one(tfm, dst, src); - crypto_xor(dst, iv, bsize); - crypto_xor_cpy(iv, dst, src, bsize); - - src += bsize; - dst += bsize; - } while ((nbytes -= bsize) >= bsize); - - return nbytes; -} - -static int crypto_pcbc_decrypt_inplace(struct skcipher_request *req, - struct skcipher_walk *walk, - struct crypto_cipher *tfm) -{ - int bsize = crypto_cipher_blocksize(tfm); - unsigned int nbytes = walk->nbytes; - u8 *dst = walk->dst.virt.addr; - u8 * const iv = walk->iv; - u8 tmpbuf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(u32)); - - do { - memcpy(tmpbuf, dst, bsize); - crypto_cipher_decrypt_one(tfm, dst, dst); - crypto_xor(dst, iv, bsize); - crypto_xor_cpy(iv, dst, tmpbuf, bsize); - - dst += bsize; - } while ((nbytes -= bsize) >= bsize); - - return nbytes; -} - -static int crypto_pcbc_decrypt(struct skcipher_request *req) -{ - struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); - struct crypto_cipher *cipher = skcipher_cipher_simple(tfm); - struct skcipher_walk walk; - unsigned int nbytes; - int err; - - err = skcipher_walk_virt(&walk, req, false); - - while (walk.nbytes) { - if (walk.src.virt.addr == walk.dst.virt.addr) - nbytes = crypto_pcbc_decrypt_inplace(req, &walk, - cipher); - else - nbytes = crypto_pcbc_decrypt_segment(req, &walk, - cipher); - err = skcipher_walk_done(&walk, nbytes); - } - - return err; -} - -static int crypto_pcbc_create(struct crypto_template *tmpl, struct rtattr **tb) -{ - struct skcipher_instance *inst; - int err; - - inst = skcipher_alloc_instance_simple(tmpl, tb); - if (IS_ERR(inst)) - return PTR_ERR(inst); - - inst->alg.encrypt = crypto_pcbc_encrypt; - inst->alg.decrypt = crypto_pcbc_decrypt; - - err = skcipher_register_instance(tmpl, inst); - if (err) - inst->free(inst); - - return err; -} - -static struct crypto_template crypto_pcbc_tmpl = { - .name = "pcbc", - .create = crypto_pcbc_create, - .module = THIS_MODULE, -}; - -static int __init crypto_pcbc_module_init(void) -{ - return crypto_register_template(&crypto_pcbc_tmpl); -} - -static void __exit crypto_pcbc_module_exit(void) -{ - crypto_unregister_template(&crypto_pcbc_tmpl); -} - -module_init(crypto_pcbc_module_init); -module_exit(crypto_pcbc_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("PCBC block cipher mode of operation"); -MODULE_ALIAS_CRYPTO("pcbc"); -MODULE_IMPORT_NS("CRYPTO_INTERNAL"); diff --git a/crypto/shash.c b/crypto/shash.c index d31a65570f69..d4a6c3b6fa37 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -393,43 +393,6 @@ int crypto_has_shash(const char *alg_name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_has_shash); -struct crypto_shash *crypto_clone_shash(struct crypto_shash *hash) -{ - struct crypto_tfm *tfm = crypto_shash_tfm(hash); - struct shash_alg *alg = crypto_shash_alg(hash); - struct crypto_shash *nhash; - int err; - - if (!crypto_shash_alg_has_setkey(alg)) { - tfm = crypto_tfm_get(tfm); - if (IS_ERR(tfm)) - return ERR_CAST(tfm); - - return hash; - } - - if (!alg->clone_tfm && (alg->init_tfm || alg->base.cra_init)) - return ERR_PTR(-ENOSYS); - - nhash = crypto_clone_tfm(&crypto_shash_type, tfm); - if (IS_ERR(nhash)) - return nhash; - - if (alg->clone_tfm) { - err = alg->clone_tfm(nhash, hash); - if (err) { - crypto_free_shash(nhash); - return ERR_PTR(err); - } - } - - if (alg->exit_tfm) - crypto_shash_tfm(nhash)->exit = crypto_shash_exit_tfm; - - return nhash; -} -EXPORT_SYMBOL_GPL(crypto_clone_shash); - int hash_prepare_alg(struct hash_alg_common *alg) { struct crypto_alg *base = &alg->base; diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index e54517605f5f..61a2501bfe9b 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -1600,10 +1600,6 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb) ret = min(ret, tcrypt_test("ecb(xeta)")); break; - case 31: - ret = min(ret, tcrypt_test("pcbc(fcrypt)")); - break; - case 32: ret = min(ret, tcrypt_test("ecb(camellia)")); ret = min(ret, tcrypt_test("cbc(camellia)")); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index b84d5fcbe107..4958211fbfa9 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -4717,15 +4717,6 @@ static const struct alg_test_desc alg_test_descs[] = { .suite = { .cipher = __VECS(des3_ede_tv_template) } - }, { - .alg = "ecb(fcrypt)", - .test = alg_test_skcipher, - .suite = { - .cipher = { - .vecs = fcrypt_pcbc_tv_template, - .count = 1 - } - } }, { .alg = "ecb(khazad)", .test = alg_test_skcipher, @@ -5144,12 +5135,6 @@ static const struct alg_test_desc alg_test_descs[] = { .test = alg_test_null, .fips_allowed = 1, }, { - .alg = "pcbc(fcrypt)", - .test = alg_test_skcipher, - .suite = { - .cipher = __VECS(fcrypt_pcbc_tv_template) - } - }, { #if IS_ENABLED(CONFIG_CRYPTO_PHMAC_S390) .alg = "phmac(sha224)", .test = alg_test_hash, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index b7dcf40af6db..c4a15e714ecd 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -24394,51 +24394,6 @@ static const struct cipher_testvec xeta_tv_template[] = { } }; -/* - * FCrypt test vectors - */ -static const struct cipher_testvec fcrypt_pcbc_tv_template[] = { - { /* http://www.openafs.org/pipermail/openafs-devel/2000-December/005320.html */ - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .klen = 8, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ptext = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ctext = "\x0E\x09\x00\xC7\x3E\xF7\xED\x41", - .len = 8, - }, { - .key = "\x11\x44\x77\xAA\xDD\x00\x33\x66", - .klen = 8, - .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ptext = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0", - .ctext = "\xD8\xED\x78\x74\x77\xEC\x06\x80", - .len = 8, - }, { /* From Arla */ - .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .klen = 8, - .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .ptext = "The quick brown fox jumps over the lazy dogs.\0\0", - .ctext = "\x00\xf0\x0e\x11\x75\xe6\x23\x82" - "\xee\xac\x98\x62\x44\x51\xe4\x84" - "\xc3\x59\xd8\xaa\x64\x60\xae\xf7" - "\xd2\xd9\x13\x79\x72\xa3\x45\x03" - "\x23\xb5\x62\xd7\x0c\xf5\x27\xd1" - "\xf8\x91\x3c\xac\x44\x22\x92\xef", - .len = 48, - }, { - .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", - .klen = 8, - .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", - .ptext = "The quick brown fox jumps over the lazy dogs.\0\0", - .ctext = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" - "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" - "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" - "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" - "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" - "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", - .len = 48, - } -}; - /* * CAMELLIA test vectors. */ diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 24c764664c24..fc0e91a1029e 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -1180,10 +1180,6 @@ static const struct atmdev_ops fpga_ops = { .close = pclose, .ioctl = NULL, .send = psend, - .send_oam = NULL, - .phy_put = NULL, - .phy_get = NULL, - .change_qos = NULL, .proc_read = NULL, .owner = THIS_MODULE }; @@ -1464,8 +1460,8 @@ static void fpga_remove(struct pci_dev *dev) } static const struct pci_device_id fpga_pci_tbl[] = { - { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } + { PCI_DEVICE(0x10ee, 0x0300) }, + { } }; MODULE_DEVICE_TABLE(pci,fpga_pci_tbl); diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index c5d45cf91f88..4e8c24d757e9 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -337,46 +337,6 @@ config BT_HCIBFUSB Say Y here to compile support for HCI BFUSB devices into the kernel or say M to compile it as module (bfusb). -config BT_HCIDTL1 - tristate "HCI DTL1 (PC Card) driver" - depends on PCMCIA && HAS_IOPORT - help - Bluetooth HCI DTL1 (PC Card) driver. - This driver provides support for Bluetooth PCMCIA devices with - Nokia DTL1 interface: - Nokia Bluetooth Card - Socket Bluetooth CF Card - - Say Y here to compile support for HCI DTL1 devices into the - kernel or say M to compile it as module (dtl1_cs). - -config BT_HCIBT3C - tristate "HCI BT3C (PC Card) driver" - depends on PCMCIA && HAS_IOPORT - select FW_LOADER - help - Bluetooth HCI BT3C (PC Card) driver. - This driver provides support for Bluetooth PCMCIA devices with - 3Com BT3C interface: - 3Com Bluetooth Card (3CRWB6096) - HP Bluetooth Card - - Say Y here to compile support for HCI BT3C devices into the - kernel or say M to compile it as module (bt3c_cs). - -config BT_HCIBLUECARD - tristate "HCI BlueCard (PC Card) driver" - depends on PCMCIA && HAS_IOPORT - help - Bluetooth HCI BlueCard (PC Card) driver. - This driver provides support for Bluetooth PCMCIA devices with - Anycom BlueCard interface: - Anycom Bluetooth PC Card - Anycom Bluetooth CF Card - - Say Y here to compile support for HCI BlueCard devices into the - kernel or say M to compile it as module (bluecard_cs). - config BT_HCIVHCI tristate "HCI VHCI (Virtual HCI device) driver" select WANT_DEV_COREDUMP @@ -502,7 +462,7 @@ config BT_NXPUART config BT_INTEL_PCIE tristate "Intel HCI PCIe driver" - depends on PCI + depends on PCI && ACPI select BT_INTEL select FW_LOADER help diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 81856512ddd0..bafc26250b63 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -9,9 +9,6 @@ obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o obj-$(CONFIG_BT_HCIBCM4377) += hci_bcm4377.o obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o -obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o -obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o -obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUSB) += btusb.o obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c deleted file mode 100644 index 1e3a56e9b139..000000000000 --- a/drivers/bluetooth/bluecard_cs.c +++ /dev/null @@ -1,908 +0,0 @@ -/* - * - * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041) - * - * Copyright (C) 2001-2002 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - - - -/* ======================== Module parameters ======================== */ - - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)"); -MODULE_LICENSE("GPL"); - - - -/* ======================== Local structures ======================== */ - - -struct bluecard_info { - struct pcmcia_device *p_dev; - - struct hci_dev *hdev; - - spinlock_t lock; /* For serializing operations */ - struct timer_list timer; /* For LED control */ - - struct sk_buff_head txq; - unsigned long tx_state; - - unsigned long rx_state; - unsigned long rx_count; - struct sk_buff *rx_skb; - - unsigned char ctrl_reg; - unsigned long hw_state; /* Status of the hardware and LED control */ -}; - - -static int bluecard_config(struct pcmcia_device *link); -static void bluecard_release(struct pcmcia_device *link); - -static void bluecard_detach(struct pcmcia_device *p_dev); - - -/* Default baud rate: 57600, 115200, 230400 or 460800 */ -#define DEFAULT_BAUD_RATE 230400 - - -/* Hardware states */ -#define CARD_READY 1 -#define CARD_ACTIVITY 2 -#define CARD_HAS_PCCARD_ID 4 -#define CARD_HAS_POWER_LED 5 -#define CARD_HAS_ACTIVITY_LED 6 - -/* Transmit states */ -#define XMIT_SENDING 1 -#define XMIT_WAKEUP 2 -#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */ -#define XMIT_BUF_ONE_READY 6 -#define XMIT_BUF_TWO_READY 7 -#define XMIT_SENDING_READY 8 - -/* Receiver states */ -#define RECV_WAIT_PACKET_TYPE 0 -#define RECV_WAIT_EVENT_HEADER 1 -#define RECV_WAIT_ACL_HEADER 2 -#define RECV_WAIT_SCO_HEADER 3 -#define RECV_WAIT_DATA 4 - -/* Special packet types */ -#define PKT_BAUD_RATE_57600 0x80 -#define PKT_BAUD_RATE_115200 0x81 -#define PKT_BAUD_RATE_230400 0x82 -#define PKT_BAUD_RATE_460800 0x83 - - -/* These are the register offsets */ -#define REG_COMMAND 0x20 -#define REG_INTERRUPT 0x21 -#define REG_CONTROL 0x22 -#define REG_RX_CONTROL 0x24 -#define REG_CARD_RESET 0x30 -#define REG_LED_CTRL 0x30 - -/* REG_COMMAND */ -#define REG_COMMAND_TX_BUF_ONE 0x01 -#define REG_COMMAND_TX_BUF_TWO 0x02 -#define REG_COMMAND_RX_BUF_ONE 0x04 -#define REG_COMMAND_RX_BUF_TWO 0x08 -#define REG_COMMAND_RX_WIN_ONE 0x00 -#define REG_COMMAND_RX_WIN_TWO 0x10 - -/* REG_CONTROL */ -#define REG_CONTROL_BAUD_RATE_57600 0x00 -#define REG_CONTROL_BAUD_RATE_115200 0x01 -#define REG_CONTROL_BAUD_RATE_230400 0x02 -#define REG_CONTROL_BAUD_RATE_460800 0x03 -#define REG_CONTROL_RTS 0x04 -#define REG_CONTROL_BT_ON 0x08 -#define REG_CONTROL_BT_RESET 0x10 -#define REG_CONTROL_BT_RES_PU 0x20 -#define REG_CONTROL_INTERRUPT 0x40 -#define REG_CONTROL_CARD_RESET 0x80 - -/* REG_RX_CONTROL */ -#define RTS_LEVEL_SHIFT_BITS 0x02 - - - -/* ======================== LED handling routines ======================== */ - - -static void bluecard_activity_led_timeout(struct timer_list *t) -{ - struct bluecard_info *info = timer_container_of(info, t, timer); - unsigned int iobase = info->p_dev->resource[0]->start; - - if (test_bit(CARD_ACTIVITY, &(info->hw_state))) { - /* leave LED in inactive state for HZ/10 for blink effect */ - clear_bit(CARD_ACTIVITY, &(info->hw_state)); - mod_timer(&(info->timer), jiffies + HZ / 10); - } - - /* Disable activity LED, enable power LED */ - outb(0x08 | 0x20, iobase + 0x30); -} - - -static void bluecard_enable_activity_led(struct bluecard_info *info) -{ - unsigned int iobase = info->p_dev->resource[0]->start; - - /* don't disturb running blink timer */ - if (timer_pending(&(info->timer))) - return; - - set_bit(CARD_ACTIVITY, &(info->hw_state)); - - if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) { - /* Enable activity LED, keep power LED enabled */ - outb(0x18 | 0x60, iobase + 0x30); - } else { - /* Disable power LED */ - outb(0x00, iobase + 0x30); - } - - /* Stop the LED after HZ/10 */ - mod_timer(&(info->timer), jiffies + HZ / 10); -} - - - -/* ======================== Interrupt handling ======================== */ - - -static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len) -{ - int i, actual; - - actual = (len > 15) ? 15 : len; - - outb_p(actual, iobase + offset); - - for (i = 0; i < actual; i++) - outb_p(buf[i], iobase + offset + i + 1); - - return actual; -} - - -static void bluecard_write_wakeup(struct bluecard_info *info) -{ - if (!info) { - BT_ERR("Unknown device"); - return; - } - - if (!test_bit(XMIT_SENDING_READY, &(info->tx_state))) - return; - - if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { - set_bit(XMIT_WAKEUP, &(info->tx_state)); - return; - } - - do { - unsigned int iobase = info->p_dev->resource[0]->start; - unsigned int offset; - unsigned char command; - unsigned long ready_bit; - register struct sk_buff *skb; - int len; - - clear_bit(XMIT_WAKEUP, &(info->tx_state)); - - if (!pcmcia_dev_present(info->p_dev)) - return; - - if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) { - if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state))) - break; - offset = 0x10; - command = REG_COMMAND_TX_BUF_TWO; - ready_bit = XMIT_BUF_TWO_READY; - } else { - if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state))) - break; - offset = 0x00; - command = REG_COMMAND_TX_BUF_ONE; - ready_bit = XMIT_BUF_ONE_READY; - } - - skb = skb_dequeue(&(info->txq)); - if (!skb) - break; - - if (hci_skb_pkt_type(skb) & 0x80) { - /* Disable RTS */ - info->ctrl_reg |= REG_CONTROL_RTS; - outb(info->ctrl_reg, iobase + REG_CONTROL); - } - - /* Activate LED */ - bluecard_enable_activity_led(info); - - /* Send frame */ - len = bluecard_write(iobase, offset, skb->data, skb->len); - - /* Tell the FPGA to send the data */ - outb_p(command, iobase + REG_COMMAND); - - /* Mark the buffer as dirty */ - clear_bit(ready_bit, &(info->tx_state)); - - if (hci_skb_pkt_type(skb) & 0x80) { - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); - DEFINE_WAIT(wait); - - unsigned char baud_reg; - - switch (hci_skb_pkt_type(skb)) { - case PKT_BAUD_RATE_460800: - baud_reg = REG_CONTROL_BAUD_RATE_460800; - break; - case PKT_BAUD_RATE_230400: - baud_reg = REG_CONTROL_BAUD_RATE_230400; - break; - case PKT_BAUD_RATE_115200: - baud_reg = REG_CONTROL_BAUD_RATE_115200; - break; - case PKT_BAUD_RATE_57600: - default: - baud_reg = REG_CONTROL_BAUD_RATE_57600; - break; - } - - /* Wait until the command reaches the baseband */ - mdelay(100); - - /* Set baud on baseband */ - info->ctrl_reg &= ~0x03; - info->ctrl_reg |= baud_reg; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Enable RTS */ - info->ctrl_reg &= ~REG_CONTROL_RTS; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Wait before the next HCI packet can be send */ - mdelay(1000); - } - - if (len == skb->len) { - kfree_skb(skb); - } else { - skb_pull(skb, len); - skb_queue_head(&(info->txq), skb); - } - - info->hdev->stat.byte_tx += len; - - /* Change buffer */ - change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state)); - - } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); - - clear_bit(XMIT_SENDING, &(info->tx_state)); -} - - -static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size) -{ - int i, n, len; - - outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND); - - len = inb(iobase + offset); - n = 0; - i = 1; - - while (n < len) { - - if (i == 16) { - outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND); - i = 0; - } - - buf[n] = inb(iobase + offset + i); - - n++; - i++; - - } - - return len; -} - - -static void bluecard_receive(struct bluecard_info *info, - unsigned int offset) -{ - unsigned int iobase; - unsigned char buf[31]; - int i, len; - - if (!info) { - BT_ERR("Unknown device"); - return; - } - - iobase = info->p_dev->resource[0]->start; - - if (test_bit(XMIT_SENDING_READY, &(info->tx_state))) - bluecard_enable_activity_led(info); - - len = bluecard_read(iobase, offset, buf, sizeof(buf)); - - for (i = 0; i < len; i++) { - - /* Allocate packet */ - if (!info->rx_skb) { - info->rx_state = RECV_WAIT_PACKET_TYPE; - info->rx_count = 0; - info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!info->rx_skb) { - BT_ERR("Can't allocate mem for new packet"); - return; - } - } - - if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - - hci_skb_pkt_type(info->rx_skb) = buf[i]; - - switch (hci_skb_pkt_type(info->rx_skb)) { - - case 0x00: - /* init packet */ - if (offset != 0x00) { - set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); - set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); - set_bit(XMIT_SENDING_READY, &(info->tx_state)); - bluecard_write_wakeup(info); - } - - kfree_skb(info->rx_skb); - info->rx_skb = NULL; - break; - - case HCI_EVENT_PKT: - info->rx_state = RECV_WAIT_EVENT_HEADER; - info->rx_count = HCI_EVENT_HDR_SIZE; - break; - - case HCI_ACLDATA_PKT: - info->rx_state = RECV_WAIT_ACL_HEADER; - info->rx_count = HCI_ACL_HDR_SIZE; - break; - - case HCI_SCODATA_PKT: - info->rx_state = RECV_WAIT_SCO_HEADER; - info->rx_count = HCI_SCO_HDR_SIZE; - break; - - default: - /* unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", - hci_skb_pkt_type(info->rx_skb)); - info->hdev->stat.err_rx++; - - kfree_skb(info->rx_skb); - info->rx_skb = NULL; - break; - - } - - } else { - - skb_put_u8(info->rx_skb, buf[i]); - info->rx_count--; - - if (info->rx_count == 0) { - - int dlen; - struct hci_event_hdr *eh; - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - - switch (info->rx_state) { - - case RECV_WAIT_EVENT_HEADER: - eh = hci_event_hdr(info->rx_skb); - info->rx_state = RECV_WAIT_DATA; - info->rx_count = eh->plen; - break; - - case RECV_WAIT_ACL_HEADER: - ah = hci_acl_hdr(info->rx_skb); - dlen = __le16_to_cpu(ah->dlen); - info->rx_state = RECV_WAIT_DATA; - info->rx_count = dlen; - break; - - case RECV_WAIT_SCO_HEADER: - sh = hci_sco_hdr(info->rx_skb); - info->rx_state = RECV_WAIT_DATA; - info->rx_count = sh->dlen; - break; - - case RECV_WAIT_DATA: - hci_recv_frame(info->hdev, info->rx_skb); - info->rx_skb = NULL; - break; - - } - - } - - } - - - } - - info->hdev->stat.byte_rx += len; -} - - -static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) -{ - struct bluecard_info *info = dev_inst; - unsigned int iobase; - unsigned char reg; - - if (!info || !info->hdev) - /* our irq handler is shared */ - return IRQ_NONE; - - if (!test_bit(CARD_READY, &(info->hw_state))) - return IRQ_HANDLED; - - iobase = info->p_dev->resource[0]->start; - - spin_lock(&(info->lock)); - - /* Disable interrupt */ - info->ctrl_reg &= ~REG_CONTROL_INTERRUPT; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - reg = inb(iobase + REG_INTERRUPT); - - if ((reg != 0x00) && (reg != 0xff)) { - - if (reg & 0x04) { - bluecard_receive(info, 0x00); - outb(0x04, iobase + REG_INTERRUPT); - outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND); - } - - if (reg & 0x08) { - bluecard_receive(info, 0x10); - outb(0x08, iobase + REG_INTERRUPT); - outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND); - } - - if (reg & 0x01) { - set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); - outb(0x01, iobase + REG_INTERRUPT); - bluecard_write_wakeup(info); - } - - if (reg & 0x02) { - set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); - outb(0x02, iobase + REG_INTERRUPT); - bluecard_write_wakeup(info); - } - - } - - /* Enable interrupt */ - info->ctrl_reg |= REG_CONTROL_INTERRUPT; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - spin_unlock(&(info->lock)); - - return IRQ_HANDLED; -} - - - -/* ======================== Device specific HCI commands ======================== */ - - -static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) -{ - struct bluecard_info *info = hci_get_drvdata(hdev); - struct sk_buff *skb; - - /* Ericsson baud rate command */ - unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 }; - - skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_KERNEL); - if (!skb) { - BT_ERR("Can't allocate mem for new packet"); - return -1; - } - - switch (baud) { - case 460800: - cmd[4] = 0x00; - hci_skb_pkt_type(skb) = PKT_BAUD_RATE_460800; - break; - case 230400: - cmd[4] = 0x01; - hci_skb_pkt_type(skb) = PKT_BAUD_RATE_230400; - break; - case 115200: - cmd[4] = 0x02; - hci_skb_pkt_type(skb) = PKT_BAUD_RATE_115200; - break; - case 57600: - default: - cmd[4] = 0x03; - hci_skb_pkt_type(skb) = PKT_BAUD_RATE_57600; - break; - } - - skb_put_data(skb, cmd, sizeof(cmd)); - - skb_queue_tail(&(info->txq), skb); - - bluecard_write_wakeup(info); - - return 0; -} - - - -/* ======================== HCI interface ======================== */ - - -static int bluecard_hci_flush(struct hci_dev *hdev) -{ - struct bluecard_info *info = hci_get_drvdata(hdev); - - /* Drop TX queue */ - skb_queue_purge(&(info->txq)); - - return 0; -} - - -static int bluecard_hci_open(struct hci_dev *hdev) -{ - struct bluecard_info *info = hci_get_drvdata(hdev); - unsigned int iobase = info->p_dev->resource[0]->start; - - if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) - bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); - - /* Enable power LED */ - outb(0x08 | 0x20, iobase + 0x30); - - return 0; -} - - -static int bluecard_hci_close(struct hci_dev *hdev) -{ - struct bluecard_info *info = hci_get_drvdata(hdev); - unsigned int iobase = info->p_dev->resource[0]->start; - - bluecard_hci_flush(hdev); - - /* Stop LED timer */ - timer_delete_sync(&(info->timer)); - - /* Disable power LED */ - outb(0x00, iobase + 0x30); - - return 0; -} - - -static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct bluecard_info *info = hci_get_drvdata(hdev); - - switch (hci_skb_pkt_type(skb)) { - case HCI_COMMAND_PKT: - hdev->stat.cmd_tx++; - break; - case HCI_ACLDATA_PKT: - hdev->stat.acl_tx++; - break; - case HCI_SCODATA_PKT: - hdev->stat.sco_tx++; - break; - } - - /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); - skb_queue_tail(&(info->txq), skb); - - bluecard_write_wakeup(info); - - return 0; -} - - - -/* ======================== Card services HCI interaction ======================== */ - - -static int bluecard_open(struct bluecard_info *info) -{ - unsigned int iobase = info->p_dev->resource[0]->start; - struct hci_dev *hdev; - unsigned char id; - - spin_lock_init(&(info->lock)); - - timer_setup(&info->timer, bluecard_activity_led_timeout, 0); - - skb_queue_head_init(&(info->txq)); - - info->rx_state = RECV_WAIT_PACKET_TYPE; - info->rx_count = 0; - info->rx_skb = NULL; - - /* Initialize HCI device */ - hdev = hci_alloc_dev(); - if (!hdev) { - BT_ERR("Can't allocate HCI device"); - return -ENOMEM; - } - - info->hdev = hdev; - - hdev->bus = HCI_PCCARD; - hci_set_drvdata(hdev, info); - SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - - hdev->open = bluecard_hci_open; - hdev->close = bluecard_hci_close; - hdev->flush = bluecard_hci_flush; - hdev->send = bluecard_hci_send_frame; - - id = inb(iobase + 0x30); - - if ((id & 0x0f) == 0x02) - set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)); - - if (id & 0x10) - set_bit(CARD_HAS_POWER_LED, &(info->hw_state)); - - if (id & 0x20) - set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state)); - - /* Reset card */ - info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Turn FPGA off */ - outb(0x80, iobase + 0x30); - - /* Wait some time */ - msleep(10); - - /* Turn FPGA on */ - outb(0x00, iobase + 0x30); - - /* Activate card */ - info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Enable interrupt */ - outb(0xff, iobase + REG_INTERRUPT); - info->ctrl_reg |= REG_CONTROL_INTERRUPT; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - if ((id & 0x0f) == 0x03) { - /* Disable RTS */ - info->ctrl_reg |= REG_CONTROL_RTS; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Set baud rate */ - info->ctrl_reg |= 0x03; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Enable RTS */ - info->ctrl_reg &= ~REG_CONTROL_RTS; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); - set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); - set_bit(XMIT_SENDING_READY, &(info->tx_state)); - } - - /* Start the RX buffers */ - outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND); - outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND); - - /* Signal that the hardware is ready */ - set_bit(CARD_READY, &(info->hw_state)); - - /* Drop TX queue */ - skb_queue_purge(&(info->txq)); - - /* Control the point at which RTS is enabled */ - outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL); - - /* Timeout before it is safe to send the first HCI packet */ - msleep(1250); - - /* Register HCI device */ - if (hci_register_dev(hdev) < 0) { - BT_ERR("Can't register HCI device"); - info->hdev = NULL; - hci_free_dev(hdev); - return -ENODEV; - } - - return 0; -} - - -static int bluecard_close(struct bluecard_info *info) -{ - unsigned int iobase = info->p_dev->resource[0]->start; - struct hci_dev *hdev = info->hdev; - - if (!hdev) - return -ENODEV; - - bluecard_hci_close(hdev); - - clear_bit(CARD_READY, &(info->hw_state)); - - /* Reset card */ - info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET; - outb(info->ctrl_reg, iobase + REG_CONTROL); - - /* Turn FPGA off */ - outb(0x80, iobase + 0x30); - - hci_unregister_dev(hdev); - hci_free_dev(hdev); - - return 0; -} - -static int bluecard_probe(struct pcmcia_device *link) -{ - struct bluecard_info *info; - - /* Create new info device */ - info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->p_dev = link; - link->priv = info; - - link->config_flags |= CONF_ENABLE_IRQ; - - return bluecard_config(link); -} - - -static void bluecard_detach(struct pcmcia_device *link) -{ - bluecard_release(link); -} - - -static int bluecard_config(struct pcmcia_device *link) -{ - struct bluecard_info *info = link->priv; - int i, n; - - link->config_index = 0x20; - - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[0]->end = 64; - link->io_lines = 6; - - for (n = 0; n < 0x400; n += 0x40) { - link->resource[0]->start = n ^ 0x300; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - - if (i != 0) - goto failed; - - i = pcmcia_request_irq(link, bluecard_interrupt); - if (i != 0) - goto failed; - - i = pcmcia_enable_device(link); - if (i != 0) - goto failed; - - if (bluecard_open(info) != 0) - goto failed; - - return 0; - -failed: - bluecard_release(link); - return -ENODEV; -} - - -static void bluecard_release(struct pcmcia_device *link) -{ - struct bluecard_info *info = link->priv; - - bluecard_close(info); - - timer_delete_sync(&(info->timer)); - - pcmcia_disable_device(link); -} - -static const struct pcmcia_device_id bluecard_ids[] = { - PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e), - PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c), - PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, bluecard_ids); - -static struct pcmcia_driver bluecard_driver = { - .owner = THIS_MODULE, - .name = "bluecard_cs", - .probe = bluecard_probe, - .remove = bluecard_detach, - .id_table = bluecard_ids, -}; -module_pcmcia_driver(bluecard_driver); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c deleted file mode 100644 index 54713833951a..000000000000 --- a/drivers/bluetooth/bt3c_cs.c +++ /dev/null @@ -1,749 +0,0 @@ -/* - * - * Driver for the 3Com Bluetooth PCMCIA card - * - * Copyright (C) 2001-2002 Marcel Holtmann - * Jose Orlando Pereira - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - - - -/* ======================== Module parameters ======================== */ - - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("BT3CPCC.bin"); - - - -/* ======================== Local structures ======================== */ - - -struct bt3c_info { - struct pcmcia_device *p_dev; - - struct hci_dev *hdev; - - spinlock_t lock; /* For serializing operations */ - - struct sk_buff_head txq; - unsigned long tx_state; - - unsigned long rx_state; - unsigned long rx_count; - struct sk_buff *rx_skb; -}; - - -static int bt3c_config(struct pcmcia_device *link); -static void bt3c_release(struct pcmcia_device *link); - -static void bt3c_detach(struct pcmcia_device *p_dev); - - -/* Transmit states */ -#define XMIT_SENDING 1 -#define XMIT_WAKEUP 2 -#define XMIT_WAITING 8 - -/* Receiver states */ -#define RECV_WAIT_PACKET_TYPE 0 -#define RECV_WAIT_EVENT_HEADER 1 -#define RECV_WAIT_ACL_HEADER 2 -#define RECV_WAIT_SCO_HEADER 3 -#define RECV_WAIT_DATA 4 - - - -/* ======================== Special I/O functions ======================== */ - - -#define DATA_L 0 -#define DATA_H 1 -#define ADDR_L 2 -#define ADDR_H 3 -#define CONTROL 4 - - -static inline void bt3c_address(unsigned int iobase, unsigned short addr) -{ - outb(addr & 0xff, iobase + ADDR_L); - outb((addr >> 8) & 0xff, iobase + ADDR_H); -} - - -static inline void bt3c_put(unsigned int iobase, unsigned short value) -{ - outb(value & 0xff, iobase + DATA_L); - outb((value >> 8) & 0xff, iobase + DATA_H); -} - - -static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value) -{ - bt3c_address(iobase, addr); - bt3c_put(iobase, value); -} - - -static inline unsigned short bt3c_get(unsigned int iobase) -{ - unsigned short value = inb(iobase + DATA_L); - - value |= inb(iobase + DATA_H) << 8; - - return value; -} - - -static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr) -{ - bt3c_address(iobase, addr); - - return bt3c_get(iobase); -} - - - -/* ======================== Interrupt handling ======================== */ - - -static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) -{ - int actual = 0; - - bt3c_address(iobase, 0x7080); - - /* Fill FIFO with current frame */ - while (actual < len) { - /* Transmit next byte */ - bt3c_put(iobase, buf[actual]); - actual++; - } - - bt3c_io_write(iobase, 0x7005, actual); - - return actual; -} - - -static void bt3c_write_wakeup(struct bt3c_info *info) -{ - if (!info) { - BT_ERR("Unknown device"); - return; - } - - if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) - return; - - do { - unsigned int iobase = info->p_dev->resource[0]->start; - register struct sk_buff *skb; - int len; - - if (!pcmcia_dev_present(info->p_dev)) - break; - - skb = skb_dequeue(&(info->txq)); - if (!skb) { - clear_bit(XMIT_SENDING, &(info->tx_state)); - break; - } - - /* Send frame */ - len = bt3c_write(iobase, 256, skb->data, skb->len); - - if (len != skb->len) - BT_ERR("Very strange"); - - kfree_skb(skb); - - info->hdev->stat.byte_tx += len; - - } while (0); -} - - -static void bt3c_receive(struct bt3c_info *info) -{ - unsigned int iobase; - int size = 0, avail; - - if (!info) { - BT_ERR("Unknown device"); - return; - } - - iobase = info->p_dev->resource[0]->start; - - avail = bt3c_read(iobase, 0x7006); - - bt3c_address(iobase, 0x7480); - while (size < avail) { - size++; - info->hdev->stat.byte_rx++; - - /* Allocate packet */ - if (!info->rx_skb) { - info->rx_state = RECV_WAIT_PACKET_TYPE; - info->rx_count = 0; - info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!info->rx_skb) { - BT_ERR("Can't allocate mem for new packet"); - return; - } - } - - - if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - - hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L); - inb(iobase + DATA_H); - - switch (hci_skb_pkt_type(info->rx_skb)) { - - case HCI_EVENT_PKT: - info->rx_state = RECV_WAIT_EVENT_HEADER; - info->rx_count = HCI_EVENT_HDR_SIZE; - break; - - case HCI_ACLDATA_PKT: - info->rx_state = RECV_WAIT_ACL_HEADER; - info->rx_count = HCI_ACL_HDR_SIZE; - break; - - case HCI_SCODATA_PKT: - info->rx_state = RECV_WAIT_SCO_HEADER; - info->rx_count = HCI_SCO_HDR_SIZE; - break; - - default: - /* Unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", - hci_skb_pkt_type(info->rx_skb)); - info->hdev->stat.err_rx++; - - kfree_skb(info->rx_skb); - info->rx_skb = NULL; - break; - - } - - } else { - - __u8 x = inb(iobase + DATA_L); - - skb_put_u8(info->rx_skb, x); - inb(iobase + DATA_H); - info->rx_count--; - - if (info->rx_count == 0) { - - int dlen; - struct hci_event_hdr *eh; - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - - switch (info->rx_state) { - - case RECV_WAIT_EVENT_HEADER: - eh = hci_event_hdr(info->rx_skb); - info->rx_state = RECV_WAIT_DATA; - info->rx_count = eh->plen; - break; - - case RECV_WAIT_ACL_HEADER: - ah = hci_acl_hdr(info->rx_skb); - dlen = __le16_to_cpu(ah->dlen); - info->rx_state = RECV_WAIT_DATA; - info->rx_count = dlen; - break; - - case RECV_WAIT_SCO_HEADER: - sh = hci_sco_hdr(info->rx_skb); - info->rx_state = RECV_WAIT_DATA; - info->rx_count = sh->dlen; - break; - - case RECV_WAIT_DATA: - hci_recv_frame(info->hdev, info->rx_skb); - info->rx_skb = NULL; - break; - - } - - } - - } - - } - - bt3c_io_write(iobase, 0x7006, 0x0000); -} - - -static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) -{ - struct bt3c_info *info = dev_inst; - unsigned int iobase; - int iir; - irqreturn_t r = IRQ_NONE; - - if (!info || !info->hdev) - /* our irq handler is shared */ - return IRQ_NONE; - - iobase = info->p_dev->resource[0]->start; - - spin_lock(&(info->lock)); - - iir = inb(iobase + CONTROL); - if (iir & 0x80) { - int stat = bt3c_read(iobase, 0x7001); - - if ((stat & 0xff) == 0x7f) { - BT_ERR("Very strange (stat=0x%04x)", stat); - } else if ((stat & 0xff) != 0xff) { - if (stat & 0x0020) { - int status = bt3c_read(iobase, 0x7002) & 0x10; - bt_dev_info(info->hdev, "Antenna %s", - status ? "out" : "in"); - } - if (stat & 0x0001) - bt3c_receive(info); - if (stat & 0x0002) { - clear_bit(XMIT_SENDING, &(info->tx_state)); - bt3c_write_wakeup(info); - } - - bt3c_io_write(iobase, 0x7001, 0x0000); - - outb(iir, iobase + CONTROL); - } - r = IRQ_HANDLED; - } - - spin_unlock(&(info->lock)); - - return r; -} - - - -/* ======================== HCI interface ======================== */ - - -static int bt3c_hci_flush(struct hci_dev *hdev) -{ - struct bt3c_info *info = hci_get_drvdata(hdev); - - /* Drop TX queue */ - skb_queue_purge(&(info->txq)); - - return 0; -} - - -static int bt3c_hci_open(struct hci_dev *hdev) -{ - return 0; -} - - -static int bt3c_hci_close(struct hci_dev *hdev) -{ - bt3c_hci_flush(hdev); - - return 0; -} - - -static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct bt3c_info *info = hci_get_drvdata(hdev); - unsigned long flags; - - switch (hci_skb_pkt_type(skb)) { - case HCI_COMMAND_PKT: - hdev->stat.cmd_tx++; - break; - case HCI_ACLDATA_PKT: - hdev->stat.acl_tx++; - break; - case HCI_SCODATA_PKT: - hdev->stat.sco_tx++; - break; - } - - /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); - skb_queue_tail(&(info->txq), skb); - - spin_lock_irqsave(&(info->lock), flags); - - bt3c_write_wakeup(info); - - spin_unlock_irqrestore(&(info->lock), flags); - - return 0; -} - - - -/* ======================== Card services HCI interaction ======================== */ - - -static int bt3c_load_firmware(struct bt3c_info *info, - const unsigned char *firmware, - int count) -{ - char *ptr = (char *) firmware; - char b[9]; - unsigned int iobase, tmp, tn; - unsigned long size, addr, fcs; - int i, err = 0; - - iobase = info->p_dev->resource[0]->start; - - /* Reset */ - bt3c_io_write(iobase, 0x8040, 0x0404); - bt3c_io_write(iobase, 0x8040, 0x0400); - - udelay(1); - - bt3c_io_write(iobase, 0x8040, 0x0404); - - udelay(17); - - /* Load */ - while (count) { - if (ptr[0] != 'S') { - BT_ERR("Bad address in firmware"); - err = -EFAULT; - goto error; - } - - memset(b, 0, sizeof(b)); - memcpy(b, ptr + 2, 2); - if (kstrtoul(b, 16, &size) < 0) - return -EINVAL; - - memset(b, 0, sizeof(b)); - memcpy(b, ptr + 4, 8); - if (kstrtoul(b, 16, &addr) < 0) - return -EINVAL; - - memset(b, 0, sizeof(b)); - memcpy(b, ptr + (size * 2) + 2, 2); - if (kstrtoul(b, 16, &fcs) < 0) - return -EINVAL; - - memset(b, 0, sizeof(b)); - for (tmp = 0, i = 0; i < size; i++) { - memcpy(b, ptr + (i * 2) + 2, 2); - if (kstrtouint(b, 16, &tn)) - return -EINVAL; - tmp += tn; - } - - if (((tmp + fcs) & 0xff) != 0xff) { - BT_ERR("Checksum error in firmware"); - err = -EILSEQ; - goto error; - } - - if (ptr[1] == '3') { - bt3c_address(iobase, addr); - - memset(b, 0, sizeof(b)); - for (i = 0; i < (size - 4) / 2; i++) { - memcpy(b, ptr + (i * 4) + 12, 4); - if (kstrtouint(b, 16, &tmp)) - return -EINVAL; - bt3c_put(iobase, tmp); - } - } - - ptr += (size * 2) + 6; - count -= (size * 2) + 6; - } - - udelay(17); - - /* Boot */ - bt3c_address(iobase, 0x3000); - outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL); - -error: - udelay(17); - - /* Clear */ - bt3c_io_write(iobase, 0x7006, 0x0000); - bt3c_io_write(iobase, 0x7005, 0x0000); - bt3c_io_write(iobase, 0x7001, 0x0000); - - return err; -} - - -static int bt3c_open(struct bt3c_info *info) -{ - const struct firmware *firmware; - struct hci_dev *hdev; - int err; - - spin_lock_init(&(info->lock)); - - skb_queue_head_init(&(info->txq)); - - info->rx_state = RECV_WAIT_PACKET_TYPE; - info->rx_count = 0; - info->rx_skb = NULL; - - /* Initialize HCI device */ - hdev = hci_alloc_dev(); - if (!hdev) { - BT_ERR("Can't allocate HCI device"); - return -ENOMEM; - } - - info->hdev = hdev; - - hdev->bus = HCI_PCCARD; - hci_set_drvdata(hdev, info); - SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - - hdev->open = bt3c_hci_open; - hdev->close = bt3c_hci_close; - hdev->flush = bt3c_hci_flush; - hdev->send = bt3c_hci_send_frame; - - /* Load firmware */ - err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev); - if (err < 0) { - BT_ERR("Firmware request failed"); - goto error; - } - - err = bt3c_load_firmware(info, firmware->data, firmware->size); - - release_firmware(firmware); - - if (err < 0) { - BT_ERR("Firmware loading failed"); - goto error; - } - - /* Timeout before it is safe to send the first HCI packet */ - msleep(1000); - - /* Register HCI device */ - err = hci_register_dev(hdev); - if (err < 0) { - BT_ERR("Can't register HCI device"); - goto error; - } - - return 0; - -error: - info->hdev = NULL; - hci_free_dev(hdev); - return err; -} - - -static int bt3c_close(struct bt3c_info *info) -{ - struct hci_dev *hdev = info->hdev; - - if (!hdev) - return -ENODEV; - - bt3c_hci_close(hdev); - - hci_unregister_dev(hdev); - hci_free_dev(hdev); - - return 0; -} - -static int bt3c_probe(struct pcmcia_device *link) -{ - struct bt3c_info *info; - - /* Create new info device */ - info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->p_dev = link; - link->priv = info; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_SET_IO; - - return bt3c_config(link); -} - - -static void bt3c_detach(struct pcmcia_device *link) -{ - bt3c_release(link); -} - -static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data) -{ - int *try = priv_data; - - if (!try) - p_dev->io_lines = 16; - - if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) - return -EINVAL; - - p_dev->resource[0]->end = 8; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - return pcmcia_request_io(p_dev); -} - -static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, - void *priv_data) -{ - static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - int j; - - if (p_dev->io_lines > 3) - return -ENODEV; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 8; - - for (j = 0; j < 5; j++) { - p_dev->resource[0]->start = base[j]; - p_dev->io_lines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; -} - -static int bt3c_config(struct pcmcia_device *link) -{ - struct bt3c_info *info = link->priv; - int i; - unsigned long try; - - /* First pass: look for a config entry that looks normal. - * Two tries: without IO aliases, then with aliases - */ - for (try = 0; try < 2; try++) - if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try)) - goto found_port; - - /* Second pass: try to find an entry that isn't picky about - * its base address, then try to grab any standard serial port - * address, and finally try to get any free port. - */ - if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL)) - goto found_port; - - BT_ERR("No usable port range found"); - goto failed; - -found_port: - i = pcmcia_request_irq(link, &bt3c_interrupt); - if (i != 0) - goto failed; - - i = pcmcia_enable_device(link); - if (i != 0) - goto failed; - - if (bt3c_open(info) != 0) - goto failed; - - return 0; - -failed: - bt3c_release(link); - return -ENODEV; -} - - -static void bt3c_release(struct pcmcia_device *link) -{ - struct bt3c_info *info = link->priv; - - bt3c_close(info); - - pcmcia_disable_device(link); -} - - -static const struct pcmcia_device_id bt3c_ids[] = { - PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, bt3c_ids); - -static struct pcmcia_driver bt3c_driver = { - .owner = THIS_MODULE, - .name = "bt3c_cs", - .probe = bt3c_probe, - .remove = bt3c_detach, - .id_table = bt3c_ids, -}; -module_pcmcia_driver(bt3c_driver); diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index dcaaa4ca02b9..5e9cac090bd8 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -67,9 +67,10 @@ static struct { u32 fw_build_num; } coredump_info; -static const guid_t btintel_guid_dsm = +const guid_t btintel_guid_dsm = GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233, 0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9); +EXPORT_SYMBOL_GPL(btintel_guid_dsm); int btintel_check_bdaddr(struct hci_dev *hdev) { @@ -2624,7 +2625,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver kfree_skb(skb); } -static int btintel_acpi_reset_method(struct hci_dev *hdev) +int btintel_acpi_reset_method(struct hci_dev *hdev) { int ret = 0; acpi_status status; @@ -2632,14 +2633,14 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer); - if (ACPI_FAILURE(status)) { + if (ACPI_FAILURE(status) || !buffer.pointer) { bt_dev_err(hdev, "Failed to run _PRR method"); ret = -ENODEV; return ret; } p = buffer.pointer; - if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) { + if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 1) { bt_dev_err(hdev, "Invalid arguments"); ret = -EINVAL; goto exit_on_error; @@ -2663,6 +2664,7 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev) kfree(buffer.pointer); return ret; } +EXPORT_SYMBOL_GPL(btintel_acpi_reset_method); static void btintel_set_dsm_reset_method(struct hci_dev *hdev, struct intel_version_tlv *ver_tlv) diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 0e9ca99aaaae..37d93abdd5a3 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -53,6 +53,7 @@ struct intel_tlv { } __packed; #define BTINTEL_HCI_OP_RESET 0xfc01 +#define BTINTEL_HCI_OP_DEBUG 0xfcd9 #define BTINTEL_CNVI_BLAZARI 0x900 /* BlazarI - Lunar Lake */ #define BTINTEL_CNVI_BLAZARIW 0x901 /* BlazarIW - Wildcat Lake */ @@ -77,8 +78,11 @@ struct intel_tlv { #define BTINTEL_HWID_BZRU 0x1d /* BlazarU - Meteor Lake */ #define BTINTEL_HWID_SCP 0x1f /* Scorpius Peak - Panther Lake */ #define BTINTEL_HWID_SCP2 0x20 /* Scorpius Peak2 - Nova Lake */ +#define BTINTEL_HWID_SCP2F 0x21 /* Scorpius Peak2-F - Nova Lake */ #define BTINTEL_HWID_BZRIW 0x22 /* BlazarIW - Wildcat Lake */ +extern const guid_t btintel_guid_dsm; + struct intel_version_tlv { u32 cnvi_top; u32 cnvr_top; @@ -289,6 +293,7 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev, int btintel_shutdown_combined(struct hci_dev *hdev); void btintel_hw_error(struct hci_dev *hdev, u8 code); void btintel_print_fseq_info(struct hci_dev *hdev); +int btintel_acpi_reset_method(struct hci_dev *hdev); #else static inline int btintel_check_bdaddr(struct hci_dev *hdev) @@ -422,4 +427,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code) static inline void btintel_print_fseq_info(struct hci_dev *hdev) { } +static inline int btintel_acpi_reset_method(struct hci_dev *hdev) +{ + return -ENODEV; +} #endif diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 37e050763633..9e39327dc1fe 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,22 @@ enum { BTINTEL_PCIE_D3 }; +enum { + BTINTEL_PCIE_DSM_SET_RESET_TIMING = 1, + BTINTEL_PCIE_DSM_GET_RESET_TIMING = 2, + BTINTEL_PCIE_DSM_BT_PLDR_CONFIG = 3, + BTINTEL_PCIE_DSM_GET_RESET_TYPE = 4, + BTINTEL_PCIE_DSM_DYNAMIC_PLDR = 5, + BTINTEL_PCIE_DSM_GET_RESET_METHOD = 6, + BTINTEL_PCIE_DSM_SET_PLDR_DELAY = 7, +}; + +enum btintel_dsm_internal_product_reset_mode { + BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET = BIT(0), + BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR = BIT(1), + BTINTEL_PCIE_DSM_PLDR_MODE_EN_BT_OFF_ON = BIT(2), +}; + /* Structure for dbgc fragment buffer * @buf_addr_lsb: LSB of the buffer's physical address * @buf_addr_msb: MSB of the buffer's physical address @@ -128,10 +145,21 @@ struct btintel_pcie_dbgc_ctxt { struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT]; }; -struct btintel_pcie_removal { - struct pci_dev *pdev; - struct work_struct work; -}; +struct btintel_pcie_trigger_evt { + u8 type; + u8 len; + __le32 addr; + __le32 size; +} __packed; + +struct btintel_pcie_fwtrigger_evt { + __le32 reserved; + u8 type; /* Debug Trigger event */ + __le16 len; + u8 event_type; + __le16 event_id; + __le16 reserved2; +} __packed; static LIST_HEAD(btintel_pcie_recovery_list); static DEFINE_SPINLOCK(btintel_pcie_recovery_lock); @@ -626,9 +654,10 @@ static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type, static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) { u32 offset, prev_size, wr_ptr_status, dump_size, data_len; + u32 status_reg, wrap_reg; struct btintel_pcie_dbgc *dbgc = &data->dbgc; struct hci_dev *hdev = data->hdev; - u8 *pdata, *p, buf_idx; + u8 *pdata, *p, buf_idx, hw_variant; struct intel_tlv *tlv; struct timespec64 now; struct tm tm_now; @@ -641,7 +670,28 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) return -EOPNOTSUPP; - wr_ptr_status = btintel_pcie_rd_dev_mem(data, BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS); + hw_variant = INTEL_HW_VARIANT(data->cnvi); + switch (hw_variant) { + case BTINTEL_HWID_BZRI: + case BTINTEL_HWID_BZRIW: + status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS; + wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND; + break; + case BTINTEL_HWID_SCP: + case BTINTEL_HWID_SCP2: + case BTINTEL_HWID_SCP2F: + status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP; + wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP; + break; + default: + bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%2.2x)", + hw_variant); + return -EINVAL; + } + + wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg); + data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg); + offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK; buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status); @@ -684,6 +734,11 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) sizeof(*tlv) + strlen(vendor) + sizeof(*tlv) + strlen(driver); + if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) { + data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type); + data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id); + } + /* * sizeof(u32) - signature * sizeof(data_len) - to store tlv data size @@ -718,10 +773,6 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) sizeof(data->dmp_hdr.write_ptr)); p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr, sizeof(data->dmp_hdr.wrap_ctr)); - - data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, - BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND); - p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason, sizeof(data->dmp_hdr.trigger_reason)); p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1, @@ -731,6 +782,17 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data) p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top, sizeof(data->dmp_hdr.cnvi_top)); + if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) { + p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE, + &data->dmp_hdr.event_type, + sizeof(data->dmp_hdr.event_type)); + p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID, + &data->dmp_hdr.event_id, + sizeof(data->dmp_hdr.event_id)); + data->dmp_hdr.event_type = 0; + data->dmp_hdr.event_id = 0; + } + memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE); dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL); return 0; @@ -755,6 +817,11 @@ static void btintel_pcie_dump_traces(struct hci_dev *hdev) bt_dev_err(hdev, "Failed to dump traces: (%d)", ret); } +static bool btintel_pcie_is_blazariw(struct pci_dev *pdev) +{ + return pdev->device == 0x4D76; +} + /* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in * BTINTEL_PCIE_CSR_FUNC_CTRL_REG register and wait for MSI-X with * BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0. @@ -774,6 +841,14 @@ static int btintel_pcie_enable_bt(struct btintel_pcie_data *data) btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_CI_ADDR_MSB_REG, (u64)data->ci_p_addr >> 32); + /* On BlazarIW, the D0 entry to MAC init does not complete in + * time. Wait 50 ms (worst case as per HW analysis) for the + * shared hardware reset flow to complete before proceeding with + * MAC init. + */ + if (btintel_pcie_is_blazariw(data->pdev)) + msleep(50); + /* Reset the cached value of boot stage. it is updated by the MSI-X * gp0 interrupt handler. */ @@ -1318,6 +1393,73 @@ static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data) kfree(buf); } +static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data) +{ + struct btintel_pcie_fwtrigger_evt *evt; + struct sk_buff *skb; + unsigned int len; + int err; + u8 *buf; + + if (!data->debug_evt_size || !data->debug_evt_addr) + return -EINVAL; + + len = data->debug_evt_size; + + len = ALIGN_DOWN(len, 4); + + if (len < sizeof(*evt) || len > HCI_MAX_EVENT_SIZE) { + bt_dev_err(data->hdev, "Invalid FW trigger data size (%u bytes)", len); + return -EINVAL; + } + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + btintel_pcie_mac_init(data); + + err = btintel_pcie_read_device_mem(data, buf, data->debug_evt_addr, + len); + if (err) + goto exit_on_error; + + evt = (void *)buf; + data->dmp_hdr.event_type = evt->event_type; + data->dmp_hdr.event_id = le16_to_cpu(evt->event_id); + + bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u", + data->dmp_hdr.event_type, data->dmp_hdr.event_id, len); + + skb = bt_skb_alloc(len, GFP_KERNEL); + if (!skb) { + err = -ENOMEM; + goto exit_on_error; + } + skb_put_data(skb, buf, len); + + hci_recv_diag(data->hdev, skb); + err = 0; + +exit_on_error: + kfree(buf); + return err; +} + +static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data) +{ + bt_dev_dbg(data->hdev, "Received firmware smart trigger cause"); + + if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) + return; + + /* Trigger device core dump when there is FW assert */ + if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) + data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; + + queue_work(data->coredump_workqueue, &data->coredump_work); +} + static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) { bt_dev_err(data->hdev, "Received hw exception interrupt"); @@ -1332,14 +1474,27 @@ static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data) if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->workqueue, &data->rx_work); + queue_work(data->coredump_workqueue, &data->coredump_work); } -static void btintel_pcie_rx_work(struct work_struct *work) +static void btintel_pcie_coredump_worker(struct work_struct *work) { struct btintel_pcie_data *data = container_of(work, - struct btintel_pcie_data, rx_work); - struct sk_buff *skb; + struct btintel_pcie_data, coredump_work); + int err; + + /* hdev is NULL until setup_hdev() succeeds, and is cleared on + * teardown after disable_work_sync() drains us; bail in that case. + */ + if (!data->hdev) + return; + + if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) { + err = btintel_pcie_dump_fwtrigger_event(data); + if (err) + bt_dev_warn(data->hdev, "failed to log fwtrigger event"); + clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags); + } if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) { btintel_pcie_dump_traces(data->hdev); @@ -1357,6 +1512,13 @@ static void btintel_pcie_rx_work(struct work_struct *work) btintel_pcie_read_hwexp(data); clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags); } +} + +static void btintel_pcie_rx_work(struct work_struct *work) +{ + struct btintel_pcie_data *data = container_of(work, + struct btintel_pcie_data, rx_work); + struct sk_buff *skb; /* Process the sk_buf in queue and send to the HCI layer */ while ((skb = skb_dequeue(&data->rx_skb_q))) { @@ -1497,6 +1659,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id) btintel_pcie_msix_tx_handle(data); } + if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG) + btintel_pcie_msix_fw_trigger_handler(data); + /* This interrupt is triggered by the firmware after updating * boot_stage register and image_response register */ @@ -1571,6 +1736,7 @@ static struct btintel_pcie_causes_list causes_list[] = { { BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 }, { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 }, { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 }, + { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x25 }, }; /* This function configures the interrupt masks for both HW_INT_CAUSES and @@ -2027,9 +2193,11 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, static void btintel_pcie_release_hdev(struct btintel_pcie_data *data) { - struct hci_dev *hdev; + struct hci_dev *hdev = data->hdev; + + if (!hdev) + return; - hdev = data->hdev; hci_unregister_dev(hdev); hci_free_dev(hdev); data->hdev = NULL; @@ -2057,6 +2225,55 @@ static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data) synchronize_irq(data->msix_entries[i].vector); } +static int btintel_pcie_get_debug_info_addr(struct hci_dev *hdev) +{ + struct btintel_pcie_data *data = hci_get_drvdata(hdev); + struct btintel_pcie_trigger_evt *evt; + u8 param[1] = {0x10}; + struct sk_buff *skb; + int err = 0; + + skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_DEBUG, 1, param, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Reading Intel read debug info address command failed (%ld)", + PTR_ERR(skb)); + /* Not all Intel products supports this command */ + if (PTR_ERR(skb) == -EOPNOTSUPP) + return 0; + return PTR_ERR(skb); + } + + if (skb->len < (1 + sizeof(*evt))) { + bt_dev_err(hdev, "Debug info response too short (%u bytes)", skb->len); + err = -EIO; + goto exit_error; + } + + /* Check the status */ + if (skb->data[0]) { + bt_dev_err(hdev, "Reading Intel read debug info command failed (0x%2.2x)", + skb->data[0]); + err = -EIO; + goto exit_error; + } + + /* Consume Command Complete Status field */ + skb_pull(skb, 1); + + evt = (void *)skb->data; + + data->debug_evt_addr = le32_to_cpu(evt->addr); + data->debug_evt_size = le32_to_cpu(evt->size); + + bt_dev_dbg(hdev, "config type: %u config len: %u debug event addr: 0x%8.8x size: 0x%8.8x", + evt->type, evt->len, data->debug_evt_addr, + data->debug_evt_size); +exit_error: + kfree_skb(skb); + return err; +} + static int btintel_pcie_setup_internal(struct hci_dev *hdev) { struct btintel_pcie_data *data = hci_get_drvdata(hdev); @@ -2156,6 +2373,10 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev) if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03) data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1; + err = btintel_pcie_get_debug_info_addr(hdev); + if (err) + goto exit_error; + btintel_print_fseq_info(hdev); exit_error: kfree_skb(skb); @@ -2265,34 +2486,167 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); +static void btintel_pcie_reset(struct hci_dev *hdev); -static void btintel_pcie_removal_work(struct work_struct *wk) +static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) { - struct btintel_pcie_removal *removal = - container_of(wk, struct btintel_pcie_removal, work); - struct pci_dev *pdev = removal->pdev; - struct btintel_pcie_data *data; + union acpi_object *obj, argv4; + acpi_handle handle; + int ret; + struct pldr_mode { + __le16 cmd_type; + __le16 cmd_payload; + } __packed; + + /* set 1 for _PRR mode + * Product Reset (PLDR Abort flow) + */ + static const struct pldr_mode mode = { + .cmd_type = cpu_to_le16(1), + .cmd_payload = cpu_to_le16(BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET | + BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR), + }; + struct hci_dev *hdev = data->hdev; + + handle = ACPI_HANDLE(GET_HCIDEV_DEV(data->hdev)); + if (!handle) { + bt_dev_err(data->hdev, "No support for bluetooth device in ACPI firmware"); + return -EACCES; + } + + if (!acpi_has_method(handle, "_PRR")) { + bt_dev_err(data->hdev, "No support for _PRR ACPI method, cold boot"); + return -ENODEV; + } + + argv4.buffer.type = ACPI_TYPE_BUFFER; + argv4.buffer.length = sizeof(mode); + argv4.buffer.pointer = (void *)&mode; + + obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0, + BTINTEL_PCIE_DSM_DYNAMIC_PLDR, &argv4); + if (!obj) { + bt_dev_err(data->hdev, "Failed to call dsm to set reset method"); + return -EIO; + } + ACPI_FREE(obj); + + pci_dev_lock(data->pdev); + pci_save_state(data->pdev); + ret = btintel_acpi_reset_method(hdev); + if (ret) + bt_dev_err(data->hdev, "ACPI _PRR reset failed (%d), PLDR incomplete", + ret); + pci_restore_state(data->pdev); + pci_dev_unlock(data->pdev); + return ret; +} + +static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data) +{ + struct pci_dev *pdev = data->pdev; + struct pci_dev *wifi = NULL; + struct pci_bus *bus; + int ret; + /* on integrated we have to look up by ID (same bus) */ + static const struct pci_device_id wifi_device_ids[] = { + #define WIFI_DEV(_id) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, _id) } + WIFI_DEV(0xA840), /* LNL */ + WIFI_DEV(0xE440), /* PTL-P */ + WIFI_DEV(0xE340), /* PTL-H */ + WIFI_DEV(0xD340), /* NVL-H */ + WIFI_DEV(0x6E70), /* NVL-S */ + WIFI_DEV(0x4D40), /* WCL */ + {} + }; + struct pci_dev *tmp = NULL; + + bus = pdev->bus; + if (!bus) + return; + + list_for_each_entry(tmp, &bus->devices, bus_list) { + if (pci_match_id(wifi_device_ids, tmp)) { + wifi = pci_dev_get(tmp); + break; + } + } + + if (wifi) + device_release_driver(&wifi->dev); + + /* Wi-Fi is fully unbound before the reset and fully reprobed after + * the normal PCI probe path handles all state setup from scratch. + * BT needs pci_save_state()/pci_restore_state() because the BT driver + * is still partially attached when the _PRR runs (it hasn't been unbound yet). + * The PCI device needs to remain minimally functional so that + * device_reprobe(&pdev->dev) can work afterward + */ + ret = btintel_pcie_acpi_reset_method(data); + + if (wifi) { + if (device_reprobe(&wifi->dev)) + BT_ERR("WiFi reprobe failed for BDF:%s", pci_name(wifi)); + pci_dev_put(wifi); + } + + if (!ret) { + if (device_reprobe(&pdev->dev)) + BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev)); + } +} + +static void btintel_pcie_reset_work(struct work_struct *wk) +{ + struct btintel_pcie_data *data = + container_of(wk, struct btintel_pcie_data, reset_work); + struct pci_dev *pdev = data->pdev; int err; pci_lock_rescan_remove(); if (!pdev->bus) - goto error; + goto out; - data = pci_get_drvdata(pdev); + if (!data) + goto out; btintel_pcie_disable_interrupts(data); btintel_pcie_synchronize_irqs(data); flush_work(&data->rx_work); + /* Drain any in-flight coredump and block new ones across reset. + * Safe from self-deadlock: coredump_work runs on a separate wq. + */ + disable_work_sync(&data->coredump_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); + if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { + /* This function holds pci_lock_rescan_remove(), which acquires + * pci_rescan_remove_lock. This mutex serializes against PCI device + * addition/removal (hotplug), so no device can be added to or + * removed from the bus list while this code runs. + * + * device_reprobe() inside btintel_pcie_perform_pldr() destroys + * 'data' via .remove(); a fresh probe re-INIT_WORKs the + * coredump_work with disable count 0, so we must not call + * enable_work() on this path. + */ + btintel_pcie_perform_pldr(data); + goto out; + } btintel_pcie_release_hdev(data); - err = pci_reset_function(pdev); + /* Use pci_try_reset_function() rather than pci_reset_function() to + * avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core + * calls .remove() with device_lock held, and remove() then waits for + * this work via cancel_work_sync(); pci_reset_function() would in + * turn try to acquire the same device_lock, deadlocking both paths. + */ + err = pci_try_reset_function(pdev); if (err) { BT_ERR("Failed resetting the pcie device (%d)", err); - goto error; + goto out_enable; } btintel_pcie_enable_interrupts(data); @@ -2302,7 +2656,7 @@ static void btintel_pcie_removal_work(struct work_struct *wk) if (err) { BT_ERR("Failed to enable bluetooth hardware after reset (%d)", err); - goto error; + goto out_enable; } btintel_pcie_reset_ia(data); @@ -2312,17 +2666,22 @@ static void btintel_pcie_removal_work(struct work_struct *wk) err = btintel_pcie_setup_hdev(data); if (err) { BT_ERR("Failed registering hdev (%d)", err); - goto error; + goto out_enable; } -error: + +out_enable: + /* Balance disable_work_sync() above on every exit. Leaving the + * counter incremented on a failed reset would permanently disable + * coredump_work even after a later successful reset. + */ + enable_work(&data->coredump_work); +out: pci_dev_put(pdev); pci_unlock_rescan_remove(); - kfree(removal); } static void btintel_pcie_reset(struct hci_dev *hdev) { - struct btintel_pcie_removal *removal; struct btintel_pcie_data *data; data = hci_get_drvdata(hdev); @@ -2333,14 +2692,8 @@ static void btintel_pcie_reset(struct hci_dev *hdev) if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) return; - removal = kzalloc_obj(*removal, GFP_ATOMIC); - if (!removal) - return; - - removal->pdev = data->pdev; - INIT_WORK(&removal->work, btintel_pcie_removal_work); - pci_dev_get(removal->pdev); - schedule_work(&removal->work); + pci_dev_get(data->pdev); + schedule_work(&data->reset_work); } static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) @@ -2350,15 +2703,19 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) struct pci_dev *pdev = dev_data->pdev; time64_t retry_window; - if (code == 0x13) { - bt_dev_err(hdev, "Encountered top exception"); - return; - } + btintel_pcie_dump_debug_registers(hdev); data = btintel_pcie_get_recovery(pdev, &hdev->dev); if (!data) return; + if (code == 0x13) + dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR; + else + dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; + + bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code, + dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); retry_window = ktime_get_boottime_seconds() - data->last_error; if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && @@ -2451,7 +2808,6 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) hdev->bus = HCI_PCI; hci_set_drvdata(hdev, data); - data->hdev = hdev; SET_HCIDEV_DEV(hdev, &data->pdev->dev); hdev->manufacturer = 2; @@ -2470,15 +2826,17 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) err = hci_register_dev(hdev); if (err < 0) { BT_ERR("Failed to register to hdev (%d)", err); - goto exit_error; + hci_free_dev(hdev); + return err; } + /* Publish hdev only after successful registration; the coredump + * worker bails on !data->hdev, so it never observes a half-set-up + * device. + */ + data->hdev = hdev; data->dmp_hdr.driver_name = KBUILD_MODNAME; return 0; - -exit_error: - hci_free_dev(hdev); - return err; } static int btintel_pcie_probe(struct pci_dev *pdev, @@ -2509,12 +2867,23 @@ static int btintel_pcie_probe(struct pci_dev *pdev, if (!data->workqueue) return -ENOMEM; + data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0); + if (!data->coredump_workqueue) { + destroy_workqueue(data->workqueue); + return -ENOMEM; + } + skb_queue_head_init(&data->rx_skb_q); INIT_WORK(&data->rx_work, btintel_pcie_rx_work); + INIT_WORK(&data->reset_work, btintel_pcie_reset_work); + INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker); data->boot_stage_cache = 0x00; data->img_resp_cache = 0x00; - + /* FLR can be invoked by echoing to debugfs path, so explicitly + * initialized + */ + data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; err = btintel_pcie_config_pcie(pdev, data); if (err) goto exit_error; @@ -2550,6 +2919,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev, /* reset device before exit */ btintel_pcie_reset_bt(data); + destroy_workqueue(data->coredump_workqueue); + pci_clear_master(pdev); pci_set_drvdata(pdev, NULL); @@ -2562,6 +2933,25 @@ static void btintel_pcie_remove(struct pci_dev *pdev) struct btintel_pcie_data *data; data = pci_get_drvdata(pdev); + if (!data) { + BT_WARN("PCI driver data is NULL, aborting remove"); + return; + } + + /* Permanently block coredump triggers and drain the worker before + * tearing down. Must run before cancel_work_sync(&reset_work) so + * the disable counter stays >= 1 even after reset_work()'s + * balanced enable_work() (counter 2 -> 1, never reaching 0). + */ + disable_work_sync(&data->coredump_work); + + /* Cancel pending reset work. Skip only when remove() is called from + * within the reset work itself (PLDR device_reprobe path) to avoid + * deadlock. current_work() returns the work_struct of the caller if + * we are in a workqueue context. + */ + if (current_work() != &data->reset_work) + disable_work_sync(&data->reset_work); btintel_pcie_disable_interrupts(data); @@ -2581,6 +2971,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev) btintel_pcie_release_hdev(data); + destroy_workqueue(data->coredump_workqueue); destroy_workqueue(data->workqueue); btintel_pcie_free(data); @@ -2596,11 +2987,19 @@ static void btintel_pcie_coredump(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct btintel_pcie_data *data = pci_get_drvdata(pdev); + if (!data) + return; + if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) return; data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER; - queue_work(data->workqueue, &data->rx_work); + /* queue_work() returns false if the work is disabled (reset or + * remove in progress); clear the in-progress bit so a later + * trigger can succeed once the work is re-enabled. + */ + if (!queue_work(data->coredump_workqueue, &data->coredump_work)) + clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags); } #endif @@ -2712,6 +3111,7 @@ static int btintel_pcie_resume(struct device *dev) if (data->pm_sx_event == PM_EVENT_FREEZE || data->pm_sx_event == PM_EVENT_HIBERNATE) { set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; btintel_pcie_reset(data->hdev); return 0; } @@ -2740,7 +3140,7 @@ static int btintel_pcie_resume(struct device *dev) &data->flags)) { data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; - queue_work(data->workqueue, &data->rx_work); + queue_work(data->coredump_workqueue, &data->coredump_work); } set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); btintel_pcie_reset(data->hdev); diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 13efef499e4e..7caee093e316 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -68,6 +68,10 @@ #define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x1C) #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x2C) +#define BTINTEL_PCIE_DBGC_BASE_ADDR_SCP (0xf0d5d500) +#define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x1C) +#define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C) + #define BTINTEL_PCIE_DBG_IDX_BIT_MASK 0x0F #define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data) (((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK) #define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK 0xFFFFFF @@ -98,6 +102,7 @@ enum msix_hw_int_causes { BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */ + BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG = BIT(5), /* cause 37 */ }; /* PCIe device states @@ -115,6 +120,7 @@ enum { BTINTEL_PCIE_CORE_HALTED, BTINTEL_PCIE_HWEXP_INPROGRESS, BTINTEL_PCIE_COREDUMP_INPROGRESS, + BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, BTINTEL_PCIE_RECOVERY_IN_PROGRESS, BTINTEL_PCIE_SETUP_DONE }; @@ -130,7 +136,9 @@ enum btintel_pcie_tlv_type { BTINTEL_DUMP_TIME, BTINTEL_FW_BUILD, BTINTEL_VENDOR, - BTINTEL_DRIVER + BTINTEL_DRIVER, + BTINTEL_EVENT_TYPE, + BTINTEL_EVENT_ID }; /* causes for the MBOX interrupts */ @@ -141,6 +149,11 @@ enum msix_mbox_int_causes { BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */ }; +enum btintel_pcie_reset_type { + BTINTEL_PCIE_IOSF_PRR_FLR = 0, + BTINTEL_PCIE_IOSF_PRR_PLDR = 1, +}; + #define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7) /* Minimum and Maximum number of MSI-X Vector @@ -425,6 +438,8 @@ struct btintel_pcie_dump_header { u32 wrap_ctr; u16 trigger_reason; int state; + u8 event_type; + u16 event_id; }; /* struct btintel_pcie_data @@ -451,6 +466,8 @@ struct btintel_pcie_dump_header { * @workqueue: workqueue for RX work * @rx_skb_q: SKB queue for RX packet * @rx_work: RX work struct to process the RX packet in @rx_skb_q + * @coredump_workqueue: dedicated workqueue for coredump collection + * @coredump_work: work struct for coredump trace collection * @dma_pool: DMA pool for descriptors, index array and ci * @dma_p_addr: DMA address for pool * @dma_v_addr: address of pool @@ -497,6 +514,10 @@ struct btintel_pcie_data { struct workqueue_struct *workqueue; struct sk_buff_head rx_skb_q; struct work_struct rx_work; + struct work_struct reset_work; + + struct workqueue_struct *coredump_workqueue; + struct work_struct coredump_work; struct dma_pool *dma_pool; dma_addr_t dma_p_addr; @@ -508,9 +529,12 @@ struct btintel_pcie_data { struct txq txq; struct rxq rxq; u32 alive_intr_ctxt; + enum btintel_pcie_reset_type reset_type; struct btintel_pcie_dbgc dbgc; struct btintel_pcie_dump_header dmp_hdr; u8 pm_sx_event; + u32 debug_evt_addr; + u32 debug_evt_size; }; static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data, diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 8ff66b276af0..02a96342e964 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -188,7 +188,7 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i + MTK_SEC_MAP_COMMON_SIZE, - MTK_SEC_MAP_NEED_SEND_SIZE + 1); + MTK_SEC_MAP_NEED_SEND_SIZE); wmt_params.op = BTMTK_WMT_PATCH_DWNLD; wmt_params.status = &status; @@ -1074,8 +1074,10 @@ struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb, if (!urb) return ERR_PTR(-ENOMEM); - if (btmtk_isopkt_pad(hdev, skb)) + if (btmtk_isopkt_pad(hdev, skb)) { + usb_free_urb(urb); return ERR_PTR(-EINVAL); + } pipe = usb_sndintpipe(btmtk_data->udev, btmtk_data->isopkt_tx_ep->bEndpointAddress); @@ -1379,6 +1381,16 @@ int btmtk_usb_setup(struct hci_dev *hdev) break; case 0x7922: case 0x7925: + /* + * A remote wakeup could cause the device completely unresponsive, and + * recovering from such a state needs a power cycle. + * + * Since the remote wakeup capability is super broken, just disable it + * to get rid of the troubles. The device can still be autosuspended + * when the bluetooth interface is closed. + */ + device_set_wakeup_capable(&btmtk_data->udev->dev, false); + fallthrough; case 0x7961: case 0x7902: case 0x6639: @@ -1547,6 +1559,29 @@ int btmtk_usb_shutdown(struct hci_dev *hdev) return 0; } EXPORT_SYMBOL_GPL(btmtk_usb_shutdown); + +int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (void *)skb->data; + struct hci_ev_cmd_complete *ec; + + if (hdr->evt == HCI_EV_CMD_COMPLETE && + skb->len >= HCI_EVENT_HDR_SIZE + sizeof(*ec)) { + u16 opcode; + + ec = (void *)(skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + + /* Filter vendor opcode */ + if (opcode == 0xfc5d) { + kfree_skb(skb); + return 0; + } + } + + return hci_recv_frame(hdev, skb); +} +EXPORT_SYMBOL_GPL(btmtk_recv_event); #endif MODULE_AUTHOR("Sean Wang "); diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h index c564aedc0ce0..c83c24897c95 100644 --- a/drivers/bluetooth/btmtk.h +++ b/drivers/bluetooth/btmtk.h @@ -220,6 +220,8 @@ int btmtk_usb_suspend(struct hci_dev *hdev); int btmtk_usb_setup(struct hci_dev *hdev); int btmtk_usb_shutdown(struct hci_dev *hdev); + +int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb); #else static inline int btmtk_set_bdaddr(struct hci_dev *hdev, @@ -299,4 +301,9 @@ static inline int btmtk_usb_shutdown(struct hci_dev *hdev) { return -EOPNOTSUPP; } + +static inline int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + return hci_recv_frame(hdev, skb); +} #endif diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 5b0fab7b89b5..c6f80c419e90 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct *work) if (btmtksdio_rx_packet(bdev, rx_size) < 0) bdev->hdev->stat.err_rx++; } - } while (int_status || time_is_before_jiffies(txrx_timeout)); + } while (int_status && time_is_after_jiffies(txrx_timeout)); /* Enable interrupt */ if (bdev->func->irq_handler) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index dda76365726f..04ebe290bc78 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -143,6 +143,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev) hci_set_fw_info(hdev, "%s", build_label); + bt_dev_info(hdev, "QCA FW build version: %s", build_label); + kfree(build_label); out: kfree_skb(skb); diff --git a/drivers/bluetooth/btrsi.c b/drivers/bluetooth/btrsi.c index c68dd2fba01c..59ad0b9b14c3 100644 --- a/drivers/bluetooth/btrsi.c +++ b/drivers/bluetooth/btrsi.c @@ -1,17 +1,7 @@ +// SPDX-License-Identifier: ISC /* * Copyright (c) 2017 Redpine Signals Inc. * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 62f9d4df3a4f..49ecb18fea45 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -1342,6 +1342,19 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) if (!btrtl_dev->ic_info) return; + switch (btrtl_dev->project_id) { + case CHIP_ID_8761B: + /* RTL8761B/BU reports HCI version 5.1 but does not support + * the LE Extended Scan commands (Opcode 0x2042), causing + * repeated -EBUSY failures when BlueZ attempts extended + * scanning while a connection is active. + */ + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN); + break; + default: + break; + } + switch (btrtl_dev->ic_info->lmp_subver) { case RTL_ROM_LMP_8703B: /* 8723CS reports two pages for local ext features, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 830fefb342c6..08c0a99a62c5 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -484,6 +484,7 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x8087, 0x0037), .driver_info = BTUSB_INTEL_COMBINED }, { USB_DEVICE(0x8087, 0x0038), .driver_info = BTUSB_INTEL_COMBINED }, { USB_DEVICE(0x8087, 0x0039), .driver_info = BTUSB_INTEL_COMBINED }, + { USB_DEVICE(0x8087, 0x0040), .driver_info = BTUSB_INTEL_COMBINED }, /* Lizard Peak 2 */ { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR }, { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED | BTUSB_INTEL_NO_WBS_SUPPORT | @@ -531,6 +532,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x7392, 0xe611), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2c4e, 0x0128), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8852AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK | @@ -601,6 +604,10 @@ static const struct usb_device_id quirks_table[] = { /* Realtek 8922AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0bda, 0xd922), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0bda, 0xd923), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3617), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3616), .driver_info = BTUSB_REALTEK | @@ -723,6 +730,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0e8d, 0x223c), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3584), .driver_info = BTUSB_MEDIATEK | @@ -773,6 +782,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe151), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0e8d, 0x8c38), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3602), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3603), .driver_info = BTUSB_MEDIATEK | @@ -781,6 +792,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3608), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3609), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3627), .driver_info = BTUSB_MEDIATEK | @@ -814,8 +827,12 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK }, /* Additional Realtek 8761BUV Bluetooth devices */ + { USB_DEVICE(0x2c4e, 0x0115), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2357, 0x0607), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x2550, 0x8761), .driver_info = BTUSB_REALTEK | @@ -830,6 +847,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, @@ -2994,6 +3013,11 @@ static int marvell_config_oob_wake(struct hci_dev *hdev) return 0; } +#else +static inline int marvell_config_oob_wake(struct hci_dev *hdev) +{ + return 0; +} #endif static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, @@ -3819,8 +3843,7 @@ static int btusb_config_oob_wake(struct hci_dev *hdev) } irq_set_status_flags(irq, IRQ_NOAUTOEN); - ret = devm_request_irq(&hdev->dev, irq, btusb_oob_wake_handler, - 0, "OOB Wake-on-BT", data); + ret = request_irq(irq, btusb_oob_wake_handler, 0, "OOB Wake-on-BT", data); if (ret) { bt_dev_err(hdev, "%s: IRQ request failed", __func__); return ret; @@ -3829,11 +3852,22 @@ static int btusb_config_oob_wake(struct hci_dev *hdev) ret = device_init_wakeup(dev, true); if (ret) { bt_dev_err(hdev, "%s: failed to init_wakeup", __func__); - return ret; + goto err_free_irq; } data->oob_wake_irq = irq; bt_dev_info(hdev, "OOB Wake-on-BT configured at IRQ %u", irq); + + return 0; + +err_free_irq: + free_irq(irq, data); + + return ret; +} +#else +static inline int btusb_config_oob_wake(struct hci_dev *hdev) +{ return 0; } #endif @@ -4085,10 +4119,8 @@ static int btusb_probe(struct usb_interface *intf, err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep, &data->bulk_tx_ep, &data->intr_ep, NULL); - if (err) { - kfree(data); - return -ENODEV; - } + if (err) + goto err_free_data; if (id->driver_info & BTUSB_AMP) { data->cmdreq_type = USB_TYPE_CLASS | 0x01; @@ -4144,8 +4176,8 @@ static int btusb_probe(struct usb_interface *intf, hdev = hci_alloc_dev_priv(priv_size); if (!hdev) { - kfree(data); - return -ENOMEM; + err = -ENOMEM; + goto err_free_data; } hdev->bus = HCI_USB; @@ -4159,7 +4191,7 @@ static int btusb_probe(struct usb_interface *intf, GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) { err = PTR_ERR(reset_gpio); - goto out_free_dev; + goto err_free_hdev; } else if (reset_gpio) { data->reset_gpio = reset_gpio; } @@ -4172,18 +4204,17 @@ static int btusb_probe(struct usb_interface *intf, hdev->wakeup = btusb_wakeup; hdev->hci_drv = &btusb_hci_drv; -#ifdef CONFIG_PM err = btusb_config_oob_wake(hdev); if (err) - goto out_free_dev; + goto err_put_reset; /* Marvell devices may need a specific chip configuration */ if (id->driver_info & BTUSB_MARVELL && data->oob_wake_irq) { err = marvell_config_oob_wake(hdev); if (err) - goto out_free_dev; + goto err_disable_wakeup; } -#endif + if (id->driver_info & BTUSB_CW6622) hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY); @@ -4218,7 +4249,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INTEL_COMBINED) { err = btintel_configure_setup(hdev, btusb_driver.name); if (err) - goto out_free_dev; + goto err_kill_tx_urbs; /* Transport specific configuration */ hdev->send = btusb_send_frame_intel; @@ -4248,6 +4279,7 @@ static int btusb_probe(struct usb_interface *intf, hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN); hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP); data->recv_acl = btmtk_usb_recv_acl; + data->recv_event = btmtk_recv_event; data->suspend = btmtk_usb_suspend; data->resume = btmtk_usb_resume; data->disconnect = btusb_mtk_disconnect; @@ -4381,7 +4413,7 @@ static int btusb_probe(struct usb_interface *intf, err = usb_set_interface(data->udev, 0, 0); if (err < 0) { BT_ERR("failed to set interface 0, alt 0 %d", err); - goto out_free_dev; + goto err_kill_tx_urbs; } } @@ -4389,7 +4421,7 @@ static int btusb_probe(struct usb_interface *intf, err = usb_driver_claim_interface(&btusb_driver, data->isoc, data); if (err < 0) - goto out_free_dev; + goto err_kill_tx_urbs; } if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) { @@ -4407,7 +4439,7 @@ static int btusb_probe(struct usb_interface *intf, err = hci_register_dev(hdev); if (err < 0) - goto out_free_dev; + goto err_release_siblings; usb_set_intfdata(intf, data); @@ -4416,11 +4448,30 @@ static int btusb_probe(struct usb_interface *intf, return 0; -out_free_dev: +err_release_siblings: + if (data->diag) { + usb_set_intfdata(data->diag, NULL); + usb_driver_release_interface(&btusb_driver, data->diag); + } + if (data->isoc) { + usb_set_intfdata(data->isoc, NULL); + usb_driver_release_interface(&btusb_driver, data->isoc); + } +err_kill_tx_urbs: + usb_kill_anchored_urbs(&data->tx_anchor); +err_disable_wakeup: + if (data->oob_wake_irq) { + device_init_wakeup(&data->udev->dev, false); + free_irq(data->oob_wake_irq, data); + } +err_put_reset: if (data->reset_gpio) gpiod_put(data->reset_gpio); +err_free_hdev: hci_free_dev(hdev); +err_free_data: kfree(data); + return err; } @@ -4448,8 +4499,11 @@ static void btusb_disconnect(struct usb_interface *intf) hci_unregister_dev(hdev); - if (data->oob_wake_irq) + if (data->oob_wake_irq) { device_init_wakeup(&data->udev->dev, false); + free_irq(data->oob_wake_irq, data); + } + if (data->reset_gpio) gpiod_put(data->reset_gpio); diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c deleted file mode 100644 index 2adfe4fade76..000000000000 --- a/drivers/bluetooth/dtl1_cs.c +++ /dev/null @@ -1,614 +0,0 @@ -/* - * - * A driver for Nokia Connectivity Card DTL-1 devices - * - * Copyright (C) 2001-2002 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - - - -/* ======================== Module parameters ======================== */ - - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1"); -MODULE_LICENSE("GPL"); - - - -/* ======================== Local structures ======================== */ - - -struct dtl1_info { - struct pcmcia_device *p_dev; - - struct hci_dev *hdev; - - spinlock_t lock; /* For serializing operations */ - - unsigned long flowmask; /* HCI flow mask */ - int ri_latch; - - struct sk_buff_head txq; - unsigned long tx_state; - - unsigned long rx_state; - unsigned long rx_count; - struct sk_buff *rx_skb; -}; - - -static int dtl1_config(struct pcmcia_device *link); - - -/* Transmit states */ -#define XMIT_SENDING 1 -#define XMIT_WAKEUP 2 -#define XMIT_WAITING 8 - -/* Receiver States */ -#define RECV_WAIT_NSH 0 -#define RECV_WAIT_DATA 1 - - -struct nsh { - u8 type; - u8 zero; - u16 len; -} __packed; /* Nokia Specific Header */ - -#define NSHL 4 /* Nokia Specific Header Length */ - - - -/* ======================== Interrupt handling ======================== */ - - -static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) -{ - int actual = 0; - - /* Tx FIFO should be empty */ - if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) - return 0; - - /* Fill FIFO with current frame */ - while ((fifo_size-- > 0) && (actual < len)) { - /* Transmit next byte */ - outb(buf[actual], iobase + UART_TX); - actual++; - } - - return actual; -} - - -static void dtl1_write_wakeup(struct dtl1_info *info) -{ - if (!info) { - BT_ERR("Unknown device"); - return; - } - - if (test_bit(XMIT_WAITING, &(info->tx_state))) { - set_bit(XMIT_WAKEUP, &(info->tx_state)); - return; - } - - if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { - set_bit(XMIT_WAKEUP, &(info->tx_state)); - return; - } - - do { - unsigned int iobase = info->p_dev->resource[0]->start; - register struct sk_buff *skb; - int len; - - clear_bit(XMIT_WAKEUP, &(info->tx_state)); - - if (!pcmcia_dev_present(info->p_dev)) - return; - - skb = skb_dequeue(&(info->txq)); - if (!skb) - break; - - /* Send frame */ - len = dtl1_write(iobase, 32, skb->data, skb->len); - - if (len == skb->len) { - set_bit(XMIT_WAITING, &(info->tx_state)); - kfree_skb(skb); - } else { - skb_pull(skb, len); - skb_queue_head(&(info->txq), skb); - } - - info->hdev->stat.byte_tx += len; - - } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); - - clear_bit(XMIT_SENDING, &(info->tx_state)); -} - - -static void dtl1_control(struct dtl1_info *info, struct sk_buff *skb) -{ - u8 flowmask = *(u8 *)skb->data; - int i; - - printk(KERN_INFO "Bluetooth: Nokia control data ="); - for (i = 0; i < skb->len; i++) - printk(" %02x", skb->data[i]); - - printk("\n"); - - /* transition to active state */ - if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) { - clear_bit(XMIT_WAITING, &(info->tx_state)); - dtl1_write_wakeup(info); - } - - info->flowmask = flowmask; - - kfree_skb(skb); -} - - -static void dtl1_receive(struct dtl1_info *info) -{ - unsigned int iobase; - struct nsh *nsh; - int boguscount = 0; - - if (!info) { - BT_ERR("Unknown device"); - return; - } - - iobase = info->p_dev->resource[0]->start; - - do { - info->hdev->stat.byte_rx++; - - /* Allocate packet */ - if (info->rx_skb == NULL) { - info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!info->rx_skb) { - BT_ERR("Can't allocate mem for new packet"); - info->rx_state = RECV_WAIT_NSH; - info->rx_count = NSHL; - return; - } - } - - skb_put_u8(info->rx_skb, inb(iobase + UART_RX)); - nsh = (struct nsh *)info->rx_skb->data; - - info->rx_count--; - - if (info->rx_count == 0) { - - switch (info->rx_state) { - case RECV_WAIT_NSH: - info->rx_state = RECV_WAIT_DATA; - info->rx_count = nsh->len + (nsh->len & 0x0001); - break; - case RECV_WAIT_DATA: - hci_skb_pkt_type(info->rx_skb) = nsh->type; - - /* remove PAD byte if it exists */ - if (nsh->len & 0x0001) { - info->rx_skb->tail--; - info->rx_skb->len--; - } - - /* remove NSH */ - skb_pull(info->rx_skb, NSHL); - - switch (hci_skb_pkt_type(info->rx_skb)) { - case 0x80: - /* control data for the Nokia Card */ - dtl1_control(info, info->rx_skb); - break; - case 0x82: - case 0x83: - case 0x84: - /* send frame to the HCI layer */ - hci_skb_pkt_type(info->rx_skb) &= 0x0f; - hci_recv_frame(info->hdev, info->rx_skb); - break; - default: - /* unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", - hci_skb_pkt_type(info->rx_skb)); - kfree_skb(info->rx_skb); - break; - } - - info->rx_state = RECV_WAIT_NSH; - info->rx_count = NSHL; - info->rx_skb = NULL; - break; - } - - } - - /* Make sure we don't stay here too long */ - if (boguscount++ > 32) - break; - - } while (inb(iobase + UART_LSR) & UART_LSR_DR); -} - - -static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) -{ - struct dtl1_info *info = dev_inst; - unsigned int iobase; - unsigned char msr; - int boguscount = 0; - int iir, lsr; - irqreturn_t r = IRQ_NONE; - - if (!info || !info->hdev) - /* our irq handler is shared */ - return IRQ_NONE; - - iobase = info->p_dev->resource[0]->start; - - spin_lock(&(info->lock)); - - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - while (iir) { - - r = IRQ_HANDLED; - /* Clear interrupt */ - lsr = inb(iobase + UART_LSR); - - switch (iir) { - case UART_IIR_RLSI: - BT_ERR("RLSI"); - break; - case UART_IIR_RDI: - /* Receive interrupt */ - dtl1_receive(info); - break; - case UART_IIR_THRI: - if (lsr & UART_LSR_THRE) { - /* Transmitter ready for data */ - dtl1_write_wakeup(info); - } - break; - default: - BT_ERR("Unhandled IIR=%#x", iir); - break; - } - - /* Make sure we don't stay here too long */ - if (boguscount++ > 100) - break; - - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - - } - - msr = inb(iobase + UART_MSR); - - if (info->ri_latch ^ (msr & UART_MSR_RI)) { - info->ri_latch = msr & UART_MSR_RI; - clear_bit(XMIT_WAITING, &(info->tx_state)); - dtl1_write_wakeup(info); - r = IRQ_HANDLED; - } - - spin_unlock(&(info->lock)); - - return r; -} - - - -/* ======================== HCI interface ======================== */ - - -static int dtl1_hci_open(struct hci_dev *hdev) -{ - return 0; -} - - -static int dtl1_hci_flush(struct hci_dev *hdev) -{ - struct dtl1_info *info = hci_get_drvdata(hdev); - - /* Drop TX queue */ - skb_queue_purge(&(info->txq)); - - return 0; -} - - -static int dtl1_hci_close(struct hci_dev *hdev) -{ - dtl1_hci_flush(hdev); - - return 0; -} - - -static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct dtl1_info *info = hci_get_drvdata(hdev); - struct sk_buff *s; - struct nsh nsh; - - switch (hci_skb_pkt_type(skb)) { - case HCI_COMMAND_PKT: - hdev->stat.cmd_tx++; - nsh.type = 0x81; - break; - case HCI_ACLDATA_PKT: - hdev->stat.acl_tx++; - nsh.type = 0x82; - break; - case HCI_SCODATA_PKT: - hdev->stat.sco_tx++; - nsh.type = 0x83; - break; - default: - return -EILSEQ; - } - - nsh.zero = 0; - nsh.len = skb->len; - - s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC); - if (!s) - return -ENOMEM; - - skb_reserve(s, NSHL); - skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len); - if (skb->len & 0x0001) - skb_put_u8(s, 0); /* PAD */ - - /* Prepend skb with Nokia frame header and queue */ - memcpy(skb_push(s, NSHL), &nsh, NSHL); - skb_queue_tail(&(info->txq), s); - - dtl1_write_wakeup(info); - - kfree_skb(skb); - - return 0; -} - - - -/* ======================== Card services HCI interaction ======================== */ - - -static int dtl1_open(struct dtl1_info *info) -{ - unsigned long flags; - unsigned int iobase = info->p_dev->resource[0]->start; - struct hci_dev *hdev; - - spin_lock_init(&(info->lock)); - - skb_queue_head_init(&(info->txq)); - - info->rx_state = RECV_WAIT_NSH; - info->rx_count = NSHL; - info->rx_skb = NULL; - - set_bit(XMIT_WAITING, &(info->tx_state)); - - /* Initialize HCI device */ - hdev = hci_alloc_dev(); - if (!hdev) { - BT_ERR("Can't allocate HCI device"); - return -ENOMEM; - } - - info->hdev = hdev; - - hdev->bus = HCI_PCCARD; - hci_set_drvdata(hdev, info); - SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - - hdev->open = dtl1_hci_open; - hdev->close = dtl1_hci_close; - hdev->flush = dtl1_hci_flush; - hdev->send = dtl1_hci_send_frame; - - spin_lock_irqsave(&(info->lock), flags); - - /* Reset UART */ - outb(0, iobase + UART_MCR); - - /* Turn off interrupts */ - outb(0, iobase + UART_IER); - - /* Initialize UART */ - outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ - outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); - - info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR) - & UART_MSR_RI; - - /* Turn on interrupts */ - outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); - - spin_unlock_irqrestore(&(info->lock), flags); - - /* Timeout before it is safe to send the first HCI packet */ - msleep(2000); - - /* Register HCI device */ - if (hci_register_dev(hdev) < 0) { - BT_ERR("Can't register HCI device"); - info->hdev = NULL; - hci_free_dev(hdev); - return -ENODEV; - } - - return 0; -} - - -static int dtl1_close(struct dtl1_info *info) -{ - unsigned long flags; - unsigned int iobase = info->p_dev->resource[0]->start; - struct hci_dev *hdev = info->hdev; - - if (!hdev) - return -ENODEV; - - dtl1_hci_close(hdev); - - spin_lock_irqsave(&(info->lock), flags); - - /* Reset UART */ - outb(0, iobase + UART_MCR); - - /* Turn off interrupts */ - outb(0, iobase + UART_IER); - - spin_unlock_irqrestore(&(info->lock), flags); - - hci_unregister_dev(hdev); - hci_free_dev(hdev); - - return 0; -} - -static int dtl1_probe(struct pcmcia_device *link) -{ - struct dtl1_info *info; - - /* Create new info device */ - info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->p_dev = link; - link->priv = info; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - return dtl1_config(link); -} - - -static void dtl1_detach(struct pcmcia_device *link) -{ - struct dtl1_info *info = link->priv; - - dtl1_close(info); - pcmcia_disable_device(link); -} - -static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data) -{ - if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8)) - return -ENODEV; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - return pcmcia_request_io(p_dev); -} - -static int dtl1_config(struct pcmcia_device *link) -{ - struct dtl1_info *info = link->priv; - int ret; - - /* Look for a generic full-sized window */ - link->resource[0]->end = 8; - ret = pcmcia_loop_config(link, dtl1_confcheck, NULL); - if (ret) - goto failed; - - ret = pcmcia_request_irq(link, dtl1_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - ret = dtl1_open(info); - if (ret) - goto failed; - - return 0; - -failed: - dtl1_detach(link); - return ret; -} - -static const struct pcmcia_device_id dtl1_ids[] = { - PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d), - PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82), - PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863), - PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, dtl1_ids); - -static struct pcmcia_driver dtl1_driver = { - .owner = THIS_MODULE, - .name = "dtl1_cs", - .probe = dtl1_probe, - .remove = dtl1_detach, - .id_table = dtl1_ids, -}; -module_pcmcia_driver(dtl1_driver); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index d35383718212..c6d9f70ad3bb 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -273,6 +273,7 @@ static int h5_close(struct hci_uart *hu) if (!hu->serdev) kfree(h5); + hu->priv = NULL; return 0; } diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 34500137df2c..244447195619 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1028,7 +1028,7 @@ static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb) skb_put_data(skb, buf, strlen(buf)); snprintf(buf, sizeof(buf), "Driver: %s\n", - hu->serdev->dev.driver->name); + hu->serdev ? hu->serdev->dev.driver->name : "hci_ldisc_qca"); skb_put_data(skb, buf, strlen(buf)); } @@ -1916,9 +1916,12 @@ static int qca_setup(struct hci_uart *hu) const char *rampatch_name = qca_get_rampatch_name(hu); int ret; struct qca_btsoc_version ver; - struct qca_serdev *qcadev = serdev_device_get_drvdata(hu->serdev); + struct qca_serdev *qcadev = NULL; const char *soc_name; + if (hu->serdev) + qcadev = serdev_device_get_drvdata(hu->serdev); + ret = qca_check_speeds(hu); if (ret) return ret; @@ -1980,7 +1983,7 @@ static int qca_setup(struct hci_uart *hu) case QCA_WCN6750: case QCA_WCN6855: case QCA_WCN7850: - if (qcadev->bdaddr_property_broken) + if (qcadev && qcadev->bdaddr_property_broken) hci_set_quirk(hdev, HCI_QUIRK_BDADDR_PROPERTY_BROKEN); hci_set_aosp_capable(hdev); @@ -2073,7 +2076,7 @@ static int qca_setup(struct hci_uart *hu) else hu->hdev->set_bdaddr = qca_set_bdaddr; - if (qcadev->support_hfp_hw_offload) + if (qcadev && qcadev->support_hfp_hw_offload) qca_configure_hfp_offload(hdev); qca->fw_version = le16_to_cpu(ver.patch_ver); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 2762eacf7f20..eddb32b30539 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -337,7 +337,17 @@ static ssize_t force_devcd_write(struct file *file, const char __user *user_buf, if (copy_from_user(&dump_data, user_buf, count)) return -EFAULT; + switch (dump_data.state) { + case HCI_DEVCOREDUMP_DONE: + case HCI_DEVCOREDUMP_ABORT: + case HCI_DEVCOREDUMP_TIMEOUT: + break; + default: + return -EINVAL; + } + data_size = count - offsetof(struct devcoredump_test_data, data); + skb = alloc_skb(data_size, GFP_ATOMIC); if (!skb) return -ENOMEM; diff --git a/drivers/dibs/Kconfig b/drivers/dibs/Kconfig index 5dc347b9b235..8f41e5622100 100644 --- a/drivers/dibs/Kconfig +++ b/drivers/dibs/Kconfig @@ -1,18 +1,18 @@ # SPDX-License-Identifier: GPL-2.0 config DIBS - tristate "DIBS support" + tristate "Direct Internal Buffer Sharing support" default n help - Direct Internal Buffer Sharing (DIBS) - A communication method that uses common physical (internal) memory - for synchronous direct access into a remote buffer. + Direct Internal Buffer Sharing (DIBS) is a communication method that + uses common physical (internal) memory for synchronous direct access + into a remote buffer. Select this option to provide the abstraction layer between dibs devices and dibs clients like the SMC protocol. The module name is dibs. config DIBS_LO - bool "intra-OS shortcut with dibs loopback" + bool "Intra-OS shortcut with dibs loopback" depends on DIBS default n help diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index cbb635db4321..2e8690cb3c16 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -71,7 +72,8 @@ void dpll_device_notify(struct dpll_device *dpll, unsigned long action) call_dpll_notifiers(action, &info); } -void dpll_pin_notify(struct dpll_pin *pin, unsigned long action) +void dpll_pin_notify(struct dpll_pin *pin, u64 src_clock_id, + unsigned long action) { struct dpll_pin_notifier_info info = { .pin = pin, @@ -80,6 +82,7 @@ void dpll_pin_notify(struct dpll_pin *pin, unsigned long action) .clock_id = pin->clock_id, .fwnode = pin->fwnode, .prop = &pin->prop, + .src_clock_id = src_clock_id, }; call_dpll_notifiers(action, &info); @@ -652,6 +655,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module, pin->pin_idx = pin_idx; pin->clock_id = clock_id; pin->module = module; + strscpy(pin->module_name, module_name(module)); if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX || prop->type > DPLL_PIN_TYPE_MAX)) { ret = -EINVAL; @@ -847,7 +851,7 @@ __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, if (ret) goto ref_pin_del; xa_set_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED); - dpll_pin_create_ntf(pin); + dpll_pin_create_ntf(pin, dpll->clock_id); return ret; @@ -879,15 +883,26 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, WARN_ON(!ops->direction_get) || WARN_ON(ops->measured_freq_get && (!dpll_device_ops(dpll)->freq_monitor_get || - !dpll_device_ops(dpll)->freq_monitor_set))) + !dpll_device_ops(dpll)->freq_monitor_set)) || + WARN_ON(ops->supported_ffo && !ops->ffo_get)) return -EINVAL; mutex_lock(&dpll_lock); - if (WARN_ON(!(dpll->module == pin->module && - dpll->clock_id == pin->clock_id))) + + /* + * For pins identified via firmware (pin->fwnode), allow registration + * even if the pin's (module, clock_id) differs from the target DPLL. + * For non-fwnode pins, require a strict (module, clock_id) match. + */ + if (!pin->fwnode && + WARN_ON_ONCE(dpll->module != pin->module || + dpll->clock_id != pin->clock_id)) { ret = -EINVAL; - else - ret = __dpll_pin_register(dpll, pin, ops, priv, NULL); + goto out_unlock; + } + + ret = __dpll_pin_register(dpll, pin, ops, priv, NULL); +out_unlock: mutex_unlock(&dpll_lock); return ret; @@ -913,11 +928,13 @@ __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv, void *cookie) { ASSERT_DPLL_PIN_REGISTERED(pin); - dpll_pin_ref_sync_pair_del(pin->id); + dpll_pin_delete_ntf(pin, dpll->clock_id); dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); - if (xa_empty(&pin->dpll_refs)) + if (xa_empty(&pin->dpll_refs)) { + dpll_pin_ref_sync_pair_del(pin->id); xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED); + } } /** @@ -939,7 +956,6 @@ void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, return; mutex_lock(&dpll_lock); - dpll_pin_delete_ntf(pin); __dpll_pin_unregister(dpll, pin, ops, priv, NULL); mutex_unlock(&dpll_lock); } @@ -985,7 +1001,7 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, stop = i; goto dpll_unregister; } - dpll_pin_create_ntf(pin); + dpll_pin_create_ntf(pin, parent->clock_id); } mutex_unlock(&dpll_lock); @@ -994,9 +1010,9 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin, dpll_unregister: xa_for_each(&parent->dpll_refs, i, ref) if (i < stop) { + dpll_pin_delete_ntf(pin, parent->clock_id); __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); - dpll_pin_delete_ntf(pin); } dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); unlock: @@ -1018,14 +1034,19 @@ EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register); void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin, const struct dpll_pin_ops *ops, void *priv) { + struct dpll_pin_registration *reg; struct dpll_pin_ref *ref; unsigned long i; mutex_lock(&dpll_lock); - dpll_pin_delete_ntf(pin); - dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); - xa_for_each(&pin->dpll_refs, i, ref) + xa_for_each(&pin->dpll_refs, i, ref) { + reg = dpll_pin_registration_find(ref, ops, priv, parent); + if (!reg) + continue; + dpll_pin_delete_ntf(pin, parent->clock_id); __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); + } + dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); mutex_unlock(&dpll_lock); } EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister); diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index 71ac88ef2017..e24577113431 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -45,6 +45,7 @@ struct dpll_device { * @pin_idx: index of a pin given by dev driver * @clock_id: clock_id of creator * @module: module of creator + * @module_name: module name of creator * @fwnode: optional reference to firmware node * @dpll_refs: hold referencees to dplls pin was registered with * @parent_refs: hold references to parent pins pin was registered with @@ -59,6 +60,7 @@ struct dpll_pin { u32 pin_idx; u64 clock_id; struct module *module; + char module_name[MODULE_NAME_LEN]; struct fwnode_handle *fwnode; struct xarray dpll_refs; struct xarray parent_refs; @@ -98,6 +100,7 @@ extern struct xarray dpll_pin_xa; extern struct mutex dpll_lock; void dpll_device_notify(struct dpll_device *dpll, unsigned long action); -void dpll_pin_notify(struct dpll_pin *pin, unsigned long action); +void dpll_pin_notify(struct dpll_pin *pin, u64 src_clock_id, + unsigned long action); #endif diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 75e3ae0c16d0..bf729cde796a 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -89,17 +89,6 @@ static struct dpll_pin *dpll_netdev_pin(const struct net_device *dev) return rcu_dereference_rtnl(dev->dpll_pin); } -/** - * dpll_netdev_pin_handle_size - get size of pin handle attribute of a netdev - * @dev: netdev from which to get the pin - * - * Return: byte size of pin handle attribute, or 0 if @dev has no pin. - */ -size_t dpll_netdev_pin_handle_size(const struct net_device *dev) -{ - return dpll_netdev_pin(dev) ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */ -} - int dpll_netdev_add_pin_handle(struct sk_buff *msg, const struct net_device *dev) { @@ -324,6 +313,30 @@ dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin, return 0; } +static int +dpll_msg_add_pin_operstate(struct sk_buff *msg, struct dpll_pin *pin, + struct dpll_pin_ref *ref, + struct netlink_ext_ack *extack) +{ + const struct dpll_pin_ops *ops = dpll_pin_ops(ref); + struct dpll_device *dpll = ref->dpll; + enum dpll_pin_operstate operstate; + int ret; + + if (!ops->operstate_on_dpll_get) + return 0; + ret = ops->operstate_on_dpll_get(pin, + dpll_pin_on_dpll_priv(dpll, pin), + dpll, dpll_priv(dpll), + &operstate, extack); + if (ret) + return ret; + if (nla_put_u32(msg, DPLL_A_PIN_OPERSTATE, operstate)) + return -EMSGSIZE; + + return 0; +} + static int dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin, struct dpll_pin_ref *ref, @@ -393,31 +406,28 @@ dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin, static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin, struct dpll_pin_ref *ref, + enum dpll_ffo_type type, struct netlink_ext_ack *extack) { const struct dpll_pin_ops *ops = dpll_pin_ops(ref); - struct dpll_device *dpll = ref->dpll; - s64 ffo; + struct dpll_ffo_param ffo = { .type = type }; int ret; - if (!ops->ffo_get) + if (!ops->ffo_get || !(ops->supported_ffo & BIT(type))) return 0; - ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(dpll, pin), - dpll, dpll_priv(dpll), &ffo, extack); + ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(ref->dpll, pin), + ref->dpll, dpll_priv(ref->dpll), &ffo, extack); if (ret) { if (ret == -ENODATA) return 0; return ret; } - /* Put the FFO value in PPM to preserve compatibility with older - * programs. - */ - ret = nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET, - div_s64(ffo, 1000000)); - if (ret) + if (nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET, + div_s64(ffo.ffo, 1000000))) return -EMSGSIZE; - return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT, - ffo); + return nla_put_sint(msg, + DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT, + ffo.ffo); } static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin, @@ -650,6 +660,9 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin, if (ret) goto nest_cancel; ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack); + if (ret) + goto nest_cancel; + ret = dpll_msg_add_pin_operstate(msg, pin, ref, extack); if (ret) goto nest_cancel; ret = dpll_msg_add_pin_prio(msg, pin, ref, extack); @@ -659,6 +672,10 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin, if (ret) goto nest_cancel; ret = dpll_msg_add_phase_offset(msg, pin, ref, extack); + if (ret) + goto nest_cancel; + ret = dpll_msg_add_ffo(msg, pin, ref, + DPLL_FFO_PIN_DEVICE, extack); if (ret) goto nest_cancel; nla_nest_end(msg, attr); @@ -686,7 +703,7 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin, if (ret) return ret; if (nla_put_string(msg, DPLL_A_PIN_MODULE_NAME, - module_name(pin->module))) + pin->module_name)) return -EMSGSIZE; if (nla_put_64bit(msg, DPLL_A_PIN_CLOCK_ID, sizeof(pin->clock_id), &pin->clock_id, DPLL_A_PIN_PAD)) @@ -721,7 +738,8 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin, ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack); if (ret) return ret; - ret = dpll_msg_add_ffo(msg, pin, ref, extack); + ret = dpll_msg_add_ffo(msg, pin, ref, + DPLL_FFO_PORT_RXTX_RATE, extack); if (ret) return ret; ret = dpll_msg_add_measured_freq(msg, pin, ref, extack); @@ -897,15 +915,15 @@ dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin) return ret; } -int dpll_pin_create_ntf(struct dpll_pin *pin) +int dpll_pin_create_ntf(struct dpll_pin *pin, u64 src_clock_id) { - dpll_pin_notify(pin, DPLL_PIN_CREATED); + dpll_pin_notify(pin, src_clock_id, DPLL_PIN_CREATED); return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin); } -int dpll_pin_delete_ntf(struct dpll_pin *pin) +int dpll_pin_delete_ntf(struct dpll_pin *pin, u64 src_clock_id) { - dpll_pin_notify(pin, DPLL_PIN_DELETED); + dpll_pin_notify(pin, src_clock_id, DPLL_PIN_DELETED); return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin); } @@ -920,7 +938,7 @@ int dpll_pin_delete_ntf(struct dpll_pin *pin) int __dpll_pin_change_ntf(struct dpll_pin *pin) { lockdep_assert_held(&dpll_lock); - dpll_pin_notify(pin, DPLL_PIN_CHANGED); + dpll_pin_notify(pin, pin->clock_id, DPLL_PIN_CHANGED); return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin); } EXPORT_SYMBOL_GPL(__dpll_pin_change_ntf); @@ -1307,8 +1325,11 @@ dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx, unsigned long i; int ret; + /* fwnode pins may not set the capability bit upfront; let the ops + * layer return -EOPNOTSUPP if the operation is unsupported. + */ if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE & - pin->prop.capabilities)) { + pin->prop.capabilities) && !pin->fwnode) { NL_SET_ERR_MSG(extack, "state changing is not allowed"); return -EOPNOTSUPP; } @@ -1343,8 +1364,11 @@ dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin, struct dpll_pin_ref *ref; int ret; + /* fwnode pins may not set the capability bit upfront; let the ops + * layer return -EOPNOTSUPP if the operation is unsupported. + */ if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE & - pin->prop.capabilities)) { + pin->prop.capabilities) && !pin->fwnode) { NL_SET_ERR_MSG(extack, "state changing is not allowed"); return -EOPNOTSUPP; } @@ -1632,9 +1656,9 @@ dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr, xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) { prop = &pin->prop; cid_match = clock_id ? pin->clock_id == clock_id : true; - mod_match = mod_name_attr && module_name(pin->module) ? + mod_match = mod_name_attr && pin->module_name[0] ? !nla_strcmp(mod_name_attr, - module_name(pin->module)) : true; + pin->module_name) : true; type_match = type ? prop->type == type : true; board_match = board_label ? (prop->board_label ? !nla_strcmp(board_label, prop->board_label) : false) : diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h index a9cfd55f57fc..4f63aa58789a 100644 --- a/drivers/dpll/dpll_netlink.h +++ b/drivers/dpll/dpll_netlink.h @@ -8,6 +8,6 @@ int dpll_device_create_ntf(struct dpll_device *dpll); int dpll_device_delete_ntf(struct dpll_device *dpll); -int dpll_pin_create_ntf(struct dpll_pin *pin); +int dpll_pin_create_ntf(struct dpll_pin *pin, u64 src_clock_id); -int dpll_pin_delete_ntf(struct dpll_pin *pin); +int dpll_pin_delete_ntf(struct dpll_pin *pin, u64 src_clock_id); diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c index 1e652340a5d7..ed3bbe9841ea 100644 --- a/drivers/dpll/dpll_nl.c +++ b/drivers/dpll/dpll_nl.c @@ -12,12 +12,15 @@ #include /* Common nested types */ -const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1] = { +const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_OPERSTATE + 1] = { [DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, }, [DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2), [DPLL_A_PIN_PRIO] = { .type = NLA_U32, }, [DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3), + [DPLL_A_PIN_OPERSTATE] = NLA_POLICY_RANGE(NLA_U32, 1, 4), [DPLL_A_PIN_PHASE_OFFSET] = { .type = NLA_S64, }, + [DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET] = { .type = NLA_SINT, }, + [DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT] = { .type = NLA_SINT, }, }; const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = { @@ -34,7 +37,7 @@ const struct nla_policy dpll_reference_sync_nl_policy[DPLL_A_PIN_STATE + 1] = { static const struct nla_policy dpll_device_id_get_nl_policy[DPLL_A_TYPE + 1] = { [DPLL_A_MODULE_NAME] = { .type = NLA_NUL_STRING, }, [DPLL_A_CLOCK_ID] = { .type = NLA_U64, }, - [DPLL_A_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 2), + [DPLL_A_TYPE] = NLA_POLICY_RANGE(NLA_U32, 1, 3), }; /* DPLL_CMD_DEVICE_GET - do */ diff --git a/drivers/dpll/dpll_nl.h b/drivers/dpll/dpll_nl.h index 7419679b6977..fa8280e3dd14 100644 --- a/drivers/dpll/dpll_nl.h +++ b/drivers/dpll/dpll_nl.h @@ -13,7 +13,7 @@ #include /* Common nested types */ -extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1]; +extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_OPERSTATE + 1]; extern const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1]; extern const struct nla_policy dpll_reference_sync_nl_policy[DPLL_A_PIN_STATE + 1]; diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c index 2f48ca239149..2fe3c3da84bb 100644 --- a/drivers/dpll/zl3073x/chan.c +++ b/drivers/dpll/zl3073x/chan.c @@ -18,6 +18,7 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index) { struct zl3073x_chan *chan = &zldev->chan[index]; + u64 val; int rc; rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index), @@ -25,8 +26,34 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index) if (rc) return rc; - return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index), - &chan->refsel_status); + rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index), + &chan->refsel_status); + if (rc) + return rc; + + /* Read df_offset vs tracked reference */ + rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), + ZL_DPLL_DF_READ_SEM); + if (rc) + return rc; + + rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index), + ZL_DPLL_DF_READ_SEM | ZL_DPLL_DF_READ_REF_OFST); + if (rc) + return rc; + + rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), + ZL_DPLL_DF_READ_SEM); + if (rc) + return rc; + + rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), &val); + if (rc) + return rc; + + chan->df_offset = sign_extend64(val, 47); + + return 0; } /** diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h index 481da2133202..4353809c6912 100644 --- a/drivers/dpll/zl3073x/chan.h +++ b/drivers/dpll/zl3073x/chan.h @@ -17,6 +17,7 @@ struct zl3073x_dev; * @ref_prio: reference priority registers (4 bits per ref, P/N packed) * @mon_status: monitor status register value * @refsel_status: reference selection status register value + * @df_offset: frequency offset vs tracked reference in 2^-48 steps */ struct zl3073x_chan { struct_group(cfg, @@ -26,6 +27,7 @@ struct zl3073x_chan { struct_group(stat, u8 mon_status; u8 refsel_status; + s64 df_offset; ); }; @@ -37,6 +39,18 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index, int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index); +/** + * zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference + * @chan: pointer to channel state + * + * Return: frequency offset in 2^-48 steps + */ +static inline s64 +zl3073x_chan_df_offset_get(const struct zl3073x_chan *chan) +{ + return chan->df_offset; +} + /** * zl3073x_chan_mode_get - get DPLL channel operating mode * @chan: pointer to channel state diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 0a133b0f2d97..8e6416a4741d 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -704,44 +704,6 @@ zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev) return 0; } -/** - * zl3073x_ref_ffo_update - update reference fractional frequency offsets - * @zldev: pointer to zl3073x_dev structure - * - * The function asks device to latch the latest measured fractional - * frequency offset values, reads and stores them into the ref state. - * - * Return: 0 on success, <0 on error - */ -static int -zl3073x_ref_ffo_update(struct zl3073x_dev *zldev) -{ - int i, rc; - - rc = zl3073x_ref_freq_meas_latch(zldev, - ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF); - if (rc) - return rc; - - /* Read DPLL-to-REFx frequency offset measurements */ - for (i = 0; i < ZL3073X_NUM_REFS; i++) { - s32 value; - - /* Read value stored in units of 2^-32 signed */ - rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value); - if (rc) - return rc; - - /* Convert to ppt - * ffo = (10^12 * value) / 2^32 - * ffo = ( 5^12 * value) / 2^20 - */ - zldev->ref[i].ffo = mul_s64_u64_shr(value, 244140625, 20); - } - - return 0; -} - static void zl3073x_dev_periodic_work(struct kthread_work *work) { @@ -773,13 +735,6 @@ zl3073x_dev_periodic_work(struct kthread_work *work) ERR_PTR(rc)); } - /* Update references' fractional frequency offsets */ - rc = zl3073x_ref_ffo_update(zldev); - if (rc) - dev_warn(zldev->dev, - "Failed to update fractional frequency offsets: %pe\n", - ERR_PTR(rc)); - list_for_each_entry(zldpll, &zldev->dplls, list) zl3073x_dpll_changes_check(zldpll); diff --git a/drivers/dpll/zl3073x/devlink.c b/drivers/dpll/zl3073x/devlink.c index ccc22332b346..218b08fd8a30 100644 --- a/drivers/dpll/zl3073x/devlink.c +++ b/drivers/dpll/zl3073x/devlink.c @@ -315,10 +315,10 @@ EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X"); static int zl3073x_devlink_param_clock_id_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - if (!val.vu64) { + if (!val->vu64) { NL_SET_ERR_MSG_MOD(extack, "'clock_id' must be non-zero"); return -EINVAL; } @@ -377,7 +377,7 @@ int zl3073x_devlink_register(struct zl3073x_dev *zldev) value.vu64 = zldev->clock_id; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_CLOCK_ID, - value); + &value); /* Register devlink instance */ devl_register(devlink); diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 0bfcbae2109f..5e58ded5734d 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -38,7 +39,7 @@ * @prio: pin priority <0, 14> * @esync_control: embedded sync is controllable * @phase_gran: phase adjustment granularity - * @pin_state: last saved pin state + * @operstate: last saved operational state * @phase_offset: last saved pin phase offset * @freq_offset: last saved fractional frequency offset * @measured_freq: last saved measured frequency @@ -55,9 +56,9 @@ struct zl3073x_dpll_pin { u8 prio; bool esync_control; s32 phase_gran; - enum dpll_pin_state pin_state; + enum dpll_pin_operstate operstate; s64 phase_offset; - s64 freq_offset; + atomic64_t freq_offset; u32 measured_freq; }; @@ -295,11 +296,15 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin, static int zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv, const struct dpll_device *dpll, void *dpll_priv, - s64 *ffo, struct netlink_ext_ack *extack) + struct dpll_ffo_param *ffo, + struct netlink_ext_ack *extack) { struct zl3073x_dpll_pin *pin = pin_priv; - *ffo = pin->freq_offset; + if (pin->operstate != DPLL_PIN_OPERSTATE_ACTIVE) + return -ENODATA; + + ffo->ffo = atomic64_read(&pin->freq_offset); return 0; } @@ -500,46 +505,41 @@ zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, } /** - * zl3073x_dpll_ref_state_get - get status for given input pin + * zl3073x_dpll_ref_operstate_get - get operational state for input pin * @pin: pointer to pin - * @state: place to store status + * @operstate: place to store operational state * - * Checks current status for the given input pin and stores the value - * to @state. + * Returns the actual hardware state of the pin: whether it is actively + * used by the DPLL, has no signal, failed qualification, or is simply + * not in use. * * Return: 0 on success, <0 on error */ static int -zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin, - enum dpll_pin_state *state) +zl3073x_dpll_ref_operstate_get(struct zl3073x_dpll_pin *pin, + enum dpll_pin_operstate *operstate) { struct zl3073x_dpll *zldpll = pin->dpll; struct zl3073x_dev *zldev = zldpll->dev; - const struct zl3073x_chan *chan; - u8 ref; + const struct zl3073x_ref *ref; + u8 ref_id; - chan = zl3073x_chan_state_get(zldev, zldpll->id); - ref = zl3073x_input_pin_ref_get(pin->id); + ref_id = zl3073x_input_pin_ref_get(pin->id); - /* Check if the pin reference is connected */ - if (ref == zl3073x_dpll_connected_ref_get(zldpll)) { - *state = DPLL_PIN_STATE_CONNECTED; + /* Check if this pin is the currently locked reference */ + if (ref_id == zl3073x_dpll_connected_ref_get(zldpll)) { + *operstate = DPLL_PIN_OPERSTATE_ACTIVE; return 0; } - /* If the DPLL is running in automatic mode and the reference is - * selectable and its monitor does not report any error then report - * pin as selectable. - */ - if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO && - zl3073x_dev_ref_is_status_ok(zldev, ref) && - zl3073x_chan_ref_is_selectable(chan, ref)) { - *state = DPLL_PIN_STATE_SELECTABLE; - return 0; - } - - /* Otherwise report the pin as disconnected */ - *state = DPLL_PIN_STATE_DISCONNECTED; + /* Check reference monitor status */ + ref = zl3073x_ref_state_get(zldev, ref_id); + if (ref->mon_status & ZL_REF_MON_STATUS_LOS) + *operstate = DPLL_PIN_OPERSTATE_NO_SIGNAL; + else if (!zl3073x_ref_is_status_ok(ref)) + *operstate = DPLL_PIN_OPERSTATE_QUAL_FAILED; + else + *operstate = DPLL_PIN_OPERSTATE_STANDBY; return 0; } @@ -551,10 +551,48 @@ zl3073x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, void *dpll_priv, enum dpll_pin_state *state, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll = dpll_priv; + struct zl3073x_dpll_pin *pin = pin_priv; + const struct zl3073x_chan *chan; + u8 mode, ref; + + chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id); + ref = zl3073x_input_pin_ref_get(pin->id); + mode = zl3073x_chan_mode_get(chan); + + switch (mode) { + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + if (ref == zl3073x_chan_ref_get(chan)) + *state = DPLL_PIN_STATE_CONNECTED; + else + *state = DPLL_PIN_STATE_DISCONNECTED; + break; + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + if (zl3073x_chan_ref_is_selectable(chan, ref)) + *state = DPLL_PIN_STATE_SELECTABLE; + else + *state = DPLL_PIN_STATE_DISCONNECTED; + break; + default: + *state = DPLL_PIN_STATE_DISCONNECTED; + break; + } + + return 0; +} + +static int +zl3073x_dpll_input_pin_operstate_on_dpll_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_operstate *operstate, + struct netlink_ext_ack *extack) { struct zl3073x_dpll_pin *pin = pin_priv; - return zl3073x_dpll_ref_state_get(pin, state); + return zl3073x_dpll_ref_operstate_get(pin, operstate); } static int @@ -1244,6 +1282,7 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll, } static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = { + .supported_ffo = BIT(DPLL_FFO_PIN_DEVICE), .direction_get = zl3073x_dpll_pin_direction_get, .esync_get = zl3073x_dpll_input_pin_esync_get, .esync_set = zl3073x_dpll_input_pin_esync_set, @@ -1251,6 +1290,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = { .frequency_get = zl3073x_dpll_input_pin_frequency_get, .frequency_set = zl3073x_dpll_input_pin_frequency_set, .measured_freq_get = zl3073x_dpll_input_pin_measured_freq_get, + .operstate_on_dpll_get = zl3073x_dpll_input_pin_operstate_on_dpll_get, .phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get, .phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get, .phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set, @@ -1668,7 +1708,7 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin) * 2) For other pins use appropriate ref_phase register if the phase * monitor feature is enabled. */ - if (pin->pin_state == DPLL_PIN_STATE_CONNECTED) + if (pin->operstate == DPLL_PIN_OPERSTATE_ACTIVE) reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id); else if (zldpll->phase_monitor) reg = ZL_REG_REF_PHASE(ref_id); @@ -1700,37 +1740,29 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin) } /** - * zl3073x_dpll_pin_ffo_check - check for pin fractional frequency offset change + * zl3073x_dpll_pin_ffo_check - check for FFO change on active pin * @pin: pin to check * - * Check for the given pin's fractional frequency change. - * - * Return: true on fractional frequency offset change, false otherwise + * Return: true on change, false otherwise */ static bool zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin) { struct zl3073x_dpll *zldpll = pin->dpll; struct zl3073x_dev *zldev = zldpll->dev; - const struct zl3073x_ref *ref; - u8 ref_id; + const struct zl3073x_chan *chan; s64 ffo; - /* Get reference monitor status */ - ref_id = zl3073x_input_pin_ref_get(pin->id); - ref = zl3073x_ref_state_get(zldev, ref_id); - - /* Do not report ffo changes if the reference monitor report errors */ - if (!zl3073x_ref_is_status_ok(ref)) + if (pin->operstate != DPLL_PIN_OPERSTATE_ACTIVE) return false; - /* Compare with previous value */ - ffo = zl3073x_ref_ffo_get(ref); - if (pin->freq_offset != ffo) { - dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n", - pin->label, pin->freq_offset, ffo); - pin->freq_offset = ffo; + chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id); + ffo = mul_s64_u64_shr(zl3073x_chan_df_offset_get(chan), + 244140625, 36); + if (atomic64_xchg(&pin->freq_offset, ffo) != ffo) { + dev_dbg(zldev->dev, "%s freq offset changed to: %lld\n", + pin->label, ffo); return true; } @@ -1822,7 +1854,7 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) } list_for_each_entry(pin, &zldpll->pins, list) { - enum dpll_pin_state state; + enum dpll_pin_operstate operstate; bool pin_changed = false; /* Output pins change checks are not necessary because output @@ -1831,18 +1863,18 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) if (!zl3073x_dpll_is_input_pin(pin)) continue; - rc = zl3073x_dpll_ref_state_get(pin, &state); + rc = zl3073x_dpll_ref_operstate_get(pin, &operstate); if (rc) { dev_err(dev, - "Failed to get %s on DPLL%u state: %pe\n", + "Failed to get %s on DPLL%u oper state: %pe\n", pin->label, zldpll->id, ERR_PTR(rc)); return; } - if (state != pin->pin_state) { - dev_dbg(dev, "%s state changed: %u->%u\n", pin->label, - pin->pin_state, state); - pin->pin_state = state; + if (operstate != pin->operstate) { + dev_dbg(dev, "%s oper state changed: %u->%u\n", + pin->label, pin->operstate, operstate); + pin->operstate = operstate; pin_changed = true; } diff --git a/drivers/dpll/zl3073x/i2c.c b/drivers/dpll/zl3073x/i2c.c index 979df85826ab..4a23340e29d0 100644 --- a/drivers/dpll/zl3073x/i2c.c +++ b/drivers/dpll/zl3073x/i2c.c @@ -26,11 +26,11 @@ static int zl3073x_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id zl3073x_i2c_id[] = { - { "zl30731" }, - { "zl30732" }, - { "zl30733" }, - { "zl30734" }, - { "zl30735" }, + { .name = "zl30731" }, + { .name = "zl30732" }, + { .name = "zl30733" }, + { .name = "zl30734" }, + { .name = "zl30735" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id); diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h index 55e80e4f0873..e140ca3ea17d 100644 --- a/drivers/dpll/zl3073x/ref.h +++ b/drivers/dpll/zl3073x/ref.h @@ -22,7 +22,6 @@ struct zl3073x_dev; * @freq_ratio_n: FEC mode divisor * @sync_ctrl: reference sync control * @config: reference config - * @ffo: current fractional frequency offset * @meas_freq: measured input frequency in Hz * @mon_status: reference monitor status */ @@ -40,7 +39,6 @@ struct zl3073x_ref { u8 config; ); struct_group(stat, /* Status */ - s64 ffo; u32 meas_freq; u8 mon_status; ); @@ -58,18 +56,6 @@ int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index); int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult); -/** - * zl3073x_ref_ffo_get - get current fractional frequency offset - * @ref: pointer to ref state - * - * Return: the latest measured fractional frequency offset - */ -static inline s64 -zl3073x_ref_ffo_get(const struct zl3073x_ref *ref) -{ - return ref->ffo; -} - /** * zl3073x_ref_meas_freq_get - get measured input frequency * @ref: pointer to ref state diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index d425dc67250f..9578f0009528 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -98,7 +98,14 @@ #define ZL_REG_REF_MON_STATUS(_idx) \ ZL_REG_IDX(_idx, 2, 0x02, 1, ZL3073X_NUM_REFS, 1) -#define ZL_REF_MON_STATUS_OK 0 /* all bits zeroed */ +#define ZL_REF_MON_STATUS_OK 0 +#define ZL_REF_MON_STATUS_LOS BIT(0) +#define ZL_REF_MON_STATUS_SCM BIT(1) +#define ZL_REF_MON_STATUS_CFM BIT(2) +#define ZL_REF_MON_STATUS_GST BIT(3) +#define ZL_REF_MON_STATUS_PFM BIT(4) +#define ZL_REF_MON_STATUS_ESYNC BIT(6) +#define ZL_REF_MON_STATUS_SPLIT_XO BIT(7) #define ZL_REG_DPLL_MON_STATUS(_idx) \ ZL_REG_IDX(_idx, 2, 0x10, 1, ZL3073X_MAX_CHANNELS, 1) @@ -157,6 +164,11 @@ #define ZL_DPLL_MODE_REFSEL_MODE_NCO 4 #define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4) +#define ZL_REG_DPLL_DF_READ(_idx) \ + ZL_REG_IDX(_idx, 5, 0x28, 1, ZL3073X_MAX_CHANNELS, 1) +#define ZL_DPLL_DF_READ_SEM BIT(4) +#define ZL_DPLL_DF_READ_REF_OFST BIT(3) + #define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1) #define ZL_DPLL_MEAS_CTRL_EN BIT(0) #define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4) @@ -169,6 +181,16 @@ #define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \ ZL_REG_IDX(_idx, 5, 0x55, 6, ZL3073X_MAX_CHANNELS, 6) +/******************************* + * Register Pages 6-7, DPLL Data + *******************************/ + +#define ZL_REG_DPLL_DF_OFFSET_03(_idx) \ + ZL_REG_IDX(_idx, 6, 0x00, 6, 4, 0x20) +#define ZL_REG_DPLL_DF_OFFSET_4 ZL_REG(7, 0x00, 6) +#define ZL_REG_DPLL_DF_OFFSET(_idx) \ + ((_idx) < 4 ? ZL_REG_DPLL_DF_OFFSET_03(_idx) : ZL_REG_DPLL_DF_OFFSET_4) + /*********************************** * Register Page 9, Synth and Output ***********************************/ diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index 2ad9f6ac6636..85bd994d15d4 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -11,6 +11,7 @@ #include #include #include +#include /* For WLAN GPIOs */ #include #include #include @@ -214,6 +215,56 @@ static const struct of_device_id ath79_gpio_of_match[] = { }; MODULE_DEVICE_TABLE(of, ath79_gpio_of_match); +#if IS_ENABLED(CONFIG_ATH9K_AHB) +/* + * This registers all of the ath79k GPIOs as descriptors to be picked + * directly from the ATH79K wifi driver if the two are jitted together + * in the same SoC. + */ +#define ATH79K_WIFI_DESCS 32 +static int ath79_gpio_register_wifi_descriptors(struct device *dev, + const char *label) +{ + struct gpiod_lookup_table *lookup; + int i; + + /* Create a gpiod lookup using gpiochip-local offsets + 1 for NULL */ + lookup = devm_kzalloc(dev, + struct_size(lookup, table, ATH79K_WIFI_DESCS + 1), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + /* + * Ugly system-wide lookup for the NULL device: we know this + * is already NULL but explicitly assign it here for people to + * know what is going on. (Yes this is an ugly legacy hack, live + * with it.) + */ + lookup->dev_id = NULL; + + for (i = 0; i < ATH79K_WIFI_DESCS; i++) { + lookup->table[i] = + /* + * Set the HW offset on the chip and the lookup + * index to the same value, so looking up index 0 + * will get HW offset 0, index 1 HW offset 1 etc. + */ + GPIO_LOOKUP_IDX(label, i, "ath9k", i, GPIO_ACTIVE_HIGH); + } + + gpiod_add_lookup_table(lookup); + + return 0; +} +#else +static int ath79_gpio_register_wifi_descriptors(struct device *dev, + const char *label) +{ + return 0; +} +#endif + static int ath79_gpio_probe(struct platform_device *pdev) { struct gpio_generic_chip_config config; @@ -276,7 +327,11 @@ static int ath79_gpio_probe(struct platform_device *pdev) girq->handler = handle_simple_irq; } - return devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl); + err = devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl); + if (err) + return err; + + return ath79_gpio_register_wifi_descriptors(dev, ctrl->chip.gc.label); } static struct platform_driver ath79_gpio_driver = { diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index afc2fc124fee..142047847f38 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -20,8 +20,10 @@ void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd, pd->vport_use_count--; WARN_ON(pd->vport_use_count < 0); - if (!pd->vport_use_count) + if (!pd->vport_use_count) { + mana_destroy_eq(mpc); mana_uncfg_vport(mpc); + } mutex_unlock(&pd->vport_mutex); } @@ -40,13 +42,27 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd, pd->vport_use_count++; if (pd->vport_use_count > 1) { + /* Reject cross-port PD sharing. EQs and vport config + * are per-port, so the PD must stay bound to the port + * that was configured on the first raw QP creation. + */ + if (pd->vport_port != port) { + pd->vport_use_count--; + mutex_unlock(&pd->vport_mutex); + ibdev_dbg(&dev->ib_dev, + "PD already bound to port %u\n", + pd->vport_port); + return -EINVAL; + } ibdev_dbg(&dev->ib_dev, "Skip as this PD is already configured vport\n"); mutex_unlock(&pd->vport_mutex); return 0; } - err = mana_cfg_vport(mpc, pd->pdn, doorbell_id); + pd->vport_port = port; + + err = mana_cfg_vport(mpc, pd->pdn, doorbell_id, true); if (err) { pd->vport_use_count--; mutex_unlock(&pd->vport_mutex); @@ -55,15 +71,23 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd, return err; } + + err = mana_create_eq(mpc); + if (err) { + mana_uncfg_vport(mpc); + pd->vport_use_count--; + } else { + pd->tx_shortform_allowed = mpc->tx_shortform_allowed; + pd->tx_vp_offset = mpc->tx_vp_offset; + } + mutex_unlock(&pd->vport_mutex); - pd->tx_shortform_allowed = mpc->tx_shortform_allowed; - pd->tx_vp_offset = mpc->tx_vp_offset; + if (!err) + ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n", + mpc->port_handle, pd->pdn, doorbell_id); - ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n", - mpc->port_handle, pd->pdn, doorbell_id); - - return 0; + return err; } int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) @@ -741,7 +765,8 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev) { struct gdma_context *gc = mdev_to_gc(mdev); struct gdma_queue_spec spec = {}; - int err, i; + struct gdma_irq_context *gic; + int err, i, msi; spec.type = GDMA_EQ; spec.monitor_avl_buf = false; @@ -749,11 +774,19 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev) spec.eq.callback = mana_ib_event_handler; spec.eq.context = mdev; spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE; - spec.eq.msix_index = 0; + + msi = 0; + gic = mana_gd_get_gic(gc, false, &msi); + if (IS_ERR(gic)) + return PTR_ERR(gic); + spec.eq.msix_index = msi; err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->fatal_err_eq); - if (err) + if (err) { + mana_gd_put_gic(gc, false, 0); return err; + } + mdev->fatal_err_eq->eq.irq = gic->irq; mdev->eqs = kzalloc_objs(struct gdma_queue *, mdev->ib_dev.num_comp_vectors); @@ -763,32 +796,50 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev) } spec.eq.callback = NULL; for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) { - spec.eq.msix_index = (i + 1) % gc->num_msix_usable; - err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->eqs[i]); - if (err) + msi = (i + 1) % gc->num_msix_usable; + + gic = mana_gd_get_gic(gc, false, &msi); + if (IS_ERR(gic)) { + err = PTR_ERR(gic); goto destroy_eqs; + } + spec.eq.msix_index = msi; + + err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->eqs[i]); + if (err) { + mana_gd_put_gic(gc, false, msi); + goto destroy_eqs; + } + mdev->eqs[i]->eq.irq = gic->irq; } return 0; destroy_eqs: - while (i-- > 0) + while (i-- > 0) { mana_gd_destroy_queue(gc, mdev->eqs[i]); + mana_gd_put_gic(gc, false, (i + 1) % gc->num_msix_usable); + } kfree(mdev->eqs); destroy_fatal_eq: mana_gd_destroy_queue(gc, mdev->fatal_err_eq); + mana_gd_put_gic(gc, false, 0); return err; } void mana_ib_destroy_eqs(struct mana_ib_dev *mdev) { struct gdma_context *gc = mdev_to_gc(mdev); - int i; + int i, msi; mana_gd_destroy_queue(gc, mdev->fatal_err_eq); + mana_gd_put_gic(gc, false, 0); - for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) + for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) { mana_gd_destroy_queue(gc, mdev->eqs[i]); + msi = (i + 1) % gc->num_msix_usable; + mana_gd_put_gic(gc, false, msi); + } kfree(mdev->eqs); } diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h index c9c94e86a72b..da05966aff19 100644 --- a/drivers/infiniband/hw/mana/mana_ib.h +++ b/drivers/infiniband/hw/mana/mana_ib.h @@ -102,6 +102,20 @@ struct mana_ib_pd { struct mutex vport_mutex; int vport_use_count; + /* Port bound to this PD for raw QP usage. Only valid when + * vport_use_count > 0. A PD can only be associated with a + * single physical port because per-port EQs and vport + * configuration are tied to the PD's refcount. + */ + u32 vport_port; + + /* Only one RSS QP is allowed per vport because each RSS QP + * overwrites the vport steering config (indirection table / + * hash key) and mana_disable_vport_rx() on destroy would + * blackhole traffic for any other RSS QP on the same vport. + */ + bool has_rss_qp; + bool tx_shortform_allowed; u32 tx_vp_offset; }; diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c index 0fbcf449c134..d3ee30b64f53 100644 --- a/drivers/infiniband/hw/mana/qp.c +++ b/drivers/infiniband/hw/mana/qp.c @@ -79,6 +79,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, struct ib_qp_init_attr *attr, struct ib_udata *udata) { + struct mana_ib_pd *mana_pd = container_of(pd, struct mana_ib_pd, ibpd); struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); struct mana_ib_dev *mdev = container_of(pd->device, struct mana_ib_dev, ib_dev); @@ -155,6 +156,30 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, qp->port = port; + /* Take a reference on the vport to ensure EQs outlive this QP. + * The vport must already be configured by a raw QP on the + * same port — cross-port PD sharing is not supported. + * Only one RSS QP per vport is allowed because each one + * overwrites the steering config and destroy disables RX + * globally. + */ + mutex_lock(&mana_pd->vport_mutex); + if (!mana_pd->vport_use_count || mana_pd->vport_port != port) { + mutex_unlock(&mana_pd->vport_mutex); + ret = -EINVAL; + goto fail; + } + if (mana_pd->has_rss_qp) { + mutex_unlock(&mana_pd->vport_mutex); + ibdev_dbg(&mdev->ib_dev, + "Only one RSS QP per vport is supported\n"); + ret = -EBUSY; + goto fail; + } + mana_pd->vport_use_count++; + mana_pd->has_rss_qp = true; + mutex_unlock(&mana_pd->vport_mutex); + for (i = 0; i < ind_tbl_size; i++) { struct mana_obj_spec wq_spec = {}; struct mana_obj_spec cq_spec = {}; @@ -171,13 +196,19 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, cq_spec.gdma_region = cq->queue.gdma_region; cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE; cq_spec.modr_ctx_id = 0; - eq = &mpc->ac->eqs[cq->comp_vector]; + /* Map comp_vector to a per-vPort EQ. The modulo handles + * the case where the RDMA-advertised num_comp_vectors + * exceeds this port's num_queues (e.g. after ethtool -L + * reduces it), remapping to an available EQ rather than + * failing the QP creation. + */ + eq = &mpc->eqs[cq->comp_vector % mpc->num_queues]; cq_spec.attached_eq = eq->eq->id; ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ, &wq_spec, &cq_spec, &wq->rx_object); if (ret) - goto fail; + goto free_vport; /* The GDMA regions are now owned by the WQ object */ wq->queue.gdma_region = GDMA_INVALID_DMA_REGION; @@ -199,7 +230,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, ret = mana_ib_install_cq_cb(mdev, cq); if (ret) { mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); - goto fail; + goto free_vport; } } resp.num_entries = i; @@ -210,7 +241,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, ucmd.rx_hash_key_len, ucmd.rx_hash_key); if (ret) - goto fail; + goto free_vport; ret = ib_copy_to_udata(udata, &resp, sizeof(resp)); if (ret) { @@ -226,7 +257,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, err_disable_vport_rx: mana_disable_vport_rx(mpc); -fail: +free_vport: while (i-- > 0) { ibwq = ind_tbl->ind_tbl[i]; ibcq = ibwq->cq; @@ -237,6 +268,13 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); } + mutex_lock(&mana_pd->vport_mutex); + mana_pd->has_rss_qp = false; + mutex_unlock(&mana_pd->vport_mutex); + + mana_ib_uncfg_vport(mdev, mana_pd, port); + +fail: kfree(mana_ind_table); return ret; @@ -299,7 +337,7 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd, err = mana_ib_cfg_vport(mdev, port, pd, mana_ucontext->doorbell); if (err) - return -ENODEV; + return err; qp->port = port; @@ -321,7 +359,14 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd, cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE; cq_spec.modr_ctx_id = 0; eq_vec = send_cq->comp_vector; - eq = &mpc->ac->eqs[eq_vec]; + if (!mpc->eqs) { + err = -EINVAL; + goto err_destroy_queue; + } + /* Map comp_vector to a per-vPort EQ. See comment in + * mana_ib_create_qp_rss() for the modulo rationale. + */ + eq = &mpc->eqs[eq_vec % mpc->num_queues]; cq_spec.attached_eq = eq->eq->id; err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec, @@ -785,14 +830,17 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, { struct mana_ib_dev *mdev = container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); + struct ib_pd *ibpd = qp->ibqp.pd; struct mana_port_context *mpc; struct net_device *ndev; + struct mana_ib_pd *pd; struct mana_ib_wq *wq; struct ib_wq *ibwq; int i; ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port); mpc = netdev_priv(ndev); + pd = container_of(ibpd, struct mana_ib_pd, ibpd); /* Disable vPort RX steering before destroying RX WQ objects. * Otherwise firmware still routes traffic to the destroyed queues, @@ -817,6 +865,12 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); } + mutex_lock(&pd->vport_mutex); + pd->has_rss_qp = false; + mutex_unlock(&pd->vport_mutex); + + mana_ib_uncfg_vport(mdev, pd, qp->port); + return 0; } diff --git a/drivers/infiniband/hw/mlx5/counters.c b/drivers/infiniband/hw/mlx5/counters.c index 5b4482dd6274..5a79e834ddea 100644 --- a/drivers/infiniband/hw/mlx5/counters.c +++ b/drivers/infiniband/hw/mlx5/counters.c @@ -697,7 +697,7 @@ static void mlx5_ib_fill_counters(struct mlx5_ib_dev *dev, u32 port_num) { bool is_vport = is_mdev_switchdev_mode(dev->mdev) && - port_num != MLX5_VPORT_PF; + port_num != MLX5_VPORT_HOST_PF; const struct mlx5_ib_counter *names; int j = 0, i, size; @@ -802,7 +802,7 @@ static int __mlx5_ib_alloc_counters(struct mlx5_ib_dev *dev, struct mlx5_ib_counters *cnts, u32 port_num) { bool is_vport = is_mdev_switchdev_mode(dev->mdev) && - port_num != MLX5_VPORT_PF; + port_num != MLX5_VPORT_HOST_PF; u32 num_counters, num_op_counters = 0, size; size = is_vport ? ARRAY_SIZE(vport_basic_q_cnts) : diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c index 1709b628702e..65d8767d1830 100644 --- a/drivers/infiniband/hw/mlx5/ib_rep.c +++ b/drivers/infiniband/hw/mlx5/ib_rep.c @@ -262,9 +262,10 @@ mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep) struct mlx5_core_dev *peer_mdev; struct mlx5_eswitch *esw; + /* Called while the master E-Switch reps_lock is held. */ mlx5_lag_for_each_peer_mdev(mdev, peer_mdev, i) { esw = peer_mdev->priv.eswitch; - mlx5_eswitch_unregister_vport_reps(esw, REP_IB); + mlx5_eswitch_unregister_vport_reps_nested(esw, REP_IB); } mlx5_ib_release_transport(mdev); } @@ -284,9 +285,10 @@ static void mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev *mdev) struct mlx5_eswitch *esw; int i; + /* Called while the master E-Switch reps_lock is held. */ mlx5_lag_for_each_peer_mdev(mdev, peer_mdev, i) { esw = peer_mdev->priv.eswitch; - mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB); + mlx5_eswitch_register_vport_reps_nested(esw, &rep_ops, REP_IB); } } diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 50a2cb5405e2..082ff387d081 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -283,12 +283,12 @@ static struct socket *rxe_setup_udp_tunnel(struct net *net, __be16 port, tnl_cfg.encap_rcv = rxe_udp_encap_recv; /* Setup UDP tunnel */ - setup_udp_tunnel_sock(net, sock, &tnl_cfg); + setup_udp_tunnel_sock(net, sock->sk, &tnl_cfg); return sock; } -static void rxe_release_udp_tunnel(struct socket *sk) +static void rxe_release_udp_tunnel(struct sock *sk) { if (sk) udp_tunnel_sock_release(sk); @@ -636,7 +636,7 @@ static void rxe_sock_put(struct sock *sk, if (refcount_read(&sk->sk_refcnt) > SK_REF_FOR_TUNNEL) { __sock_put(sk); } else { - rxe_release_udp_tunnel(sk->sk_socket); + rxe_release_udp_tunnel(sk); sk = NULL; set_sk(net, sk); } diff --git a/drivers/infiniband/sw/rxe/rxe_ns.c b/drivers/infiniband/sw/rxe/rxe_ns.c index 8b9d734229b2..64621c89f8bf 100644 --- a/drivers/infiniband/sw/rxe/rxe_ns.c +++ b/drivers/infiniband/sw/rxe/rxe_ns.c @@ -47,7 +47,7 @@ static void rxe_ns_exit(struct net *net) rcu_read_unlock(); if (sk) { rcu_assign_pointer(ns_sk->rxe_sk4, NULL); - udp_tunnel_sock_release(sk->sk_socket); + udp_tunnel_sock_release(sk); } #if IS_ENABLED(CONFIG_IPV6) @@ -56,7 +56,7 @@ static void rxe_ns_exit(struct net *net) rcu_read_unlock(); if (sk) { rcu_assign_pointer(ns_sk->rxe_sk6, NULL); - udp_tunnel_sock_release(sk->sk_socket); + udp_tunnel_sock_release(sk); } #endif } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 3e1e1e861739..16a015b67206 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1297,18 +1297,19 @@ static int ipoib_hard_header(struct sk_buff *skb, return IPOIB_HARD_LEN; } -static void ipoib_set_rx_mode_async(struct net_device *dev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int ipoib_set_rx_mode_async(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct ipoib_dev_priv *priv = ipoib_priv(dev); if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) { ipoib_dbg(priv, "IPOIB_FLAG_OPER_UP not set"); - return; + return 0; } queue_work(priv->wq, &priv->restart_task); + return 0; } static int ipoib_get_iflink(const struct net_device *dev) diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 12cb3311ea22..64c078e997f2 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../leds.h" #define NETDEV_LED_DEFAULT_INTERVAL 50 @@ -228,7 +229,7 @@ static void get_device_state(struct led_netdev_data *trigger_data) trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); - if (__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) + if (netif_get_link_ksettings(trigger_data->net_dev, &cmd)) return; if (trigger_data->carrier_link_up) { @@ -259,31 +260,33 @@ static ssize_t device_name_show(struct device *dev, static int set_device_name(struct led_netdev_data *trigger_data, const char *name, size_t size) { + struct net_device *new_dev = NULL; + char device_name[IFNAMSIZ]; + if (size >= IFNAMSIZ) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); + memcpy(device_name, name, size); + device_name[size] = 0; + if (size > 0 && device_name[size - 1] == '\n') + device_name[size - 1] = 0; + /* - * Take RTNL lock before trigger_data lock to prevent potential - * deadlock with netdev notifier registration. + * Lock order: rtnl_lock -> netdev instance lock -> trigger_data lock. */ rtnl_lock(); + if (device_name[0]) { + new_dev = dev_get_by_name(&init_net, device_name); + if (new_dev) + netdev_lock_ops(new_dev); + } mutex_lock(&trigger_data->lock); - if (trigger_data->net_dev) { - dev_put(trigger_data->net_dev); - trigger_data->net_dev = NULL; - } - - memcpy(trigger_data->device_name, name, size); - trigger_data->device_name[size] = 0; - if (size > 0 && trigger_data->device_name[size - 1] == '\n') - trigger_data->device_name[size - 1] = 0; - - if (trigger_data->device_name[0] != 0) - trigger_data->net_dev = - dev_get_by_name(&init_net, trigger_data->device_name); + dev_put(trigger_data->net_dev); + trigger_data->net_dev = new_dev; + strscpy(trigger_data->device_name, device_name); trigger_data->carrier_link_up = false; trigger_data->link_speed = SPEED_UNKNOWN; @@ -298,6 +301,8 @@ static int set_device_name(struct led_netdev_data *trigger_data, set_baseline_state(trigger_data); mutex_unlock(&trigger_data->lock); + if (new_dev) + netdev_unlock_ops(new_dev); rtnl_unlock(); return 0; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 8ec98f6dfef9..ff79c466712d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -630,11 +630,4 @@ config NET_FAILOVER a VM with direct attached VF by failing over to the paravirtual datapath when the VF is unplugged. -config NETDEV_LEGACY_INIT - bool - depends on ISA - help - Drivers that call netdev_boot_setup_check() should select this - symbol, everything else no longer needs it. - endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b87a741fc952..88e4c485d6b2 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_MACVTAP) += macvtap.o obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_MDIO) += mdio.o obj-$(CONFIG_NET) += loopback.o -obj-$(CONFIG_NETDEV_LEGACY_INIT) += Space.o obj-$(CONFIG_NETCONSOLE) += netconsole.o obj-$(CONFIG_NETKIT) += netkit.o obj-y += phy/ diff --git a/drivers/net/Space.c b/drivers/net/Space.c deleted file mode 100644 index 305f0a712a64..000000000000 --- a/drivers/net/Space.c +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * Holds initial configuration information for devices. - * - * Version: @(#)Space.c 1.0.7 08/12/93 - * - * Authors: Ross Biro - * Fred N. van Kempen, - * Donald J. Becker, - * - * Changelog: - * Stephen Hemminger (09/2003) - * - get rid of pre-linked dev list, dynamic device allocation - * Paul Gortmaker (03/2002) - * - struct init cleanup, enable multiple ISA autoprobes. - * Arnaldo Carvalho de Melo - 09/1999 - * - fix sbni: s/device/net_device/ - * Paul Gortmaker (06/98): - * - sort probes in a sane way, make sure all (safe) probes - * get run once & failed autoprobes don't autoprobe again. - */ -#include -#include -#include -#include -#include -#include - -/* - * This structure holds boot-time configured netdevice settings. They - * are then used in the device probing. - */ -struct netdev_boot_setup { - char name[IFNAMSIZ]; - struct ifmap map; -}; -#define NETDEV_BOOT_SETUP_MAX 8 - - -/****************************************************************************** - * - * Device Boot-time Settings Routines - * - ******************************************************************************/ - -/* Boot time configuration table */ -static struct netdev_boot_setup dev_boot_setup[NETDEV_BOOT_SETUP_MAX]; - -/** - * netdev_boot_setup_add - add new setup entry - * @name: name of the device - * @map: configured settings for the device - * - * Adds new setup entry to the dev_boot_setup list. The function - * returns 0 on error and 1 on success. This is a generic routine to - * all netdevices. - */ -static int netdev_boot_setup_add(char *name, struct ifmap *map) -{ - struct netdev_boot_setup *s; - int i; - - s = dev_boot_setup; - for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { - if (s[i].name[0] == '\0' || s[i].name[0] == ' ') { - strscpy_pad(s[i].name, name); - memcpy(&s[i].map, map, sizeof(s[i].map)); - break; - } - } - - return i >= NETDEV_BOOT_SETUP_MAX ? 0 : 1; -} - -/** - * netdev_boot_setup_check - check boot time settings - * @dev: the netdevice - * - * Check boot time settings for the device. - * The found settings are set for the device to be used - * later in the device probing. - * Returns 0 if no settings found, 1 if they are. - */ -int netdev_boot_setup_check(struct net_device *dev) -{ - struct netdev_boot_setup *s = dev_boot_setup; - int i; - - for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { - if (s[i].name[0] != '\0' && s[i].name[0] != ' ' && - !strcmp(dev->name, s[i].name)) { - dev->irq = s[i].map.irq; - dev->base_addr = s[i].map.base_addr; - dev->mem_start = s[i].map.mem_start; - dev->mem_end = s[i].map.mem_end; - return 1; - } - } - return 0; -} -EXPORT_SYMBOL(netdev_boot_setup_check); - -/** - * netdev_boot_base - get address from boot time settings - * @prefix: prefix for network device - * @unit: id for network device - * - * Check boot time settings for the base address of device. - * The found settings are set for the device to be used - * later in the device probing. - * Returns 0 if no settings found. - */ -static unsigned long netdev_boot_base(const char *prefix, int unit) -{ - const struct netdev_boot_setup *s = dev_boot_setup; - char name[IFNAMSIZ]; - int i; - - sprintf(name, "%s%d", prefix, unit); - - /* - * If device already registered then return base of 1 - * to indicate not to probe for this interface - */ - if (__dev_get_by_name(&init_net, name)) - return 1; - - for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) - if (!strcmp(name, s[i].name)) - return s[i].map.base_addr; - return 0; -} - -/* - * Saves at boot time configured settings for any netdevice. - */ -static int __init netdev_boot_setup(char *str) -{ - int ints[5]; - struct ifmap map; - - str = get_options(str, ARRAY_SIZE(ints), ints); - if (!str || !*str) - return 0; - - /* Save settings */ - memset(&map, 0, sizeof(map)); - if (ints[0] > 0) - map.irq = ints[1]; - if (ints[0] > 1) - map.base_addr = ints[2]; - if (ints[0] > 2) - map.mem_start = ints[3]; - if (ints[0] > 3) - map.mem_end = ints[4]; - - /* Add new entry to the list */ - return netdev_boot_setup_add(str, &map); -} - -__setup("netdev=", netdev_boot_setup); - -static int __init ether_boot_setup(char *str) -{ - return netdev_boot_setup(str); -} -__setup("ether=", ether_boot_setup); - - -/* A unified ethernet device probe. This is the easiest way to have every - * ethernet adaptor have the name "eth[0123...]". - */ - -struct devprobe2 { - struct net_device *(*probe)(int unit); - int status; /* non-zero if autoprobe has failed */ -}; - -static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe) -{ - struct net_device *dev; - - for (; p->probe; p++) { - if (autoprobe && p->status) - continue; - dev = p->probe(unit); - if (!IS_ERR(dev)) - return 0; - if (autoprobe) - p->status = PTR_ERR(dev); - } - return -ENODEV; -} - -/* ISA probes that touch addresses < 0x400 (including those that also - * look for EISA/PCI cards in addition to ISA cards). - */ -static struct devprobe2 isa_probes[] __initdata = { -#if defined(CONFIG_NE2000) /* ISA (use ne2k-pci for PCI cards) */ - {ne_probe, 0}, -#endif -#ifdef CONFIG_CS89x0_ISA - {cs89x0_probe, 0}, -#endif - {NULL, 0}, -}; - -/* Unified ethernet device probe, segmented per architecture and - * per bus interface. This drives the legacy devices only for now. - */ - -static void __init ethif_probe2(int unit) -{ - unsigned long base_addr = netdev_boot_base("eth", unit); - - if (base_addr == 1) - return; - - probe_list2(unit, isa_probes, base_addr == 0); -} - -/* Statically configured drivers -- order matters here. */ -static int __init net_olddevs_init(void) -{ - int num; - - for (num = 0; num < 8; ++num) - ethif_probe2(num); - - return 0; -} - -device_initcall(net_olddevs_init); diff --git a/drivers/net/amt.c b/drivers/net/amt.c index f2f3139e38a5..724a8163a514 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -614,24 +614,24 @@ static void amt_send_discovery(struct amt_dev *amt) { struct amt_header_discovery *amtd; int hlen, tlen, offset; - struct socket *sock; struct udphdr *udph; struct sk_buff *skb; struct iphdr *iph; struct rtable *rt; struct flowi4 fl4; + struct sock *sk; u32 len; int err; rcu_read_lock(); - sock = rcu_dereference(amt->sock); - if (!sock) + sk = rcu_dereference(amt->sk); + if (!sk) goto out; if (!netif_running(amt->stream_dev) || !netif_running(amt->dev)) goto out; - rt = ip_route_output_ports(amt->net, &fl4, sock->sk, + rt = ip_route_output_ports(amt->net, &fl4, sk, amt->discovery_ip, amt->local_ip, amt->gw_port, amt->relay_port, IPPROTO_UDP, 0, @@ -690,7 +690,7 @@ static void amt_send_discovery(struct amt_dev *amt) skb->ip_summed = CHECKSUM_NONE; ip_select_ident(amt->net, skb, NULL); ip_send_check(iph); - err = ip_local_out(amt->net, sock->sk, skb); + err = ip_local_out(amt->net, sk, skb); if (unlikely(net_xmit_eval(err))) amt->dev->stats.tx_errors++; @@ -703,24 +703,24 @@ static void amt_send_request(struct amt_dev *amt, bool v6) { struct amt_header_request *amtrh; int hlen, tlen, offset; - struct socket *sock; struct udphdr *udph; struct sk_buff *skb; struct iphdr *iph; struct rtable *rt; struct flowi4 fl4; + struct sock *sk; u32 len; int err; rcu_read_lock(); - sock = rcu_dereference(amt->sock); - if (!sock) + sk = rcu_dereference(amt->sk); + if (!sk) goto out; if (!netif_running(amt->stream_dev) || !netif_running(amt->dev)) goto out; - rt = ip_route_output_ports(amt->net, &fl4, sock->sk, + rt = ip_route_output_ports(amt->net, &fl4, sk, amt->remote_ip, amt->local_ip, amt->gw_port, amt->relay_port, IPPROTO_UDP, 0, @@ -781,7 +781,7 @@ static void amt_send_request(struct amt_dev *amt, bool v6) skb->ip_summed = CHECKSUM_NONE; ip_select_ident(amt->net, skb, NULL); ip_send_check(iph); - err = ip_local_out(amt->net, sock->sk, skb); + err = ip_local_out(amt->net, sk, skb); if (unlikely(net_xmit_eval(err))) amt->dev->stats.tx_errors++; @@ -1000,14 +1000,14 @@ static bool amt_send_membership_update(struct amt_dev *amt, bool v6) { struct amt_header_membership_update *amtmu; - struct socket *sock; struct iphdr *iph; struct flowi4 fl4; struct rtable *rt; + struct sock *sk; int err; - sock = rcu_dereference_bh(amt->sock); - if (!sock) + sk = rcu_dereference_bh(amt->sk); + if (!sk) return true; err = skb_cow_head(skb, LL_RESERVED_SPACE(amt->dev) + sizeof(*amtmu) + @@ -1039,7 +1039,7 @@ static bool amt_send_membership_update(struct amt_dev *amt, skb_set_inner_protocol(skb, htons(ETH_P_IP)); else skb_set_inner_protocol(skb, htons(ETH_P_IPV6)); - udp_tunnel_xmit_skb(rt, sock->sk, skb, + udp_tunnel_xmit_skb(rt, sk, skb, fl4.saddr, fl4.daddr, AMT_TOS, @@ -1060,14 +1060,14 @@ static void amt_send_multicast_data(struct amt_dev *amt, bool v6) { struct amt_header_mcast_data *amtmd; - struct socket *sock; struct sk_buff *skb; struct iphdr *iph; struct flowi4 fl4; struct rtable *rt; + struct sock *sk; - sock = rcu_dereference_bh(amt->sock); - if (!sock) + sk = rcu_dereference_bh(amt->sk); + if (!sk) return; skb = skb_copy_expand(oskb, sizeof(*amtmd) + sizeof(*iph) + @@ -1097,7 +1097,7 @@ static void amt_send_multicast_data(struct amt_dev *amt, skb_set_inner_protocol(skb, htons(ETH_P_IP)); else skb_set_inner_protocol(skb, htons(ETH_P_IPV6)); - udp_tunnel_xmit_skb(rt, sock->sk, skb, + udp_tunnel_xmit_skb(rt, sk, skb, fl4.saddr, fl4.daddr, AMT_TOS, @@ -1116,13 +1116,13 @@ static bool amt_send_membership_query(struct amt_dev *amt, bool v6) { struct amt_header_membership_query *amtmq; - struct socket *sock; struct rtable *rt; struct flowi4 fl4; + struct sock *sk; int err; - sock = rcu_dereference_bh(amt->sock); - if (!sock) + sk = rcu_dereference_bh(amt->sk); + if (!sk) return true; err = skb_cow_head(skb, LL_RESERVED_SPACE(amt->dev) + sizeof(*amtmq) + @@ -1156,7 +1156,7 @@ static bool amt_send_membership_query(struct amt_dev *amt, skb_set_inner_protocol(skb, htons(ETH_P_IP)); else skb_set_inner_protocol(skb, htons(ETH_P_IPV6)); - udp_tunnel_xmit_skb(rt, sock->sk, skb, + udp_tunnel_xmit_skb(rt, sk, skb, fl4.saddr, fl4.daddr, AMT_TOS, @@ -2554,24 +2554,24 @@ static void amt_send_advertisement(struct amt_dev *amt, __be32 nonce, { struct amt_header_advertisement *amta; int hlen, tlen, offset; - struct socket *sock; struct udphdr *udph; struct sk_buff *skb; struct iphdr *iph; struct rtable *rt; struct flowi4 fl4; + struct sock *sk; u32 len; int err; rcu_read_lock(); - sock = rcu_dereference(amt->sock); - if (!sock) + sk = rcu_dereference(amt->sk); + if (!sk) goto out; if (!netif_running(amt->stream_dev) || !netif_running(amt->dev)) goto out; - rt = ip_route_output_ports(amt->net, &fl4, sock->sk, + rt = ip_route_output_ports(amt->net, &fl4, sk, daddr, amt->local_ip, dport, amt->relay_port, IPPROTO_UDP, 0, @@ -2631,7 +2631,7 @@ static void amt_send_advertisement(struct amt_dev *amt, __be32 nonce, skb->ip_summed = CHECKSUM_NONE; ip_select_ident(amt->net, skb, NULL); ip_send_check(iph); - err = ip_local_out(amt->net, sock->sk, skb); + err = ip_local_out(amt->net, sk, skb); if (unlikely(net_xmit_eval(err))) amt->dev->stats.tx_errors++; @@ -2944,7 +2944,7 @@ static int amt_err_lookup(struct sock *sk, struct sk_buff *skb) return 0; } -static struct socket *amt_create_sock(struct net *net, __be16 port) +static struct sock *amt_create_sock(struct net *net, __be16 port) { struct udp_port_cfg udp_conf; struct socket *sock; @@ -2960,17 +2960,17 @@ static struct socket *amt_create_sock(struct net *net, __be16 port) if (err < 0) return ERR_PTR(err); - return sock; + return sock->sk; } static int amt_socket_create(struct amt_dev *amt) { struct udp_tunnel_sock_cfg tunnel_cfg; - struct socket *sock; + struct sock *sk; - sock = amt_create_sock(amt->net, amt->relay_port); - if (IS_ERR(sock)) - return PTR_ERR(sock); + sk = amt_create_sock(amt->net, amt->relay_port); + if (IS_ERR(sk)) + return PTR_ERR(sk); /* Mark socket as an encapsulation socket */ memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); @@ -2979,9 +2979,9 @@ static int amt_socket_create(struct amt_dev *amt) tunnel_cfg.encap_rcv = amt_rcv; tunnel_cfg.encap_err_lookup = amt_err_lookup; tunnel_cfg.encap_destroy = NULL; - setup_udp_tunnel_sock(amt->net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(amt->net, sk, &tunnel_cfg); - rcu_assign_pointer(amt->sock, sock); + rcu_assign_pointer(amt->sk, sk); return 0; } @@ -3019,8 +3019,8 @@ static int amt_dev_stop(struct net_device *dev) { struct amt_dev *amt = netdev_priv(dev); struct amt_tunnel_list *tunnel, *tmp; - struct socket *sock; struct sk_buff *skb; + struct sock *sk; int i; cancel_delayed_work_sync(&amt->req_wq); @@ -3028,11 +3028,11 @@ static int amt_dev_stop(struct net_device *dev) cancel_delayed_work_sync(&amt->secret_wq); /* shutdown */ - sock = rtnl_dereference(amt->sock); - RCU_INIT_POINTER(amt->sock, NULL); + sk = rtnl_dereference(amt->sk); + RCU_INIT_POINTER(amt->sk, NULL); synchronize_net(); - if (sock) - udp_tunnel_sock_release(sock); + if (sk) + udp_tunnel_sock_release(sk); cancel_work_sync(&amt->event_wq); for (i = 0; i < AMT_MAX_EVENTS; i++) { diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig index d1d07a1d4fbc..4611a37168c4 100644 --- a/drivers/net/arcnet/Kconfig +++ b/drivers/net/arcnet/Kconfig @@ -4,7 +4,7 @@ # menuconfig ARCNET - depends on NETDEVICES && (ISA || PCI || PCMCIA) && HAS_IOPORT + depends on NETDEVICES && PCI && HAS_IOPORT tristate "ARCnet support" help If you have a network card of this type, say Y and check out the @@ -12,9 +12,7 @@ menuconfig ARCNET . You need both this driver, and the driver for the particular ARCnet - chipset of your card. If you don't know, then it's probably a - COM90xx type card, so say Y (or M) to "ARCnet COM90xx chipset - support" below. + chipset of your card. To compile this driver as a module, choose M here. The module will be called arcnet. @@ -70,38 +68,6 @@ config ARCNET_CAP Cap only listens to protocol 1-8. -config ARCNET_COM90xx - tristate "ARCnet COM90xx (normal) chipset driver" - help - This is the chipset driver for the standard COM90xx cards. If you - have always used the old ARCnet driver without knowing what type of - card you had, this is probably the one for you. - - To compile this driver as a module, choose M here. The module will - be called com90xx. - -config ARCNET_COM90xxIO - tristate "ARCnet COM90xx (IO mapped) chipset driver" - help - This is the chipset driver for the COM90xx cards, using them in - IO-mapped mode instead of memory-mapped mode. This is slower than - the normal driver. Only use it if your card doesn't support shared - memory. - - To compile this driver as a module, choose M here. The module will - be called com90io. - -config ARCNET_RIM_I - tristate "ARCnet COM90xx (RIM I) chipset driver" - help - This is yet another chipset driver for the COM90xx cards, but this - time only using memory-mapped mode, and no IO ports at all. This - driver is completely untested, so if you have one of these cards, - please mail , especially if it works! - - To compile this driver as a module, choose M here. The module will - be called arc-rimi. - config ARCNET_COM20020 tristate "ARCnet COM20020 chipset driver" depends on LEDS_CLASS @@ -113,22 +79,8 @@ config ARCNET_COM20020 To compile this driver as a module, choose M here. The module will be called com20020. -config ARCNET_COM20020_ISA - tristate "Support for COM20020 on ISA" - depends on ARCNET_COM20020 && ISA - config ARCNET_COM20020_PCI tristate "Support for COM20020 on PCI" depends on ARCNET_COM20020 && PCI -config ARCNET_COM20020_CS - tristate "COM20020 ARCnet PCMCIA support" - depends on ARCNET_COM20020 && PCMCIA - help - Say Y here if you intend to attach this type of ARCnet PCMCIA card - to your computer. - - To compile this driver as a module, choose M here: the module will be - called com20020_cs. If unsure, say N. - endif # ARCNET diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile index 53525e8ea130..e5df03a08b0e 100644 --- a/drivers/net/arcnet/Makefile +++ b/drivers/net/arcnet/Makefile @@ -7,10 +7,5 @@ obj-$(CONFIG_ARCNET_1201) += rfc1201.o obj-$(CONFIG_ARCNET_1051) += rfc1051.o obj-$(CONFIG_ARCNET_RAW) += arc-rawmode.o obj-$(CONFIG_ARCNET_CAP) += capmode.o -obj-$(CONFIG_ARCNET_COM90xx) += com90xx.o -obj-$(CONFIG_ARCNET_COM90xxIO) += com90io.o -obj-$(CONFIG_ARCNET_RIM_I) += arc-rimi.o obj-$(CONFIG_ARCNET_COM20020) += com20020.o -obj-$(CONFIG_ARCNET_COM20020_ISA) += com20020-isa.o obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o -obj-$(CONFIG_ARCNET_COM20020_CS) += com20020_cs.o diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c deleted file mode 100644 index 53d10a04d1bd..000000000000 --- a/drivers/net/arcnet/arc-rimi.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards - * - * Written 1994-1999 by Avery Pennarun. - * Written 1999-2000 by Martin Mares . - * Derived from skeleton.c by Donald Becker. - * - * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) - * for sponsoring the further development of this driver. - * - * ********************** - * - * The original copyright of skeleton.c was as follows: - * - * skeleton.c Written 1993 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may only be used - * and distributed according to the terms of the GNU General Public License as - * modified by SRC, incorporated herein by reference. - * - * ********************** - * - * For more details, see drivers/net/arcnet.c - * - * ********************** - */ - -#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arcdevice.h" -#include "com9026.h" - -/* Internal function declarations */ - -static int arcrimi_probe(struct net_device *dev); -static int arcrimi_found(struct net_device *dev); -static void arcrimi_command(struct net_device *dev, int command); -static int arcrimi_status(struct net_device *dev); -static void arcrimi_setmask(struct net_device *dev, int mask); -static int arcrimi_reset(struct net_device *dev, int really_reset); -static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count); -static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count); - -/* Handy defines for ARCnet specific stuff */ - -/* Amount of I/O memory used by the card */ -#define BUFFER_SIZE (512) -#define MIRROR_SIZE (BUFFER_SIZE * 4) - -/* We cannot probe for a RIM I card; one reason is I don't know how to reset - * them. In fact, we can't even get their node ID automatically. So, we - * need to be passed a specific shmem address, IRQ, and node ID. - */ -static int __init arcrimi_probe(struct net_device *dev) -{ - if (BUGLVL(D_NORMAL)) { - pr_info("%s\n", "RIM I (entirely mem-mapped) support"); - pr_info("E-mail me if you actually test the RIM I driver, please!\n"); - pr_info("Given: node %02Xh, shmem %lXh, irq %d\n", - dev->dev_addr[0], dev->mem_start, dev->irq); - } - - if (dev->mem_start <= 0 || dev->irq <= 0) { - if (BUGLVL(D_NORMAL)) - pr_err("No autoprobe for RIM I; you must specify the shmem and irq!\n"); - return -ENODEV; - } - if (dev->dev_addr[0] == 0) { - if (BUGLVL(D_NORMAL)) - pr_err("You need to specify your card's station ID!\n"); - return -ENODEV; - } - /* Grab the memory region at mem_start for MIRROR_SIZE bytes. - * Later in arcrimi_found() the real size will be determined - * and this reserve will be released and the correct size - * will be taken. - */ - if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) { - if (BUGLVL(D_NORMAL)) - pr_notice("Card memory already allocated\n"); - return -ENODEV; - } - return arcrimi_found(dev); -} - -static int check_mirror(unsigned long addr, size_t size) -{ - void __iomem *p; - int res = -1; - - if (!request_mem_region(addr, size, "arcnet (90xx)")) - return -1; - - p = ioremap(addr, size); - if (p) { - if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue) - res = 1; - else - res = 0; - iounmap(p); - } - - release_mem_region(addr, size); - return res; -} - -/* Set up the struct net_device associated with this card. - * Called after probing succeeds. - */ -static int __init arcrimi_found(struct net_device *dev) -{ - struct arcnet_local *lp; - unsigned long first_mirror, last_mirror, shmem; - void __iomem *p; - int mirror_size; - int err; - - p = ioremap(dev->mem_start, MIRROR_SIZE); - if (!p) { - release_mem_region(dev->mem_start, MIRROR_SIZE); - arc_printk(D_NORMAL, dev, "Can't ioremap\n"); - return -ENODEV; - } - - /* reserve the irq */ - if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) { - iounmap(p); - release_mem_region(dev->mem_start, MIRROR_SIZE); - arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq); - return -ENODEV; - } - - shmem = dev->mem_start; - arcnet_writeb(TESTvalue, p, COM9026_REG_W_INTMASK); - arcnet_writeb(TESTvalue, p, COM9026_REG_W_COMMAND); - /* actually the station/node ID */ - - /* find the real shared memory start/end points, including mirrors */ - - /* guess the actual size of one "memory mirror" - the number of - * bytes between copies of the shared memory. On most cards, it's - * 2k (or there are no mirrors at all) but on some, it's 4k. - */ - mirror_size = MIRROR_SIZE; - if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue && - check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && - check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) - mirror_size = 2 * MIRROR_SIZE; - - first_mirror = shmem - mirror_size; - while (check_mirror(first_mirror, mirror_size) == 1) - first_mirror -= mirror_size; - first_mirror += mirror_size; - - last_mirror = shmem + mirror_size; - while (check_mirror(last_mirror, mirror_size) == 1) - last_mirror += mirror_size; - last_mirror -= mirror_size; - - dev->mem_start = first_mirror; - dev->mem_end = last_mirror + MIRROR_SIZE - 1; - - /* initialize the rest of the device structure. */ - - lp = netdev_priv(dev); - lp->card_name = "RIM I"; - lp->hw.command = arcrimi_command; - lp->hw.status = arcrimi_status; - lp->hw.intmask = arcrimi_setmask; - lp->hw.reset = arcrimi_reset; - lp->hw.owner = THIS_MODULE; - lp->hw.copy_to_card = arcrimi_copy_to_card; - lp->hw.copy_from_card = arcrimi_copy_from_card; - - /* re-reserve the memory region - arcrimi_probe() alloced this reqion - * but didn't know the real size. Free that region and then re-get - * with the correct size. There is a VERY slim chance this could - * fail. - */ - iounmap(p); - release_mem_region(shmem, MIRROR_SIZE); - if (!request_mem_region(dev->mem_start, - dev->mem_end - dev->mem_start + 1, - "arcnet (90xx)")) { - arc_printk(D_NORMAL, dev, "Card memory already allocated\n"); - goto err_free_irq; - } - - lp->mem_start = ioremap(dev->mem_start, - dev->mem_end - dev->mem_start + 1); - if (!lp->mem_start) { - arc_printk(D_NORMAL, dev, "Can't remap device memory!\n"); - goto err_release_mem; - } - - /* get and check the station ID from offset 1 in shmem */ - arcnet_set_addr(dev, arcnet_readb(lp->mem_start, - COM9026_REG_R_STATION)); - - arc_printk(D_NORMAL, dev, "ARCnet RIM I: station %02Xh found at IRQ %d, ShMem %lXh (%ld*%d bytes)\n", - dev->dev_addr[0], - dev->irq, dev->mem_start, - (dev->mem_end - dev->mem_start + 1) / mirror_size, - mirror_size); - - err = register_netdev(dev); - if (err) - goto err_unmap; - - return 0; - -err_unmap: - iounmap(lp->mem_start); -err_release_mem: - release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1); -err_free_irq: - free_irq(dev->irq, dev); - return -EIO; -} - -/* Do a hardware reset on the card, and set up necessary registers. - * - * This should be called as little as possible, because it disrupts the - * token on the network (causes a RECON) and requires a significant delay. - * - * However, it does make sure the card is in a defined state. - */ -static int arcrimi_reset(struct net_device *dev, int really_reset) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *ioaddr = lp->mem_start + 0x800; - - arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", - dev->name, arcnet_readb(ioaddr, COM9026_REG_R_STATUS)); - - if (really_reset) { - arcnet_writeb(TESTvalue, ioaddr, -0x800); /* fake reset */ - return 0; - } - /* clear flags & end reset */ - arcnet_writeb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); - arcnet_writeb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); - - /* enable extended (512-byte) packets */ - arcnet_writeb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); - - /* done! return success. */ - return 0; -} - -static void arcrimi_setmask(struct net_device *dev, int mask) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *ioaddr = lp->mem_start + 0x800; - - arcnet_writeb(mask, ioaddr, COM9026_REG_W_INTMASK); -} - -static int arcrimi_status(struct net_device *dev) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *ioaddr = lp->mem_start + 0x800; - - return arcnet_readb(ioaddr, COM9026_REG_R_STATUS); -} - -static void arcrimi_command(struct net_device *dev, int cmd) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *ioaddr = lp->mem_start + 0x800; - - arcnet_writeb(cmd, ioaddr, COM9026_REG_W_COMMAND); -} - -static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; - - TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count)); -} - -static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; - - TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); -} - -static int node; -static int io; /* use the insmod io= irq= node= options */ -static int irq; -static char device[9]; /* use eg. device=arc1 to change name */ - -module_param(node, int, 0); -module_param(io, int, 0); -module_param(irq, int, 0); -module_param_string(device, device, sizeof(device), 0); -MODULE_DESCRIPTION("ARCnet COM90xx RIM I chipset driver"); -MODULE_LICENSE("GPL"); - -static struct net_device *my_dev; - -static int __init arc_rimi_init(void) -{ - struct net_device *dev; - - dev = alloc_arcdev(device); - if (!dev) - return -ENOMEM; - - if (node && node != 0xff) - arcnet_set_addr(dev, node); - - dev->mem_start = io; - dev->irq = irq; - if (dev->irq == 2) - dev->irq = 9; - - if (arcrimi_probe(dev)) { - free_arcdev(dev); - return -EIO; - } - - my_dev = dev; - return 0; -} - -static void __exit arc_rimi_exit(void) -{ - struct net_device *dev = my_dev; - struct arcnet_local *lp = netdev_priv(dev); - - unregister_netdev(dev); - iounmap(lp->mem_start); - release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1); - free_irq(dev->irq, dev); - free_arcdev(dev); -} - -#ifndef MODULE -static int __init arcrimi_setup(char *s) -{ - int ints[8]; - - s = get_options(s, 8, ints); - if (!ints[0]) - return 1; - switch (ints[0]) { - default: /* ERROR */ - pr_err("Too many arguments\n"); - fallthrough; - case 3: /* Node ID */ - node = ints[3]; - fallthrough; - case 2: /* IRQ */ - irq = ints[2]; - fallthrough; - case 1: /* IO address */ - io = ints[1]; - } - if (*s) - snprintf(device, sizeof(device), "%s", s); - return 1; -} -__setup("arcrimi=", arcrimi_setup); -#endif /* MODULE */ - -module_init(arc_rimi_init) -module_exit(arc_rimi_exit) diff --git a/drivers/net/arcnet/arcdevice.h b/drivers/net/arcnet/arcdevice.h index bee60b377d7c..a7c217359679 100644 --- a/drivers/net/arcnet/arcdevice.h +++ b/drivers/net/arcnet/arcdevice.h @@ -137,7 +137,7 @@ do { \ #define TXACKflag 0x02 /* transmitted msg. ackd */ #define RECONflag 0x04 /* network reconfigured */ #define TESTflag 0x08 /* test flag */ -#define EXCNAKflag 0x08 /* excesive nak flag */ +#define EXCNAKflag 0x08 /* excessive nak flag */ #define RESETflag 0x10 /* power-on-reset */ #define RES1flag 0x20 /* reserved - usually set by jumper */ #define RES2flag 0x40 /* reserved - usually set by jumper */ @@ -166,7 +166,7 @@ do { \ #define RESETclear 0x08 /* power-on-reset */ #define CONFIGclear 0x10 /* system reconfigured */ -#define EXCNAKclear 0x0E /* Clear and acknowledge the excive nak bit */ +#define EXCNAKclear 0x0E /* Clear and acknowledge the excessive nak bit */ /* flags for "load test flags" command */ #define TESTload 0x08 /* test flag (diagnostic) */ @@ -194,7 +194,7 @@ do { \ struct ArcProto { char suffix; /* a for RFC1201, e for ether-encap, etc. */ int mtu; /* largest possible packet */ - int is_ip; /* This is a ip plugin - not a raw thing */ + int is_ip; /* This is an ip plugin - not a raw thing */ void (*rx)(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length); @@ -257,7 +257,7 @@ struct arcnet_local { char *card_name; /* card ident string */ int card_flags; /* special card features */ - /* On preemtive and SMB a lock is needed */ + /* On preemptive and SMP a lock is needed */ spinlock_t lock; struct led_trigger *tx_led_trig; @@ -299,7 +299,7 @@ struct arcnet_local { int num_recons; /* number of RECONs between first and last. */ int network_down; /* do we think the network is down? */ - int excnak_pending; /* We just got an excesive nak interrupt */ + int excnak_pending; /* We just got an excessive nak interrupt */ /* RESET flag handling */ int reset_in_progress; @@ -372,31 +372,5 @@ static inline void arcnet_set_addr(struct net_device *dev, u8 addr) dev_addr_set(dev, &addr); } -/* I/O equivalents */ - -#ifdef CONFIG_SA1100_CT6001 -#define BUS_ALIGN 2 /* 8 bit device on a 16 bit bus - needs padding */ -#else -#define BUS_ALIGN 1 -#endif - -/* addr and offset allow register like names to define the actual IO address. - * A configuration option multiplies the offset for alignment. - */ -#define arcnet_inb(addr, offset) \ - inb((addr) + BUS_ALIGN * (offset)) -#define arcnet_outb(value, addr, offset) \ - outb(value, (addr) + BUS_ALIGN * (offset)) - -#define arcnet_insb(addr, offset, buffer, count) \ - insb((addr) + BUS_ALIGN * (offset), buffer, count) -#define arcnet_outsb(addr, offset, buffer, count) \ - outsb((addr) + BUS_ALIGN * (offset), buffer, count) - -#define arcnet_readb(addr, offset) \ - readb((addr) + (offset)) -#define arcnet_writeb(value, addr, offset) \ - writeb(value, (addr) + (offset)) - #endif /* __KERNEL__ */ #endif /* _LINUX_ARCDEVICE_H */ diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c deleted file mode 100644 index fef2ac2852a8..000000000000 --- a/drivers/net/arcnet/com20020-isa.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Linux ARCnet driver - COM20020 chipset support - * - * Written 1997 by David Woodhouse. - * Written 1994-1999 by Avery Pennarun. - * Written 1999-2000 by Martin Mares . - * Derived from skeleton.c by Donald Becker. - * - * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) - * for sponsoring the further development of this driver. - * - * ********************** - * - * The original copyright of skeleton.c was as follows: - * - * skeleton.c Written 1993 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may only be used - * and distributed according to the terms of the GNU General Public License as - * modified by SRC, incorporated herein by reference. - * - * ********************** - * - * For more details, see drivers/net/arcnet.c - * - * ********************** - */ - -#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arcdevice.h" -#include "com20020.h" - -/* We cannot (yet) probe for an IO mapped card, although we can check that - * it's where we were told it was, and even do autoirq. - */ -static int __init com20020isa_probe(struct net_device *dev) -{ - int ioaddr; - unsigned long airqmask; - struct arcnet_local *lp = netdev_priv(dev); - int err; - - if (BUGLVL(D_NORMAL)) - pr_info("%s\n", "COM20020 ISA support (by David Woodhouse et al.)"); - - ioaddr = dev->base_addr; - if (!ioaddr) { - arc_printk(D_NORMAL, dev, "No autoprobe (yet) for IO mapped cards; you must specify the base address!\n"); - return -ENODEV; - } - if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) { - arc_printk(D_NORMAL, dev, "IO region %xh-%xh already allocated.\n", - ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); - return -ENXIO; - } - if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) { - arc_printk(D_NORMAL, dev, "IO address %x empty\n", ioaddr); - err = -ENODEV; - goto out; - } - if (com20020_check(dev)) { - err = -ENODEV; - goto out; - } - - if (!dev->irq) { - /* if we do this, we're sure to get an IRQ since the - * card has just reset and the NORXflag is on until - * we tell it to start receiving. - */ - arc_printk(D_INIT_REASONS, dev, "intmask was %02Xh\n", - arcnet_inb(ioaddr, COM20020_REG_R_STATUS)); - arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK); - airqmask = probe_irq_on(); - arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK); - udelay(1); - arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK); - dev->irq = probe_irq_off(airqmask); - - if ((int)dev->irq <= 0) { - arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed first time\n"); - airqmask = probe_irq_on(); - arcnet_outb(NORXflag, ioaddr, COM20020_REG_W_INTMASK); - udelay(5); - arcnet_outb(0, ioaddr, COM20020_REG_W_INTMASK); - dev->irq = probe_irq_off(airqmask); - if ((int)dev->irq <= 0) { - arc_printk(D_NORMAL, dev, "Autoprobe IRQ failed.\n"); - err = -ENODEV; - goto out; - } - } - } - - lp->card_name = "ISA COM20020"; - - err = com20020_found(dev, 0); - if (err != 0) - goto out; - - return 0; - -out: - release_region(ioaddr, ARCNET_TOTAL_SIZE); - return err; -} - -static int node = 0; -static int io = 0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ -static int irq = 0; /* or use the insmod io= irq= shmem= options */ -static char device[9]; /* use eg. device="arc1" to change name */ -static int timeout = 3; -static int backplane = 0; -static int clockp = 0; -static int clockm = 0; - -module_param(node, int, 0); -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param_string(device, device, sizeof(device), 0); -module_param(timeout, int, 0); -module_param(backplane, int, 0); -module_param(clockp, int, 0); -module_param(clockm, int, 0); - -MODULE_DESCRIPTION("ARCnet COM20020 chipset ISA driver"); -MODULE_LICENSE("GPL"); - -static struct net_device *my_dev; - -static int __init com20020_init(void) -{ - struct net_device *dev; - struct arcnet_local *lp; - - dev = alloc_arcdev(device); - if (!dev) - return -ENOMEM; - - if (node && node != 0xff) - arcnet_set_addr(dev, node); - - dev->netdev_ops = &com20020_netdev_ops; - - lp = netdev_priv(dev); - lp->backplane = backplane; - lp->clockp = clockp & 7; - lp->clockm = clockm & 3; - lp->timeout = timeout & 3; - lp->hw.owner = THIS_MODULE; - - dev->base_addr = io; - dev->irq = irq; - - if (dev->irq == 2) - dev->irq = 9; - - if (com20020isa_probe(dev)) { - free_arcdev(dev); - return -EIO; - } - - my_dev = dev; - return 0; -} - -static void __exit com20020_exit(void) -{ - unregister_netdev(my_dev); - free_irq(my_dev->irq, my_dev); - release_region(my_dev->base_addr, ARCNET_TOTAL_SIZE); - free_arcdev(my_dev); -} - -#ifndef MODULE -static int __init com20020isa_setup(char *s) -{ - int ints[8]; - - s = get_options(s, 8, ints); - if (!ints[0]) - return 1; - - switch (ints[0]) { - default: /* ERROR */ - pr_info("Too many arguments\n"); - fallthrough; - case 6: /* Timeout */ - timeout = ints[6]; - fallthrough; - case 5: /* CKP value */ - clockp = ints[5]; - fallthrough; - case 4: /* Backplane flag */ - backplane = ints[4]; - fallthrough; - case 3: /* Node ID */ - node = ints[3]; - fallthrough; - case 2: /* IRQ */ - irq = ints[2]; - fallthrough; - case 1: /* IO address */ - io = ints[1]; - } - if (*s) - snprintf(device, sizeof(device), "%s", s); - return 1; -} - -__setup("com20020=", com20020isa_setup); - -#endif /* MODULE */ - -module_init(com20020_init) -module_exit(com20020_exit) diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index dbadda08dce2..19cd36cba64d 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -127,7 +127,8 @@ static int com20020pci_probe(struct pci_dev *pdev, struct net_device *dev; struct arcnet_local *lp; struct com20020_priv *priv; - int i, ioaddr, ret; + int ioaddr, ret; + u8 i; struct resource *r; ret = 0; @@ -195,8 +196,8 @@ static int com20020pci_probe(struct pci_dev *pdev, * ARCNET controller needs * this access to detect bustype */ - arcnet_outb(0x00, ioaddr, COM20020_REG_W_COMMAND); - arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT); + outb(0x00, ioaddr + COM20020_REG_W_COMMAND); + inb(ioaddr + COM20020_REG_R_DIAGSTAT); SET_NETDEV_DEV(dev, &pdev->dev); dev->base_addr = ioaddr; @@ -224,7 +225,7 @@ static int com20020pci_probe(struct pci_dev *pdev, snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i); } - if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) { + if (inb(ioaddr + COM20020_REG_R_STATUS) == 0xFF) { pr_err("IO address %Xh is empty!\n", ioaddr); ret = -EIO; goto err_free_arcdev; @@ -459,168 +460,88 @@ static struct com20020_pci_card_info card_info_eae_fb2 = { static const struct pci_device_id com20020pci_id_table[] = { { - 0x1571, 0xa001, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0, + PCI_DEVICE(0x1571, 0xa001), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa002), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa003), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa004), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa005), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa006), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa007), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa008), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa009), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00a), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00b), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00c), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00d), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00e), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa201), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa202), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa203), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa204), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa205), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa206), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9030, 0x10B5, 0x2978), + .driver_data = (kernel_ulong_t)&card_info_sohard, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x2273), + .driver_data = (kernel_ulong_t)&card_info_sohard, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x3263), + .driver_data = (kernel_ulong_t)&card_info_eae_arc1, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x3292), + .driver_data = (kernel_ulong_t)&card_info_eae_ma1, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x3294), + .driver_data = (kernel_ulong_t)&card_info_eae_fb2, + }, { + PCI_DEVICE(0x14BA, 0x6000), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x10B5, 0x2200), + .driver_data = (kernel_ulong_t)&card_info_10mbit, }, - { - 0x1571, 0xa002, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0, - }, - { - 0x1571, 0xa003, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0 - }, - { - 0x1571, 0xa004, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0, - }, - { - 0x1571, 0xa005, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0 - }, - { - 0x1571, 0xa006, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0 - }, - { - 0x1571, 0xa007, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0 - }, - { - 0x1571, 0xa008, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - 0 - }, - { - 0x1571, 0xa009, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_5mbit - }, - { - 0x1571, 0xa00a, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_5mbit - }, - { - 0x1571, 0xa00b, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_5mbit - }, - { - 0x1571, 0xa00c, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_5mbit - }, - { - 0x1571, 0xa00d, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_5mbit - }, - { - 0x1571, 0xa00e, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_5mbit - }, - { - 0x1571, 0xa201, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x1571, 0xa202, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x1571, 0xa203, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x1571, 0xa204, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x1571, 0xa205, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x1571, 0xa206, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x10B5, 0x9030, - 0x10B5, 0x2978, - 0, 0, - (kernel_ulong_t)&card_info_sohard - }, - { - 0x10B5, 0x9050, - 0x10B5, 0x2273, - 0, 0, - (kernel_ulong_t)&card_info_sohard - }, - { - 0x10B5, 0x9050, - 0x10B5, 0x3263, - 0, 0, - (kernel_ulong_t)&card_info_eae_arc1 - }, - { - 0x10B5, 0x9050, - 0x10B5, 0x3292, - 0, 0, - (kernel_ulong_t)&card_info_eae_ma1 - }, - { - 0x10B5, 0x9050, - 0x10B5, 0x3294, - 0, 0, - (kernel_ulong_t)&card_info_eae_fb2 - }, - { - 0x14BA, 0x6000, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { - 0x10B5, 0x2200, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&card_info_10mbit - }, - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, com20020pci_id_table); diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index b8526805ffac..21f962357b0b 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -56,7 +56,7 @@ static void com20020_copy_to_card(struct net_device *dev, int bufnum, int offset, void *buf, int count); static void com20020_copy_from_card(struct net_device *dev, int bufnum, int offset, void *buf, int count); -static void com20020_set_mc_list(struct net_device *dev); +static void com20020_set_rx_mode(struct net_device *dev); static void com20020_close(struct net_device *); static void com20020_copy_from_card(struct net_device *dev, int bufnum, @@ -65,13 +65,13 @@ static void com20020_copy_from_card(struct net_device *dev, int bufnum, int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; /* set up the address register */ - arcnet_outb((ofs >> 8) | RDDATAflag | AUTOINCflag, - ioaddr, COM20020_REG_W_ADDR_HI); - arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); + outb((ofs >> 8) | RDDATAflag | AUTOINCflag, + ioaddr + COM20020_REG_W_ADDR_HI); + outb(ofs & 0xff, ioaddr + COM20020_REG_W_ADDR_LO); /* copy the data */ TIME(dev, "insb", count, - arcnet_insb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count)); + insb(ioaddr + COM20020_REG_RW_MEMDATA, buf, count)); } static void com20020_copy_to_card(struct net_device *dev, int bufnum, @@ -80,12 +80,12 @@ static void com20020_copy_to_card(struct net_device *dev, int bufnum, int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset; /* set up the address register */ - arcnet_outb((ofs >> 8) | AUTOINCflag, ioaddr, COM20020_REG_W_ADDR_HI); - arcnet_outb(ofs & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); + outb((ofs >> 8) | AUTOINCflag, ioaddr + COM20020_REG_W_ADDR_HI); + outb(ofs & 0xff, ioaddr + COM20020_REG_W_ADDR_LO); /* copy the data */ TIME(dev, "outsb", count, - arcnet_outsb(ioaddr, COM20020_REG_RW_MEMDATA, buf, count)); + outsb(ioaddr + COM20020_REG_RW_MEMDATA, buf, count)); } /* Reset the card and check some basic stuff during the detection stage. */ @@ -94,9 +94,9 @@ int com20020_check(struct net_device *dev) int ioaddr = dev->base_addr, status; struct arcnet_local *lp = netdev_priv(dev); - arcnet_outb(XTOcfg(3) | RESETcfg, ioaddr, COM20020_REG_W_CONFIG); + outb(XTOcfg(3) | RESETcfg, ioaddr + COM20020_REG_W_CONFIG); udelay(5); - arcnet_outb(XTOcfg(3), ioaddr, COM20020_REG_W_CONFIG); + outb(XTOcfg(3), ioaddr + COM20020_REG_W_CONFIG); mdelay(RESETtime); lp->setup = lp->clockm ? 0 : (lp->clockp << 1); @@ -107,23 +107,23 @@ int com20020_check(struct net_device *dev) lp->setup = lp->setup | P1MODE; com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); - arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); if (lp->clockm != 0) { com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); - arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG); + outb(lp->setup2, ioaddr + COM20020_REG_W_XREG); /* must now write the magic "restart operation" command */ mdelay(1); - arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); + outb(STARTIOcmd, ioaddr + COM20020_REG_W_COMMAND); } lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */ - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); - arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + outb(0x42, ioaddr + COM20020_REG_W_XREG); - status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); + status = inb(ioaddr + COM20020_REG_R_STATUS); if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) { arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n", status); @@ -131,18 +131,17 @@ int com20020_check(struct net_device *dev) } arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status); - arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, - ioaddr, COM20020_REG_W_COMMAND); - status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); + outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr + COM20020_REG_W_COMMAND); + status = inb(ioaddr + COM20020_REG_R_STATUS); arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n", status); /* Read first location of memory */ - arcnet_outb(0 | RDDATAflag | AUTOINCflag, - ioaddr, COM20020_REG_W_ADDR_HI); - arcnet_outb(0, ioaddr, COM20020_REG_W_ADDR_LO); + outb(0 | RDDATAflag | AUTOINCflag, ioaddr + COM20020_REG_W_ADDR_HI); + outb(0, ioaddr + COM20020_REG_W_ADDR_LO); - status = arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA); + status = inb(ioaddr + COM20020_REG_RW_MEMDATA); if (status != TESTvalue) { arc_printk(D_NORMAL, dev, "Signature byte not found (%02Xh != D1h).\n", status); @@ -159,7 +158,7 @@ static int com20020_set_hwaddr(struct net_device *dev, void *addr) dev_addr_set(dev, hwaddr->sa_data); com20020_set_subaddress(lp, ioaddr, SUB_NODE); - arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); + outb(dev->dev_addr[0], ioaddr + COM20020_REG_W_XREG); return 0; } @@ -170,7 +169,7 @@ static int com20020_netdev_open(struct net_device *dev) struct arcnet_local *lp = netdev_priv(dev); lp->config |= TXENcfg; - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); return arcnet_open(dev); } @@ -184,7 +183,7 @@ static int com20020_netdev_close(struct net_device *dev) /* disable transmitter */ lp->config &= ~TXENcfg; - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); return 0; } @@ -194,7 +193,7 @@ const struct net_device_ops com20020_netdev_ops = { .ndo_start_xmit = arcnet_send_packet, .ndo_tx_timeout = arcnet_timeout, .ndo_set_mac_address = com20020_set_hwaddr, - .ndo_set_rx_mode = com20020_set_mc_list, + .ndo_set_rx_mode = com20020_set_rx_mode, }; /* Set up the struct net_device associated with this card. Called after @@ -220,24 +219,24 @@ int com20020_found(struct net_device *dev, int shared) /* FIXME: do this some other way! */ if (!dev->dev_addr[0]) - arcnet_set_addr(dev, arcnet_inb(ioaddr, 8)); + arcnet_set_addr(dev, inb(ioaddr + 8)); com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); - arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); if (lp->card_flags & ARC_CAN_10MBIT) { com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); - arcnet_outb(lp->setup2, ioaddr, COM20020_REG_W_XREG); + outb(lp->setup2, ioaddr + COM20020_REG_W_XREG); /* must now write the magic "restart operation" command */ mdelay(1); - arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); + outb(STARTIOcmd, ioaddr + COM20020_REG_W_COMMAND); } lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; /* Default 0x38 + register: Node ID */ - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); - arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + outb(dev->dev_addr[0], ioaddr + COM20020_REG_W_XREG); /* reserve the irq */ if (request_irq(dev->irq, arcnet_interrupt, shared, @@ -288,26 +287,26 @@ static int com20020_reset(struct net_device *dev, int really_reset) arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n", __FILE__, __LINE__, __func__, dev, lp, dev->name); arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", - dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS)); + dev->name, inb(ioaddr + COM20020_REG_R_STATUS)); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); lp->config |= (lp->timeout << 3) | (lp->backplane << 2); /* power-up defaults */ - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); if (really_reset) { /* reset the card */ - arcnet_outb(lp->config | RESETcfg, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config | RESETcfg, ioaddr + COM20020_REG_W_CONFIG); udelay(5); - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); mdelay(RESETtime * 2); /* COM20020 seems to be slower sometimes */ } /* clear flags & end reset */ arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); - arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, - ioaddr, COM20020_REG_W_COMMAND); + outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr + COM20020_REG_W_COMMAND); /* verify that the ARCnet signature byte is present */ arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); @@ -321,7 +320,7 @@ static int com20020_reset(struct net_device *dev, int really_reset) return 1; } /* enable extended (512-byte) packets */ - arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM20020_REG_W_COMMAND); + outb(CONFIGcmd | EXTconf, ioaddr + COM20020_REG_W_COMMAND); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); @@ -334,22 +333,22 @@ static void com20020_setmask(struct net_device *dev, int mask) u_int ioaddr = dev->base_addr; arc_printk(D_DURING, dev, "Setting mask to %x at %x\n", mask, ioaddr); - arcnet_outb(mask, ioaddr, COM20020_REG_W_INTMASK); + outb(mask, ioaddr + COM20020_REG_W_INTMASK); } static void com20020_command(struct net_device *dev, int cmd) { u_int ioaddr = dev->base_addr; - arcnet_outb(cmd, ioaddr, COM20020_REG_W_COMMAND); + outb(cmd, ioaddr + COM20020_REG_W_COMMAND); } static int com20020_status(struct net_device *dev) { u_int ioaddr = dev->base_addr; - return arcnet_inb(ioaddr, COM20020_REG_R_STATUS) + - (arcnet_inb(ioaddr, COM20020_REG_R_DIAGSTAT) << 8); + return inb(ioaddr + COM20020_REG_R_STATUS) + + (inb(ioaddr + COM20020_REG_R_DIAGSTAT) << 8); } static void com20020_close(struct net_device *dev) @@ -359,17 +358,11 @@ static void com20020_close(struct net_device *dev) /* disable transmitter */ lp->config &= ~TXENcfg; - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); } -/* Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets - * num_addrs == 0 Normal mode, clear multicast list - * num_addrs > 0 Multicast mode, receive normal and MC packets, and do - * best-effort filtering. - * FIXME - do multicast stuff, not just promiscuous. - */ -static void com20020_set_mc_list(struct net_device *dev) +/* ARCnet does not support multicast, only unicast and broadcast */ +static void com20020_set_rx_mode(struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; @@ -380,20 +373,18 @@ static void com20020_set_mc_list(struct net_device *dev) arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n"); com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); lp->setup |= PROMISCset; - arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); } else { /* Disable promiscuous mode, use normal mode */ if ((lp->setup & PROMISCset)) arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n"); com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); lp->setup &= ~PROMISCset; - arcnet_outb(lp->setup, ioaddr, COM20020_REG_W_XREG); + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); } } -#if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \ - defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \ - defined(CONFIG_ARCNET_COM20020_CS_MODULE) +#ifdef CONFIG_ARCNET_COM20020_PCI_MODULE EXPORT_SYMBOL(com20020_check); EXPORT_SYMBOL(com20020_found); EXPORT_SYMBOL(com20020_netdev_ops); diff --git a/drivers/net/arcnet/com20020.h b/drivers/net/arcnet/com20020.h index 0bcc5d0a6903..68f49fdc2c28 100644 --- a/drivers/net/arcnet/com20020.h +++ b/drivers/net/arcnet/com20020.h @@ -50,7 +50,7 @@ struct com20020_pci_channel_map { struct com20020_pci_card_info { const char *name; - int devcount; + u8 devcount; struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS]; struct com20020_pci_channel_map misc_map; @@ -123,9 +123,9 @@ static inline void com20020_set_subaddress(struct arcnet_local *lp, { if (val < 4) { lp->config = (lp->config & ~0x03) | val; - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); } else { - arcnet_outb(val, ioaddr, COM20020_REG_W_SUBADR); + outb(val, ioaddr + COM20020_REG_W_SUBADR); } } diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c deleted file mode 100644 index 5c3c91677b62..000000000000 --- a/drivers/net/arcnet/com20020_cs.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Linux ARCnet driver - COM20020 PCMCIA support - * - * Written 1994-1999 by Avery Pennarun, - * based on an ISA version by David Woodhouse. - * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4) - * which was derived from pcnet_cs.c by David Hinds. - * Some additional portions derived from skeleton.c by Donald Becker. - * - * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) - * for sponsoring the further development of this driver. - * - * ********************** - * - * The original copyright of skeleton.c was as follows: - * - * skeleton.c Written 1993 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may only be used - * and distributed according to the terms of the GNU General Public License as - * modified by SRC, incorporated herein by reference. - * - * ********************** - * Changes: - * Arnaldo Carvalho de Melo - 08/08/2000 - * - reorganize kmallocs in com20020_attach, checking all for failure - * and releasing the previous allocations if one fails - * ********************** - * - * For more details, see drivers/net/arcnet.c - * - * ********************** - */ - -#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arcdevice.h" -#include "com20020.h" - -static void regdump(struct net_device *dev) -{ -#ifdef DEBUG - int ioaddr = dev->base_addr; - int count; - - netdev_dbg(dev, "register dump:\n"); - for (count = 0; count < 16; count++) { - if (!(count % 16)) - pr_cont("%04X:", ioaddr + count); - pr_cont(" %02X", arcnet_inb(ioaddr, count)); - } - pr_cont("\n"); - - netdev_dbg(dev, "buffer0 dump:\n"); - /* set up the address register */ - count = 0; - arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag, - ioaddr, COM20020_REG_W_ADDR_HI); - arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO); - - for (count = 0; count < 256 + 32; count++) { - if (!(count % 16)) - pr_cont("%04X:", count); - - /* copy the data */ - pr_cont(" %02X", arcnet_inb(ioaddr, COM20020_REG_RW_MEMDATA)); - } - pr_cont("\n"); -#endif -} - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -static int node; -static int timeout = 3; -static int backplane; -static int clockp; -static int clockm; - -module_param(node, int, 0); -module_param(timeout, int, 0); -module_param(backplane, int, 0); -module_param(clockp, int, 0); -module_param(clockm, int, 0); - -MODULE_DESCRIPTION("ARCnet COM20020 chipset PCMCIA driver"); -MODULE_LICENSE("GPL"); - -/*====================================================================*/ - -static int com20020_config(struct pcmcia_device *link); -static void com20020_release(struct pcmcia_device *link); - -static void com20020_detach(struct pcmcia_device *p_dev); - -/*====================================================================*/ - -static int com20020_probe(struct pcmcia_device *p_dev) -{ - struct com20020_dev *info; - struct net_device *dev; - struct arcnet_local *lp; - int ret = -ENOMEM; - - dev_dbg(&p_dev->dev, "com20020_attach()\n"); - - /* Create new network device */ - info = kzalloc_obj(*info); - if (!info) - goto fail_alloc_info; - - dev = alloc_arcdev(""); - if (!dev) - goto fail_alloc_dev; - - lp = netdev_priv(dev); - lp->timeout = timeout; - lp->backplane = backplane; - lp->clockp = clockp; - lp->clockm = clockm & 3; - lp->hw.owner = THIS_MODULE; - - /* fill in our module parameters as defaults */ - arcnet_set_addr(dev, node); - - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 16; - p_dev->config_flags |= CONF_ENABLE_IRQ; - - info->dev = dev; - p_dev->priv = info; - - ret = com20020_config(p_dev); - if (ret) - goto fail_config; - - return 0; - -fail_config: - free_arcdev(dev); -fail_alloc_dev: - kfree(info); -fail_alloc_info: - return ret; -} /* com20020_attach */ - -static void com20020_detach(struct pcmcia_device *link) -{ - struct com20020_dev *info = link->priv; - struct net_device *dev = info->dev; - - dev_dbg(&link->dev, "detach...\n"); - - dev_dbg(&link->dev, "com20020_detach\n"); - - dev_dbg(&link->dev, "unregister...\n"); - - unregister_netdev(dev); - - /* this is necessary because we register our IRQ separately - * from card services. - */ - if (dev->irq) - free_irq(dev->irq, dev); - - com20020_release(link); - - /* Unlink device structure, free bits */ - dev_dbg(&link->dev, "unlinking...\n"); - if (link->priv) { - dev = info->dev; - if (dev) { - dev_dbg(&link->dev, "kfree...\n"); - free_arcdev(dev); - } - dev_dbg(&link->dev, "kfree2...\n"); - kfree(info); - } - -} /* com20020_detach */ - -static int com20020_config(struct pcmcia_device *link) -{ - struct arcnet_local *lp; - struct com20020_dev *info; - struct net_device *dev; - int i, ret; - int ioaddr; - - info = link->priv; - dev = info->dev; - - dev_dbg(&link->dev, "config...\n"); - - dev_dbg(&link->dev, "com20020_config\n"); - - dev_dbg(&link->dev, "baseport1 is %Xh\n", - (unsigned int)link->resource[0]->start); - - i = -ENODEV; - link->io_lines = 16; - - if (!link->resource[0]->start) { - for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) { - link->resource[0]->start = ioaddr; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - } else { - i = pcmcia_request_io(link); - } - - if (i != 0) { - dev_dbg(&link->dev, "requestIO failed totally!\n"); - goto failed; - } - - ioaddr = dev->base_addr = link->resource[0]->start; - dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr); - - dev_dbg(&link->dev, "request IRQ %d\n", - link->irq); - if (!link->irq) { - dev_dbg(&link->dev, "requestIRQ failed totally!\n"); - goto failed; - } - - dev->irq = link->irq; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - if (com20020_check(dev)) { - regdump(dev); - goto failed; - } - - lp = netdev_priv(dev); - lp->card_name = "PCMCIA COM20020"; - lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ - - SET_NETDEV_DEV(dev, &link->dev); - - i = com20020_found(dev, 0); /* calls register_netdev */ - - if (i != 0) { - dev_notice(&link->dev, - "com20020_found() failed\n"); - goto failed; - } - - netdev_dbg(dev, "port %#3lx, irq %d\n", - dev->base_addr, dev->irq); - return 0; - -failed: - dev_dbg(&link->dev, "com20020_config failed...\n"); - com20020_release(link); - return -ENODEV; -} /* com20020_config */ - -static void com20020_release(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "com20020_release\n"); - pcmcia_disable_device(link); -} - -static int com20020_suspend(struct pcmcia_device *link) -{ - struct com20020_dev *info = link->priv; - struct net_device *dev = info->dev; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int com20020_resume(struct pcmcia_device *link) -{ - struct com20020_dev *info = link->priv; - struct net_device *dev = info->dev; - - if (link->open) { - int ioaddr = dev->base_addr; - struct arcnet_local *lp = netdev_priv(dev); - - arcnet_outb(lp->config | 0x80, ioaddr, COM20020_REG_W_CONFIG); - udelay(5); - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); - } - - return 0; -} - -static const struct pcmcia_device_id com20020_ids[] = { - PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", - "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf), - PCMCIA_DEVICE_PROD_ID12("SoHard AG", - "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, com20020_ids); - -static struct pcmcia_driver com20020_cs_driver = { - .owner = THIS_MODULE, - .name = "com20020_cs", - .probe = com20020_probe, - .remove = com20020_detach, - .id_table = com20020_ids, - .suspend = com20020_suspend, - .resume = com20020_resume, -}; -module_pcmcia_driver(com20020_cs_driver); diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c deleted file mode 100644 index 3b463fbc6402..000000000000 --- a/drivers/net/arcnet/com90io.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers) - * - * Written 1997 by David Woodhouse. - * Written 1994-1999 by Avery Pennarun. - * Written 1999-2000 by Martin Mares . - * Derived from skeleton.c by Donald Becker. - * - * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) - * for sponsoring the further development of this driver. - * - * ********************** - * - * The original copyright of skeleton.c was as follows: - * - * skeleton.c Written 1993 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may only be used - * and distributed according to the terms of the GNU General Public License as - * modified by SRC, incorporated herein by reference. - * - * ********************** - * - * For more details, see drivers/net/arcnet.c - * - * ********************** - */ - -#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arcdevice.h" -#include "com9026.h" - -/* Internal function declarations */ - -static int com90io_found(struct net_device *dev); -static void com90io_command(struct net_device *dev, int command); -static int com90io_status(struct net_device *dev); -static void com90io_setmask(struct net_device *dev, int mask); -static int com90io_reset(struct net_device *dev, int really_reset); -static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count); -static void com90io_copy_from_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count); - -/* Handy defines for ARCnet specific stuff */ - -/* The number of low I/O ports used by the card. */ -#define ARCNET_TOTAL_SIZE 16 - -/**************************************************************************** - * * - * IO-mapped operation routines * - * * - ****************************************************************************/ - -#undef ONE_AT_A_TIME_TX -#undef ONE_AT_A_TIME_RX - -static u_char get_buffer_byte(struct net_device *dev, unsigned offset) -{ - int ioaddr = dev->base_addr; - - arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI); - arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); - - return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); -} - -#ifdef ONE_AT_A_TIME_TX -static void put_buffer_byte(struct net_device *dev, unsigned offset, - u_char datum) -{ - int ioaddr = dev->base_addr; - - arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI); - arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); - - arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA); -} - -#endif - -static void get_whole_buffer(struct net_device *dev, unsigned offset, - unsigned length, char *dest) -{ - int ioaddr = dev->base_addr; - - arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); - arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO); - - while (length--) -#ifdef ONE_AT_A_TIME_RX - *(dest++) = get_buffer_byte(dev, offset++); -#else - *(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); -#endif -} - -static void put_whole_buffer(struct net_device *dev, unsigned offset, - unsigned length, char *dest) -{ - int ioaddr = dev->base_addr; - - arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); - arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO); - - while (length--) -#ifdef ONE_AT_A_TIME_TX - put_buffer_byte(dev, offset++, *(dest++)); -#else - arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA); -#endif -} - -/* We cannot probe for an IO mapped card either, although we can check that - * it's where we were told it was, and even autoirq - */ -static int __init com90io_probe(struct net_device *dev) -{ - int ioaddr = dev->base_addr, status; - unsigned long airqmask; - - if (BUGLVL(D_NORMAL)) { - pr_info("%s\n", "COM90xx IO-mapped mode support (by David Woodhouse et el.)"); - pr_info("E-mail me if you actually test this driver, please!\n"); - } - - if (!ioaddr) { - arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n"); - return -ENODEV; - } - if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) { - arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n", - ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1); - return -ENXIO; - } - if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) { - arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n", - ioaddr); - goto err_out; - } - arcnet_inb(ioaddr, COM9026_REG_R_RESET); - mdelay(RESETtime); - - status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); - - if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) { - arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n", - status); - goto err_out; - } - arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status); - - arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, - ioaddr, COM9026_REG_W_COMMAND); - - arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n", - status); - - status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); - - if (status & RESETflag) { - arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n", - status); - goto err_out; - } - arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag, - ioaddr, COM9026_REG_RW_CONFIG); - - /* Read first loc'n of memory */ - - arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI); - arcnet_outb(0, ioaddr, COM9026_REG_W_ADDR_LO); - - status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA); - if (status != 0xd1) { - arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n", - status); - goto err_out; - } - if (!dev->irq) { - /* if we do this, we're sure to get an IRQ since the - * card has just reset and the NORXflag is on until - * we tell it to start receiving. - */ - - airqmask = probe_irq_on(); - arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK); - udelay(1); - arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK); - dev->irq = probe_irq_off(airqmask); - - if ((int)dev->irq <= 0) { - arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n"); - goto err_out; - } - } - release_region(ioaddr, ARCNET_TOTAL_SIZE); /* end of probing */ - return com90io_found(dev); - -err_out: - release_region(ioaddr, ARCNET_TOTAL_SIZE); - return -ENODEV; -} - -/* Set up the struct net_device associated with this card. Called after - * probing succeeds. - */ -static int __init com90io_found(struct net_device *dev) -{ - struct arcnet_local *lp; - int ioaddr = dev->base_addr; - int err; - - /* Reserve the irq */ - if (request_irq(dev->irq, arcnet_interrupt, 0, - "arcnet (COM90xx-IO)", dev)) { - arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq); - return -ENODEV; - } - /* Reserve the I/O region */ - if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, - "arcnet (COM90xx-IO)")) { - free_irq(dev->irq, dev); - return -EBUSY; - } - - lp = netdev_priv(dev); - lp->card_name = "COM90xx I/O"; - lp->hw.command = com90io_command; - lp->hw.status = com90io_status; - lp->hw.intmask = com90io_setmask; - lp->hw.reset = com90io_reset; - lp->hw.owner = THIS_MODULE; - lp->hw.copy_to_card = com90io_copy_to_card; - lp->hw.copy_from_card = com90io_copy_from_card; - - lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag; - arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG); - - /* get and check the station ID from offset 1 in shmem */ - - arcnet_set_addr(dev, get_buffer_byte(dev, 1)); - - err = register_netdev(dev); - if (err) { - arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag, - ioaddr, COM9026_REG_RW_CONFIG); - free_irq(dev->irq, dev); - release_region(dev->base_addr, ARCNET_TOTAL_SIZE); - return err; - } - - arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n", - dev->dev_addr[0], dev->base_addr, dev->irq); - - return 0; -} - -/* Do a hardware reset on the card, and set up necessary registers. - * - * This should be called as little as possible, because it disrupts the - * token on the network (causes a RECON) and requires a significant delay. - * - * However, it does make sure the card is in a defined state. - */ -static int com90io_reset(struct net_device *dev, int really_reset) -{ - struct arcnet_local *lp = netdev_priv(dev); - short ioaddr = dev->base_addr; - - arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", - dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS)); - - if (really_reset) { - /* reset the card */ - arcnet_inb(ioaddr, COM9026_REG_R_RESET); - mdelay(RESETtime); - } - /* Set the thing to IO-mapped, 8-bit mode */ - lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag; - arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG); - - arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); - /* clear flags & end reset */ - arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); - - /* verify that the ARCnet signature byte is present */ - if (get_buffer_byte(dev, 0) != TESTvalue) { - arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n"); - return 1; - } - /* enable extended (512-byte) packets */ - arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); - /* done! return success. */ - return 0; -} - -static void com90io_command(struct net_device *dev, int cmd) -{ - short ioaddr = dev->base_addr; - - arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND); -} - -static int com90io_status(struct net_device *dev) -{ - short ioaddr = dev->base_addr; - - return arcnet_inb(ioaddr, COM9026_REG_R_STATUS); -} - -static void com90io_setmask(struct net_device *dev, int mask) -{ - short ioaddr = dev->base_addr; - - arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK); -} - -static void com90io_copy_to_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count) -{ - TIME(dev, "put_whole_buffer", count, - put_whole_buffer(dev, bufnum * 512 + offset, count, buf)); -} - -static void com90io_copy_from_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count) -{ - TIME(dev, "get_whole_buffer", count, - get_whole_buffer(dev, bufnum * 512 + offset, count, buf)); -} - -static int io; /* use the insmod io= irq= shmem= options */ -static int irq; -static char device[9]; /* use eg. device=arc1 to change name */ - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param_string(device, device, sizeof(device), 0); -MODULE_DESCRIPTION("ARCnet COM90xx IO mapped chipset driver"); -MODULE_LICENSE("GPL"); - -#ifndef MODULE -static int __init com90io_setup(char *s) -{ - int ints[4]; - - s = get_options(s, 4, ints); - if (!ints[0]) - return 0; - switch (ints[0]) { - default: /* ERROR */ - pr_err("Too many arguments\n"); - fallthrough; - case 2: /* IRQ */ - irq = ints[2]; - fallthrough; - case 1: /* IO address */ - io = ints[1]; - } - if (*s) - snprintf(device, sizeof(device), "%s", s); - return 1; -} -__setup("com90io=", com90io_setup); -#endif - -static struct net_device *my_dev; - -static int __init com90io_init(void) -{ - struct net_device *dev; - int err; - - dev = alloc_arcdev(device); - if (!dev) - return -ENOMEM; - - dev->base_addr = io; - dev->irq = irq; - if (dev->irq == 2) - dev->irq = 9; - - err = com90io_probe(dev); - - if (err) { - free_arcdev(dev); - return err; - } - - my_dev = dev; - return 0; -} - -static void __exit com90io_exit(void) -{ - struct net_device *dev = my_dev; - int ioaddr = dev->base_addr; - - unregister_netdev(dev); - - /* In case the old driver is loaded later, - * set the thing back to MMAP mode - */ - arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag, - ioaddr, COM9026_REG_RW_CONFIG); - - free_irq(dev->irq, dev); - release_region(dev->base_addr, ARCNET_TOTAL_SIZE); - free_arcdev(dev); -} - -module_init(com90io_init) -module_exit(com90io_exit) diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c deleted file mode 100644 index b3b287c16561..000000000000 --- a/drivers/net/arcnet/com90xx.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Linux ARCnet driver - COM90xx chipset (memory-mapped buffers) - * - * Written 1994-1999 by Avery Pennarun. - * Written 1999 by Martin Mares . - * Derived from skeleton.c by Donald Becker. - * - * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) - * for sponsoring the further development of this driver. - * - * ********************** - * - * The original copyright of skeleton.c was as follows: - * - * skeleton.c Written 1993 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may only be used - * and distributed according to the terms of the GNU General Public License as - * modified by SRC, incorporated herein by reference. - * - * ********************** - * - * For more details, see drivers/net/arcnet.c - * - * ********************** - */ - -#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arcdevice.h" -#include "com9026.h" - -/* Define this to speed up the autoprobe by assuming if only one io port and - * shmem are left in the list at Stage 5, they must correspond to each - * other. - * - * This is undefined by default because it might not always be true, and the - * extra check makes the autoprobe even more careful. Speed demons can turn - * it on - I think it should be fine if you only have one ARCnet card - * installed. - * - * If no ARCnet cards are installed, this delay never happens anyway and thus - * the option has no effect. - */ -#undef FAST_PROBE - -/* Internal function declarations */ -static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *); -static void com90xx_command(struct net_device *dev, int command); -static int com90xx_status(struct net_device *dev); -static void com90xx_setmask(struct net_device *dev, int mask); -static int com90xx_reset(struct net_device *dev, int really_reset); -static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset, - void *buf, int count); -static void com90xx_copy_from_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count); - -/* Known ARCnet cards */ - -static struct net_device *cards[16]; -static int numcards; - -/* Handy defines for ARCnet specific stuff */ - -/* The number of low I/O ports used by the card */ -#define ARCNET_TOTAL_SIZE 16 - -/* Amount of I/O memory used by the card */ -#define BUFFER_SIZE (512) -#define MIRROR_SIZE (BUFFER_SIZE * 4) - -static int com90xx_skip_probe __initdata = 0; - -/* Module parameters */ - -static int io; /* use the insmod io= irq= shmem= options */ -static int irq; -static int shmem; -static char device[9]; /* use eg. device=arc1 to change name */ - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param(shmem, int, 0); -module_param_string(device, device, sizeof(device), 0); - -static void __init com90xx_probe(void) -{ - int count, status, ioaddr, numprint, airq, openparen = 0; - unsigned long airqmask; - int ports[(0x3f0 - 0x200) / 16 + 1] = { 0 }; - unsigned long *shmems; - void __iomem **iomem; - int numports, numshmems, *port; - u_long *p; - int index; - - if (!io && !irq && !shmem && !*device && com90xx_skip_probe) - return; - - shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long), - GFP_KERNEL); - if (!shmems) - return; - iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *), - GFP_KERNEL); - if (!iomem) { - kfree(shmems); - return; - } - - if (BUGLVL(D_NORMAL)) - pr_info("%s\n", "COM90xx chipset support"); - - /* set up the arrays where we'll store the possible probe addresses */ - numports = numshmems = 0; - if (io) - ports[numports++] = io; - else - for (count = 0x200; count <= 0x3f0; count += 16) - ports[numports++] = count; - if (shmem) - shmems[numshmems++] = shmem; - else - for (count = 0xA0000; count <= 0xFF800; count += 2048) - shmems[numshmems++] = count; - - /* Stage 1: abandon any reserved ports, or ones with status==0xFF - * (empty), and reset any others by reading the reset port. - */ - numprint = -1; - for (port = &ports[0]; port - ports < numports; port++) { - numprint++; - numprint %= 8; - if (!numprint) { - arc_cont(D_INIT, "\n"); - arc_cont(D_INIT, "S1: "); - } - arc_cont(D_INIT, "%Xh ", *port); - - ioaddr = *port; - - if (!request_region(*port, ARCNET_TOTAL_SIZE, - "arcnet (90xx)")) { - arc_cont(D_INIT_REASONS, "(request_region)\n"); - arc_cont(D_INIT_REASONS, "S1: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - *port-- = ports[--numports]; - continue; - } - if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) { - arc_cont(D_INIT_REASONS, "(empty)\n"); - arc_cont(D_INIT_REASONS, "S1: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - release_region(*port, ARCNET_TOTAL_SIZE); - *port-- = ports[--numports]; - continue; - } - /* begin resetting card */ - arcnet_inb(ioaddr, COM9026_REG_R_RESET); - - arc_cont(D_INIT_REASONS, "\n"); - arc_cont(D_INIT_REASONS, "S1: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - } - arc_cont(D_INIT, "\n"); - - if (!numports) { - arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n"); - kfree(shmems); - kfree(iomem); - return; - } - /* Stage 2: we have now reset any possible ARCnet cards, so we can't - * do anything until they finish. If D_INIT, print the list of - * cards that are left. - */ - numprint = -1; - for (port = &ports[0]; port < ports + numports; port++) { - numprint++; - numprint %= 8; - if (!numprint) { - arc_cont(D_INIT, "\n"); - arc_cont(D_INIT, "S2: "); - } - arc_cont(D_INIT, "%Xh ", *port); - } - arc_cont(D_INIT, "\n"); - mdelay(RESETtime); - - /* Stage 3: abandon any shmem addresses that don't have the signature - * 0xD1 byte in the right place, or are read-only. - */ - numprint = -1; - for (index = 0, p = &shmems[0]; index < numshmems; p++, index++) { - void __iomem *base; - - numprint++; - numprint %= 8; - if (!numprint) { - arc_cont(D_INIT, "\n"); - arc_cont(D_INIT, "S3: "); - } - arc_cont(D_INIT, "%lXh ", *p); - - if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) { - arc_cont(D_INIT_REASONS, "(request_mem_region)\n"); - arc_cont(D_INIT_REASONS, "Stage 3: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - goto out; - } - base = ioremap(*p, MIRROR_SIZE); - if (!base) { - arc_cont(D_INIT_REASONS, "(ioremap)\n"); - arc_cont(D_INIT_REASONS, "Stage 3: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - goto out1; - } - if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) { - arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n", - arcnet_readb(base, COM9026_REG_R_STATUS), - TESTvalue); - arc_cont(D_INIT_REASONS, "S3: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - goto out2; - } - /* By writing 0x42 to the TESTvalue location, we also make - * sure no "mirror" shmem areas show up - if they occur - * in another pass through this loop, they will be discarded - * because *cptr != TESTvalue. - */ - arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK); - if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) { - arc_cont(D_INIT_REASONS, "(read only)\n"); - arc_cont(D_INIT_REASONS, "S3: "); - goto out2; - } - arc_cont(D_INIT_REASONS, "\n"); - arc_cont(D_INIT_REASONS, "S3: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - iomem[index] = base; - continue; - out2: - iounmap(base); - out1: - release_mem_region(*p, MIRROR_SIZE); - out: - *p-- = shmems[--numshmems]; - index--; - } - arc_cont(D_INIT, "\n"); - - if (!numshmems) { - arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n"); - for (port = &ports[0]; port < ports + numports; port++) - release_region(*port, ARCNET_TOTAL_SIZE); - kfree(shmems); - kfree(iomem); - return; - } - /* Stage 4: something of a dummy, to report the shmems that are - * still possible after stage 3. - */ - numprint = -1; - for (p = &shmems[0]; p < shmems + numshmems; p++) { - numprint++; - numprint %= 8; - if (!numprint) { - arc_cont(D_INIT, "\n"); - arc_cont(D_INIT, "S4: "); - } - arc_cont(D_INIT, "%lXh ", *p); - } - arc_cont(D_INIT, "\n"); - - /* Stage 5: for any ports that have the correct status, can disable - * the RESET flag, and (if no irq is given) generate an autoirq, - * register an ARCnet device. - * - * Currently, we can only register one device per probe, so quit - * after the first one is found. - */ - numprint = -1; - for (port = &ports[0]; port < ports + numports; port++) { - int found = 0; - - numprint++; - numprint %= 8; - if (!numprint) { - arc_cont(D_INIT, "\n"); - arc_cont(D_INIT, "S5: "); - } - arc_cont(D_INIT, "%Xh ", *port); - - ioaddr = *port; - status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); - - if ((status & 0x9D) - != (NORXflag | RECONflag | TXFREEflag | RESETflag)) { - arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status); - arc_cont(D_INIT_REASONS, "S5: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - release_region(*port, ARCNET_TOTAL_SIZE); - *port-- = ports[--numports]; - continue; - } - arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, - ioaddr, COM9026_REG_W_COMMAND); - status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS); - if (status & RESETflag) { - arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n", - status); - arc_cont(D_INIT_REASONS, "S5: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - release_region(*port, ARCNET_TOTAL_SIZE); - *port-- = ports[--numports]; - continue; - } - /* skip this completely if an IRQ was given, because maybe - * we're on a machine that locks during autoirq! - */ - if (!irq) { - /* if we do this, we're sure to get an IRQ since the - * card has just reset and the NORXflag is on until - * we tell it to start receiving. - */ - airqmask = probe_irq_on(); - arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK); - udelay(1); - arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK); - airq = probe_irq_off(airqmask); - - if (airq <= 0) { - arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq); - arc_cont(D_INIT_REASONS, "S5: "); - if (BUGLVL(D_INIT_REASONS)) - numprint = 0; - release_region(*port, ARCNET_TOTAL_SIZE); - *port-- = ports[--numports]; - continue; - } - } else { - airq = irq; - } - - arc_cont(D_INIT, "(%d,", airq); - openparen = 1; - - /* Everything seems okay. But which shmem, if any, puts - * back its signature byte when the card is reset? - * - * If there are multiple cards installed, there might be - * multiple shmems still in the list. - */ -#ifdef FAST_PROBE - if (numports > 1 || numshmems > 1) { - arcnet_inb(ioaddr, COM9026_REG_R_RESET); - mdelay(RESETtime); - } else { - /* just one shmem and port, assume they match */ - arcnet_writeb(TESTvalue, iomem[0], - COM9026_REG_W_INTMASK); - } -#else - arcnet_inb(ioaddr, COM9026_REG_R_RESET); - mdelay(RESETtime); -#endif - - for (index = 0; index < numshmems; index++) { - u_long ptr = shmems[index]; - void __iomem *base = iomem[index]; - - if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) { /* found one */ - arc_cont(D_INIT, "%lXh)\n", *p); - openparen = 0; - - /* register the card */ - if (com90xx_found(*port, airq, ptr, base) == 0) - found = 1; - numprint = -1; - - /* remove shmem from the list */ - shmems[index] = shmems[--numshmems]; - iomem[index] = iomem[numshmems]; - break; /* go to the next I/O port */ - } else { - arc_cont(D_INIT_REASONS, "%Xh-", - arcnet_readb(base, COM9026_REG_R_STATUS)); - } - } - - if (openparen) { - if (BUGLVL(D_INIT)) - pr_cont("no matching shmem)\n"); - if (BUGLVL(D_INIT_REASONS)) { - pr_cont("S5: "); - numprint = 0; - } - } - if (!found) - release_region(*port, ARCNET_TOTAL_SIZE); - *port-- = ports[--numports]; - } - - if (BUGLVL(D_INIT_REASONS)) - pr_cont("\n"); - - /* Now put back TESTvalue on all leftover shmems. */ - for (index = 0; index < numshmems; index++) { - arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK); - iounmap(iomem[index]); - release_mem_region(shmems[index], MIRROR_SIZE); - } - kfree(shmems); - kfree(iomem); -} - -static int __init check_mirror(unsigned long addr, size_t size) -{ - void __iomem *p; - int res = -1; - - if (!request_mem_region(addr, size, "arcnet (90xx)")) - return -1; - - p = ioremap(addr, size); - if (p) { - if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue) - res = 1; - else - res = 0; - iounmap(p); - } - - release_mem_region(addr, size); - return res; -} - -/* Set up the struct net_device associated with this card. Called after - * probing succeeds. - */ -static int __init com90xx_found(int ioaddr, int airq, u_long shmem, - void __iomem *p) -{ - struct net_device *dev = NULL; - struct arcnet_local *lp; - u_long first_mirror, last_mirror; - int mirror_size; - - /* allocate struct net_device */ - dev = alloc_arcdev(device); - if (!dev) { - arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n"); - iounmap(p); - release_mem_region(shmem, MIRROR_SIZE); - return -ENOMEM; - } - lp = netdev_priv(dev); - /* find the real shared memory start/end points, including mirrors */ - - /* guess the actual size of one "memory mirror" - the number of - * bytes between copies of the shared memory. On most cards, it's - * 2k (or there are no mirrors at all) but on some, it's 4k. - */ - mirror_size = MIRROR_SIZE; - if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue && - check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && - check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) - mirror_size = 2 * MIRROR_SIZE; - - first_mirror = shmem - mirror_size; - while (check_mirror(first_mirror, mirror_size) == 1) - first_mirror -= mirror_size; - first_mirror += mirror_size; - - last_mirror = shmem + mirror_size; - while (check_mirror(last_mirror, mirror_size) == 1) - last_mirror += mirror_size; - last_mirror -= mirror_size; - - dev->mem_start = first_mirror; - dev->mem_end = last_mirror + MIRROR_SIZE - 1; - - iounmap(p); - release_mem_region(shmem, MIRROR_SIZE); - - if (!request_mem_region(dev->mem_start, - dev->mem_end - dev->mem_start + 1, - "arcnet (90xx)")) - goto err_free_dev; - - /* reserve the irq */ - if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) { - arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq); - goto err_release_mem; - } - dev->irq = airq; - - /* Initialize the rest of the device structure. */ - lp->card_name = "COM90xx"; - lp->hw.command = com90xx_command; - lp->hw.status = com90xx_status; - lp->hw.intmask = com90xx_setmask; - lp->hw.reset = com90xx_reset; - lp->hw.owner = THIS_MODULE; - lp->hw.copy_to_card = com90xx_copy_to_card; - lp->hw.copy_from_card = com90xx_copy_from_card; - lp->mem_start = ioremap(dev->mem_start, - dev->mem_end - dev->mem_start + 1); - if (!lp->mem_start) { - arc_printk(D_NORMAL, dev, "Can't remap device memory!\n"); - goto err_free_irq; - } - - /* get and check the station ID from offset 1 in shmem */ - arcnet_set_addr(dev, arcnet_readb(lp->mem_start, - COM9026_REG_R_STATION)); - - dev->base_addr = ioaddr; - - arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n", - dev->dev_addr[0], - dev->base_addr, dev->irq, dev->mem_start, - (dev->mem_end - dev->mem_start + 1) / mirror_size, - mirror_size); - - if (register_netdev(dev)) - goto err_unmap; - - cards[numcards++] = dev; - return 0; - -err_unmap: - iounmap(lp->mem_start); -err_free_irq: - free_irq(dev->irq, dev); -err_release_mem: - release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1); -err_free_dev: - free_arcdev(dev); - return -EIO; -} - -static void com90xx_command(struct net_device *dev, int cmd) -{ - short ioaddr = dev->base_addr; - - arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND); -} - -static int com90xx_status(struct net_device *dev) -{ - short ioaddr = dev->base_addr; - - return arcnet_inb(ioaddr, COM9026_REG_R_STATUS); -} - -static void com90xx_setmask(struct net_device *dev, int mask) -{ - short ioaddr = dev->base_addr; - - arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK); -} - -/* Do a hardware reset on the card, and set up necessary registers. - * - * This should be called as little as possible, because it disrupts the - * token on the network (causes a RECON) and requires a significant delay. - * - * However, it does make sure the card is in a defined state. - */ -static int com90xx_reset(struct net_device *dev, int really_reset) -{ - struct arcnet_local *lp = netdev_priv(dev); - short ioaddr = dev->base_addr; - - arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n", - arcnet_inb(ioaddr, COM9026_REG_R_STATUS)); - - if (really_reset) { - /* reset the card */ - arcnet_inb(ioaddr, COM9026_REG_R_RESET); - mdelay(RESETtime); - } - /* clear flags & end reset */ - arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND); - arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND); - -#if 0 - /* don't do this until we verify that it doesn't hurt older cards! */ - arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag, - ioaddr, COM9026_REG_RW_CONFIG); -#endif - - /* verify that the ARCnet signature byte is present */ - if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) { - if (really_reset) - arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n"); - return 1; - } - /* enable extended (512-byte) packets */ - arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND); - - /* clean out all the memory to make debugging make more sense :) */ - if (BUGLVL(D_DURING)) - memset_io(lp->mem_start, 0x42, 2048); - - /* done! return success. */ - return 0; -} - -static void com90xx_copy_to_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset; - - TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count)); -} - -static void com90xx_copy_from_card(struct net_device *dev, int bufnum, - int offset, void *buf, int count) -{ - struct arcnet_local *lp = netdev_priv(dev); - void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset; - - TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); -} - -MODULE_DESCRIPTION("ARCnet COM90xx normal chipset driver"); -MODULE_LICENSE("GPL"); - -static int __init com90xx_init(void) -{ - if (irq == 2) - irq = 9; - com90xx_probe(); - if (!numcards) - return -EIO; - return 0; -} - -static void __exit com90xx_exit(void) -{ - struct net_device *dev; - struct arcnet_local *lp; - int count; - - for (count = 0; count < numcards; count++) { - dev = cards[count]; - lp = netdev_priv(dev); - - unregister_netdev(dev); - free_irq(dev->irq, dev); - iounmap(lp->mem_start); - release_region(dev->base_addr, ARCNET_TOTAL_SIZE); - release_mem_region(dev->mem_start, - dev->mem_end - dev->mem_start + 1); - free_arcdev(dev); - } -} - -module_init(com90xx_init); -module_exit(com90xx_exit); - -#ifndef MODULE -static int __init com90xx_setup(char *s) -{ - int ints[8]; - - s = get_options(s, 8, ints); - if (!ints[0] && !*s) { - pr_notice("Disabled\n"); - return 1; - } - - switch (ints[0]) { - default: /* ERROR */ - pr_err("Too many arguments\n"); - fallthrough; - case 3: /* Mem address */ - shmem = ints[3]; - fallthrough; - case 2: /* IRQ */ - irq = ints[2]; - fallthrough; - case 1: /* IO address */ - io = ints[1]; - } - - if (*s) - snprintf(device, sizeof(device), "%s", s); - - return 1; -} - -__setup("com90xx=", com90xx_setup); -#endif diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c index 0edf35d971c5..f2ec58d06fc3 100644 --- a/drivers/net/arcnet/rfc1201.c +++ b/drivers/net/arcnet/rfc1201.c @@ -235,7 +235,7 @@ static void rx(struct net_device *dev, int bufnum, skb->protocol = type_trans(skb, dev); netif_rx(skb); } else { /* split packet */ - /* NOTE: MSDOS ARP packet correction should only need to + /* NOTE: MS-DOS ARP packet correction should only need to * apply to unsplit packets, since ARP packets are so short. * * My interpretation of the RFC1201 document is that if a diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index da5866ba0699..5ef841c85526 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -53,7 +53,7 @@ struct bareudp_dev { __be16 port; u16 sport_min; bool multi_proto_mode; - struct socket __rcu *sock; + struct sock __rcu *sk; struct list_head next; /* bareudp node on namespace list */ struct gro_cells gro_cells; }; @@ -228,7 +228,7 @@ static void bareudp_uninit(struct net_device *dev) gro_cells_destroy(&bareudp->gro_cells); } -static struct socket *bareudp_create_sock(struct net *net, __be16 port) +static struct sock *bareudp_create_sock(struct net *net, __be16 port) { struct udp_port_cfg udp_conf; struct socket *sock; @@ -248,18 +248,18 @@ static struct socket *bareudp_create_sock(struct net *net, __be16 port) return ERR_PTR(err); udp_allow_gso(sock->sk); - return sock; + return sock->sk; } /* Create new listen socket if needed */ static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port) { struct udp_tunnel_sock_cfg tunnel_cfg; - struct socket *sock; + struct sock *sk; - sock = bareudp_create_sock(bareudp->net, port); - if (IS_ERR(sock)) - return PTR_ERR(sock); + sk = bareudp_create_sock(bareudp->net, port); + if (IS_ERR(sk)) + return PTR_ERR(sk); /* Mark socket as an encapsulation socket */ memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); @@ -268,29 +268,26 @@ static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port) tunnel_cfg.encap_rcv = bareudp_udp_encap_recv; tunnel_cfg.encap_err_lookup = bareudp_err_lookup; tunnel_cfg.encap_destroy = NULL; - setup_udp_tunnel_sock(bareudp->net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(bareudp->net, sk, &tunnel_cfg); - rcu_assign_pointer(bareudp->sock, sock); + rcu_assign_pointer(bareudp->sk, sk); return 0; } static int bareudp_open(struct net_device *dev) { struct bareudp_dev *bareudp = netdev_priv(dev); - int ret = 0; - ret = bareudp_socket_create(bareudp, bareudp->port); - return ret; + return bareudp_socket_create(bareudp, bareudp->port); } static void bareudp_sock_release(struct bareudp_dev *bareudp) { - struct socket *sock; + struct sock *sk; - sock = bareudp->sock; - rcu_assign_pointer(bareudp->sock, NULL); - synchronize_net(); - udp_tunnel_sock_release(sock); + sk = rtnl_dereference(bareudp->sk); + rcu_assign_pointer(bareudp->sk, NULL); + udp_tunnel_sock_release(sk); } static int bareudp_stop(struct net_device *dev) @@ -308,7 +305,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags); bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev)); bool use_cache = ip_tunnel_dst_cache_usable(skb, info); - struct socket *sock = rcu_dereference(bareudp->sock); + struct sock *sk = rcu_dereference(bareudp->sk); const struct ip_tunnel_key *key = &info->key; struct rtable *rt; __be16 sport, df; @@ -320,7 +317,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (skb_vlan_inet_prepare(skb, skb->protocol != htons(ETH_P_TEB))) return -EINVAL; - if (!sock) + if (!sk) return -ESHUTDOWN; sport = udp_flow_src_port(bareudp->net, skb, @@ -359,7 +356,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev, goto free_dst; skb_set_inner_protocol(skb, bareudp->ethertype); - udp_tunnel_xmit_skb(rt, sock->sk, skb, saddr, info->key.u.ipv4.dst, + udp_tunnel_xmit_skb(rt, sk, skb, saddr, info->key.u.ipv4.dst, tos, ttl, df, sport, bareudp->port, !net_eq(bareudp->net, dev_net(bareudp->dev)), !test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags), @@ -378,7 +375,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags); bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev)); bool use_cache = ip_tunnel_dst_cache_usable(skb, info); - struct socket *sock = rcu_dereference(bareudp->sock); + struct sock *sk = rcu_dereference(bareudp->sk); const struct ip_tunnel_key *key = &info->key; struct dst_entry *dst = NULL; struct in6_addr saddr, daddr; @@ -390,13 +387,13 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (skb_vlan_inet_prepare(skb, skb->protocol != htons(ETH_P_TEB))) return -EINVAL; - if (!sock) + if (!sk) return -ESHUTDOWN; sport = udp_flow_src_port(bareudp->net, skb, bareudp->sport_min, USHRT_MAX, true); - dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock, 0, &saddr, + dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sk, 0, &saddr, key, sport, bareudp->port, key->tos, use_cache ? (struct dst_cache *) &info->dst_cache : NULL); @@ -427,7 +424,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev, goto free_dst; daddr = info->key.u.ipv6.dst; - udp_tunnel6_xmit_skb(dst, sock->sk, skb, dev, + udp_tunnel6_xmit_skb(dst, sk, skb, dev, &saddr, &daddr, prio, ttl, info->key.label, sport, bareudp->port, !test_bit(IP_TUNNEL_CSUM_BIT, @@ -527,12 +524,13 @@ static int bareudp_fill_metadata_dst(struct net_device *dev, } else if (ip_tunnel_info_af(info) == AF_INET6) { struct dst_entry *dst; struct in6_addr saddr; - struct socket *sock = rcu_dereference(bareudp->sock); + struct sock *sk; - if (!sock) + sk = rcu_dereference(bareudp->sk); + if (!sk) return -ESHUTDOWN; - dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock, + dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sk, 0, &saddr, &info->key, sport, bareudp->port, info->key.tos, use_cache ? &info->dst_cache : NULL); diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 985ef66dc333..acbba08dbdfa 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -745,6 +745,21 @@ static void __set_agg_ports_ready(struct aggregator *aggregator, int val) } } +static int __agg_usable_ports(struct aggregator *agg) +{ + struct port *port; + int valid = 0; + + for (port = agg->lag_ports; port; + port = port->next_port_in_aggregator) { + if (port->actor_oper_port_state & LACP_STATE_COLLECTING && + port->actor_oper_port_state & LACP_STATE_DISTRIBUTING) + valid++; + } + + return valid; +} + static int __agg_active_ports(struct aggregator *agg) { struct port *port; @@ -1179,10 +1194,10 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) switch (port->sm_mux_state) { case AD_MUX_DETACHED: port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; - ad_disable_collecting_distributing(port, - update_slave_arr); port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; + ad_disable_collecting_distributing(port, + update_slave_arr); port->ntt = true; break; case AD_MUX_WAITING: @@ -1322,6 +1337,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) fallthrough; case AD_RX_PORT_DISABLED: port->sm_vars &= ~AD_PORT_MATCHED; + port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION; break; case AD_RX_LACP_DISABLED: port->sm_vars &= ~AD_PORT_SELECTED; @@ -2107,6 +2123,7 @@ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) port->actor_port_number, aggregator->aggregator_identifier); __disable_distributing_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs an update */ *update_slave_arr = true; } @@ -2130,6 +2147,7 @@ static void ad_enable_collecting_distributing(struct port *port, port->actor_port_number, aggregator->aggregator_identifier); __enable_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs update */ *update_slave_arr = true; /* Should notify peers if possible */ @@ -2153,6 +2171,7 @@ static void ad_disable_collecting_distributing(struct port *port, port->actor_port_number, aggregator->aggregator_identifier); __disable_port(port); + bond_3ad_set_carrier(port->slave->bond); /* Slave array needs an update */ *update_slave_arr = true; } @@ -2832,7 +2851,9 @@ int bond_3ad_set_carrier(struct bonding *bond) active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator)); if (active) { /* are enough slaves available to consider link up? */ - if (__agg_active_ports(active) < bond->params.min_links) { + if ((bond->params.lacp_strict ? __agg_usable_ports(active) + : __agg_active_ports(active)) < + bond->params.min_links) { if (netif_carrier_ok(bond->dev)) { netif_carrier_off(bond->dev); goto out; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8e75453ce0ef..e044fc733b8c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -787,6 +787,10 @@ int bond_set_carrier(struct bonding *bond) * values are invalid, set speed and duplex to -1, * and return. Return 1 if speed or duplex settings are * UNKNOWN; 0 otherwise. + * + * Caller must hold the slave's netdev ops lock. The notifier path + * (bond_netdev_event NETDEV_CHANGE/UP) reaches us with the slave's ops + * lock held; other call sites take it explicitly. */ static int bond_update_speed_duplex(struct slave *slave) { @@ -794,7 +798,7 @@ static int bond_update_speed_duplex(struct slave *slave) struct ethtool_link_ksettings ecmd; int res; - res = __ethtool_get_link_ksettings(slave_dev, &ecmd); + res = netif_get_link_ksettings(slave_dev, &ecmd); if (res < 0) goto speed_duplex_unknown; if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) @@ -2112,8 +2116,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, new_slave->delay = 0; new_slave->link_failure_count = 0; - if (bond_update_speed_duplex(new_slave) && - bond_needs_speed_duplex(bond)) + netdev_lock_ops(slave_dev); + res = bond_update_speed_duplex(new_slave); + netdev_unlock_ops(slave_dev); + if (res && bond_needs_speed_duplex(bond)) new_slave->link = BOND_LINK_DOWN; new_slave->last_rx = jiffies - @@ -2780,6 +2786,7 @@ static void bond_miimon_commit(struct bonding *bond) struct slave *slave, *primary, *active; bool do_failover = false; struct list_head *iter; + int err; ASSERT_RTNL(); @@ -2798,8 +2805,10 @@ static void bond_miimon_commit(struct bonding *bond) continue; case BOND_LINK_UP: - if (bond_update_speed_duplex(slave) && - bond_needs_speed_duplex(bond)) { + netdev_lock_ops(slave->dev); + err = bond_update_speed_duplex(slave); + netdev_unlock_ops(slave->dev); + if (err && bond_needs_speed_duplex(bond)) { slave->link = BOND_LINK_DOWN; if (net_ratelimit()) slave_warn(bond->dev, slave->dev, @@ -5861,7 +5870,9 @@ static int bond_ethtool_get_link_ksettings(struct net_device *bond_dev, */ bond_for_each_slave(bond, slave, iter) { if (bond_slave_can_tx(slave)) { + netdev_lock_ops(slave->dev); bond_update_speed_duplex(slave); + netdev_unlock_ops(slave->dev); if (slave->speed != SPEED_UNKNOWN) { if (BOND_MODE(bond) == BOND_MODE_BROADCAST) speed = bond_mode_bcast_speed(slave, @@ -6446,6 +6457,7 @@ static int __init bond_check_params(struct bond_params *params) params->ad_user_port_key = ad_user_port_key; params->coupled_control = 1; params->broadcast_neighbor = 0; + params->lacp_strict = 0; if (packets_per_slave > 0) { params->reciprocal_packets_per_slave = reciprocal_value(packets_per_slave); diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 90365d3f7ebf..4a11572f663d 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -143,6 +143,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { [IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED }, [IFLA_BOND_COUPLED_CONTROL] = { .type = NLA_U8 }, [IFLA_BOND_BROADCAST_NEIGH] = { .type = NLA_U8 }, + [IFLA_BOND_LACP_STRICT] = { .type = NLA_U8 }, }; static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = { @@ -599,6 +600,16 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[], return err; } + if (data[IFLA_BOND_LACP_STRICT]) { + int fallback_mode = nla_get_u8(data[IFLA_BOND_LACP_STRICT]); + + bond_opt_initval(&newval, fallback_mode); + err = __bond_opt_set(bond, BOND_OPT_LACP_STRICT, &newval, + data[IFLA_BOND_LACP_STRICT], extack); + if (err) + return err; + } + return 0; } @@ -671,6 +682,7 @@ static size_t bond_get_size(const struct net_device *bond_dev) nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS + nla_total_size(sizeof(u8)) + /* IFLA_BOND_COUPLED_CONTROL */ nla_total_size(sizeof(u8)) + /* IFLA_BOND_BROADCAST_NEIGH */ + nla_total_size(sizeof(u8)) + /* IFLA_BOND_LACP_STRICT */ 0; } @@ -838,6 +850,10 @@ static int bond_fill_info(struct sk_buff *skb, bond->params.broadcast_neighbor)) goto nla_put_failure; + if (nla_put_u8(skb, IFLA_BOND_LACP_STRICT, + bond->params.lacp_strict)) + goto nla_put_failure; + if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info info; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 7380cc4ee75a..e590c8dee86e 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -68,6 +68,8 @@ static int bond_option_lacp_active_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_lacp_rate_set(struct bonding *bond, const struct bond_opt_value *newval); +static int bond_option_lacp_strict_set(struct bonding *bond, + const struct bond_opt_value *newval); static int bond_option_ad_select_set(struct bonding *bond, const struct bond_opt_value *newval); static int bond_option_queue_id_set(struct bonding *bond, @@ -162,6 +164,12 @@ static const struct bond_opt_value bond_lacp_rate_tbl[] = { { NULL, -1, 0}, }; +static const struct bond_opt_value bond_lacp_strict_tbl[] = { + { "off", 0, BOND_VALFLAG_DEFAULT}, + { "on", 1, 0}, + { NULL, -1, 0 } +}; + static const struct bond_opt_value bond_ad_select_tbl[] = { { "stable", BOND_AD_STABLE, BOND_VALFLAG_DEFAULT}, { "bandwidth", BOND_AD_BANDWIDTH, 0}, @@ -363,6 +371,14 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = { .values = bond_lacp_rate_tbl, .set = bond_option_lacp_rate_set }, + [BOND_OPT_LACP_STRICT] = { + .id = BOND_OPT_LACP_STRICT, + .name = "lacp_strict", + .desc = "Define the LACP fallback mode when no slaves have negotiated", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)), + .values = bond_lacp_strict_tbl, + .set = bond_option_lacp_strict_set + }, [BOND_OPT_MINLINKS] = { .id = BOND_OPT_MINLINKS, .name = "min_links", @@ -902,22 +918,22 @@ static int bond_option_mode_set(struct bonding *bond, netdev_dbg(bond->dev, "%s mode is incompatible with arp monitoring, start mii monitoring\n", newval->string); /* disable arp monitoring */ - bond->params.arp_interval = 0; + WRITE_ONCE(bond->params.arp_interval, 0); } if (!bond->params.miimon) { /* set miimon to default value */ - bond->params.miimon = BOND_DEFAULT_MIIMON; + WRITE_ONCE(bond->params.miimon, BOND_DEFAULT_MIIMON); netdev_dbg(bond->dev, "Setting MII monitoring interval to %d\n", bond->params.miimon); } } if (newval->value == BOND_MODE_ALB) - bond->params.tlb_dynamic_lb = 1; + WRITE_ONCE(bond->params.tlb_dynamic_lb, 1); /* don't cache arp_validate between modes */ - bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; + WRITE_ONCE(bond->params.arp_validate, BOND_ARP_VALIDATE_NONE); bond->params.mode = newval->value; /* When changing mode, the bond device is down, we may reduce @@ -1010,7 +1026,7 @@ static int bond_option_miimon_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting MII monitoring interval to %llu\n", newval->value); - bond->params.miimon = newval->value; + WRITE_ONCE(bond->params.miimon, newval->value); if (bond->params.updelay) netdev_dbg(bond->dev, "Note: Updating updelay (to %d) since it is a multiple of the miimon value\n", bond->params.updelay * bond->params.miimon); @@ -1022,9 +1038,10 @@ static int bond_option_miimon_set(struct bonding *bond, bond->params.peer_notif_delay * bond->params.miimon); if (newval->value && bond->params.arp_interval) { netdev_dbg(bond->dev, "MII monitoring cannot be used with ARP monitoring - disabling ARP monitoring...\n"); - bond->params.arp_interval = 0; + WRITE_ONCE(bond->params.arp_interval, 0); if (bond->params.arp_validate) - bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; + WRITE_ONCE(bond->params.arp_validate, + BOND_ARP_VALIDATE_NONE); } if (bond->dev->flags & IFF_UP) { /* If the interface is up, we may need to fire off @@ -1067,7 +1084,7 @@ static int _bond_option_delay_set(struct bonding *bond, (value / bond->params.miimon) * bond->params.miimon); } - *target = value / bond->params.miimon; + WRITE_ONCE(*target, value / bond->params.miimon); netdev_dbg(bond->dev, "Setting %s to %d\n", name, *target * bond->params.miimon); @@ -1113,11 +1130,11 @@ static int bond_option_arp_interval_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting ARP monitoring interval to %llu\n", newval->value); - bond->params.arp_interval = newval->value; + WRITE_ONCE(bond->params.arp_interval, newval->value); if (newval->value) { if (bond->params.miimon) { netdev_dbg(bond->dev, "ARP monitoring cannot be used with MII monitoring. Disabling MII monitoring\n"); - bond->params.miimon = 0; + WRITE_ONCE(bond->params.miimon, 0); } if (!bond->params.arp_targets[0]) netdev_dbg(bond->dev, "ARP monitoring has been set up, but no ARP targets have been specified\n"); @@ -1154,7 +1171,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot, if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) { bond_for_each_slave(bond, slave, iter) WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx); - targets[slot] = target; + WRITE_ONCE(targets[slot], target); } } @@ -1226,8 +1243,8 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target) WRITE_ONCE(targets_rx[i], 0); } for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++) - targets[i] = targets[i+1]; - targets[i] = 0; + WRITE_ONCE(targets[i], targets[i+1]); + WRITE_ONCE(targets[i], 0); return 0; } @@ -1449,7 +1466,7 @@ static int bond_option_arp_validate_set(struct bonding *bond, netdev_dbg(bond->dev, "Setting arp_validate to %s (%llu)\n", newval->string, newval->value); - bond->params.arp_validate = newval->value; + WRITE_ONCE(bond->params.arp_validate, newval->value); if (changed) { bond_for_each_slave(bond, slave, iter) @@ -1464,7 +1481,7 @@ static int bond_option_arp_all_targets_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting arp_all_targets to %s (%llu)\n", newval->string, newval->value); - bond->params.arp_all_targets = newval->value; + WRITE_ONCE(bond->params.arp_all_targets, newval->value); return 0; } @@ -1474,7 +1491,7 @@ static int bond_option_missed_max_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting missed max to %s (%llu)\n", newval->string, newval->value); - bond->params.missed_max = newval->value; + WRITE_ONCE(bond->params.missed_max, newval->value); return 0; } @@ -1553,7 +1570,7 @@ static int bond_option_primary_reselect_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting primary_reselect to %s (%llu)\n", newval->string, newval->value); - bond->params.primary_reselect = newval->value; + WRITE_ONCE(bond->params.primary_reselect, newval->value); block_netpoll_tx(); bond_select_active_slave(bond); @@ -1567,7 +1584,7 @@ static int bond_option_fail_over_mac_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting fail_over_mac to %s (%llu)\n", newval->string, newval->value); - bond->params.fail_over_mac = newval->value; + WRITE_ONCE(bond->params.fail_over_mac, newval->value); return 0; } @@ -1579,7 +1596,7 @@ static int bond_option_xmit_hash_policy_set(struct bonding *bond, return -EOPNOTSUPP; netdev_dbg(bond->dev, "Setting xmit hash policy to %s (%llu)\n", newval->string, newval->value); - bond->params.xmit_policy = newval->value; + WRITE_ONCE(bond->params.xmit_policy, newval->value); return 0; } @@ -1589,7 +1606,7 @@ static int bond_option_resend_igmp_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting resend_igmp to %llu\n", newval->value); - bond->params.resend_igmp = newval->value; + WRITE_ONCE(bond->params.resend_igmp, newval->value); return 0; } @@ -1597,7 +1614,7 @@ static int bond_option_resend_igmp_set(struct bonding *bond, static int bond_option_num_peer_notif_set(struct bonding *bond, const struct bond_opt_value *newval) { - bond->params.num_peer_notif = newval->value; + WRITE_ONCE(bond->params.num_peer_notif, newval->value); return 0; } @@ -1610,7 +1627,7 @@ static int bond_option_all_slaves_active_set(struct bonding *bond, if (newval->value == bond->params.all_slaves_active) return 0; - bond->params.all_slaves_active = newval->value; + WRITE_ONCE(bond->params.all_slaves_active, newval->value); bond_for_each_slave(bond, slave, iter) { if (!bond_is_active_slave(slave)) { if (newval->value) @@ -1628,7 +1645,7 @@ static int bond_option_min_links_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting min links value to %llu\n", newval->value); - bond->params.min_links = newval->value; + WRITE_ONCE(bond->params.min_links, newval->value); bond_set_carrier(bond); return 0; @@ -1637,7 +1654,7 @@ static int bond_option_min_links_set(struct bonding *bond, static int bond_option_lp_interval_set(struct bonding *bond, const struct bond_opt_value *newval) { - bond->params.lp_interval = newval->value; + WRITE_ONCE(bond->params.lp_interval, newval->value); return 0; } @@ -1647,7 +1664,7 @@ static int bond_option_pps_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting packets per slave to %llu\n", newval->value); - bond->params.packets_per_slave = newval->value; + WRITE_ONCE(bond->params.packets_per_slave, newval->value); if (newval->value > 0) { bond->params.reciprocal_packets_per_slave = reciprocal_value(newval->value); @@ -1667,7 +1684,7 @@ static int bond_option_lacp_active_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting LACP active to %s (%llu)\n", newval->string, newval->value); - bond->params.lacp_active = newval->value; + WRITE_ONCE(bond->params.lacp_active, newval->value); bond_3ad_update_lacp_active(bond); return 0; @@ -1678,18 +1695,29 @@ static int bond_option_lacp_rate_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting LACP rate to %s (%llu)\n", newval->string, newval->value); - bond->params.lacp_fast = newval->value; + WRITE_ONCE(bond->params.lacp_fast, newval->value); bond_3ad_update_lacp_rate(bond); return 0; } +static int bond_option_lacp_strict_set(struct bonding *bond, + const struct bond_opt_value *newval) +{ + netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n", + newval->string, newval->value); + bond->params.lacp_strict = newval->value; + bond_3ad_set_carrier(bond); + + return 0; +} + static int bond_option_ad_select_set(struct bonding *bond, const struct bond_opt_value *newval) { netdev_dbg(bond->dev, "Setting ad_select to %s (%llu)\n", newval->string, newval->value); - bond->params.ad_select = newval->value; + WRITE_ONCE(bond->params.ad_select, newval->value); return 0; } @@ -1808,7 +1836,7 @@ static int bond_option_tlb_dynamic_lb_set(struct bonding *bond, { netdev_dbg(bond->dev, "Setting dynamic-lb to %s (%llu)\n", newval->string, newval->value); - bond->params.tlb_dynamic_lb = newval->value; + WRITE_ONCE(bond->params.tlb_dynamic_lb, newval->value); return 0; } @@ -1819,7 +1847,7 @@ static int bond_option_ad_actor_sys_prio_set(struct bonding *bond, netdev_dbg(bond->dev, "Setting ad_actor_sys_prio to %llu\n", newval->value); - bond->params.ad_actor_sys_prio = newval->value; + WRITE_ONCE(bond->params.ad_actor_sys_prio, newval->value); bond_3ad_update_ad_actor_settings(bond); return 0; @@ -1879,7 +1907,7 @@ static int bond_option_ad_user_port_key_set(struct bonding *bond, netdev_dbg(bond->dev, "Setting ad_user_port_key to %llu\n", newval->value); - bond->params.ad_user_port_key = newval->value; + WRITE_ONCE(bond->params.ad_user_port_key, newval->value); return 0; } @@ -1889,7 +1917,7 @@ static int bond_option_coupled_control_set(struct bonding *bond, netdev_info(bond->dev, "Setting coupled_control to %s (%llu)\n", newval->string, newval->value); - bond->params.coupled_control = newval->value; + WRITE_ONCE(bond->params.coupled_control, newval->value); return 0; } diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 3607b62f9b63..9d71053cddc5 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -61,27 +61,28 @@ static void bond_info_show_master(struct seq_file *seq) struct bonding *bond = pde_data(file_inode(seq->file)); const struct bond_opt_value *optval; struct slave *curr, *primary; - int i; + int arp_interval, fail_over_mac, miimon, i; curr = rcu_dereference(bond->curr_active_slave); seq_printf(seq, "Bonding Mode: %s", bond_mode_name(BOND_MODE(bond))); - if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP && - bond->params.fail_over_mac) { + fail_over_mac = READ_ONCE(bond->params.fail_over_mac); + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP && fail_over_mac) { optval = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC, - bond->params.fail_over_mac); + fail_over_mac); seq_printf(seq, " (fail_over_mac %s)", optval->string); } seq_printf(seq, "\n"); if (bond_mode_uses_xmit_hash(bond)) { - optval = bond_opt_get_val(BOND_OPT_XMIT_HASH, - bond->params.xmit_policy); + int xmit_policy = READ_ONCE(bond->params.xmit_policy); + + optval = bond_opt_get_val(BOND_OPT_XMIT_HASH, xmit_policy); seq_printf(seq, "Transmit Hash Policy: %s (%d)\n", - optval->string, bond->params.xmit_policy); + optval->string, xmit_policy); } if (bond_uses_primary(bond)) { @@ -90,7 +91,7 @@ static void bond_info_show_master(struct seq_file *seq) primary ? primary->dev->name : "None"); if (primary) { optval = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, - bond->params.primary_reselect); + READ_ONCE(bond->params.primary_reselect)); seq_printf(seq, " (primary_reselect %s)", optval->string); } @@ -101,32 +102,36 @@ static void bond_info_show_master(struct seq_file *seq) seq_printf(seq, "MII Status: %s\n", netif_carrier_ok(bond->dev) ? "up" : "down"); - seq_printf(seq, "MII Polling Interval (ms): %d\n", bond->params.miimon); + miimon = READ_ONCE(bond->params.miimon); + seq_printf(seq, "MII Polling Interval (ms): %d\n", miimon); seq_printf(seq, "Up Delay (ms): %d\n", - bond->params.updelay * bond->params.miimon); + READ_ONCE(bond->params.updelay) * miimon); seq_printf(seq, "Down Delay (ms): %d\n", - bond->params.downdelay * bond->params.miimon); + READ_ONCE(bond->params.downdelay) * miimon); seq_printf(seq, "Peer Notification Delay (ms): %d\n", - bond->params.peer_notif_delay * bond->params.miimon); + READ_ONCE(bond->params.peer_notif_delay) * miimon); /* ARP information */ - if (bond->params.arp_interval > 0) { + arp_interval = READ_ONCE(bond->params.arp_interval); + if (arp_interval > 0) { int printed = 0; seq_printf(seq, "ARP Polling Interval (ms): %d\n", - bond->params.arp_interval); + arp_interval); seq_printf(seq, "ARP Missed Max: %u\n", - bond->params.missed_max); + READ_ONCE(bond->params.missed_max)); seq_printf(seq, "ARP IP target/s (n.n.n.n form):"); for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) { - if (!bond->params.arp_targets[i]) + __be32 t = READ_ONCE(bond->params.arp_targets[i]); + + if (!t) break; if (printed) seq_printf(seq, ","); - seq_printf(seq, " %pI4", &bond->params.arp_targets[i]); + seq_printf(seq, " %pI4", &t); printed = 1; } seq_printf(seq, "\n"); @@ -152,12 +157,13 @@ static void bond_info_show_master(struct seq_file *seq) seq_puts(seq, "\n802.3ad info\n"); seq_printf(seq, "LACP active: %s\n", - (bond->params.lacp_active) ? "on" : "off"); + READ_ONCE(bond->params.lacp_active) ? "on" : "off"); seq_printf(seq, "LACP rate: %s\n", - (bond->params.lacp_fast) ? "fast" : "slow"); - seq_printf(seq, "Min links: %d\n", bond->params.min_links); + READ_ONCE(bond->params.lacp_fast) ? "fast" : "slow"); + seq_printf(seq, "Min links: %d\n", + READ_ONCE(bond->params.min_links)); optval = bond_opt_get_val(BOND_OPT_AD_SELECT, - bond->params.ad_select); + READ_ONCE(bond->params.ad_select)); seq_printf(seq, "Aggregator selection policy (ad_select): %s\n", optval->string); if (capable(CAP_NET_ADMIN)) { diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index eaba44c76a5e..3232406cbd24 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -213,10 +213,12 @@ static ssize_t bonding_show_xmit_hash(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int xmit_policy; - val = bond_opt_get_val(BOND_OPT_XMIT_HASH, bond->params.xmit_policy); + xmit_policy = READ_ONCE(bond->params.xmit_policy); + val = bond_opt_get_val(BOND_OPT_XMIT_HASH, xmit_policy); - return sysfs_emit(buf, "%s %d\n", val->string, bond->params.xmit_policy); + return sysfs_emit(buf, "%s %d\n", val->string, xmit_policy); } static DEVICE_ATTR(xmit_hash_policy, 0644, bonding_show_xmit_hash, bonding_sysfs_store_option); @@ -228,11 +230,12 @@ static ssize_t bonding_show_arp_validate(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int arp_validate; - val = bond_opt_get_val(BOND_OPT_ARP_VALIDATE, - bond->params.arp_validate); + arp_validate = READ_ONCE(bond->params.arp_validate); + val = bond_opt_get_val(BOND_OPT_ARP_VALIDATE, arp_validate); - return sysfs_emit(buf, "%s %d\n", val->string, bond->params.arp_validate); + return sysfs_emit(buf, "%s %d\n", val->string, arp_validate); } static DEVICE_ATTR(arp_validate, 0644, bonding_show_arp_validate, bonding_sysfs_store_option); @@ -244,11 +247,11 @@ static ssize_t bonding_show_arp_all_targets(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int arp_all_targets; - val = bond_opt_get_val(BOND_OPT_ARP_ALL_TARGETS, - bond->params.arp_all_targets); - return sysfs_emit(buf, "%s %d\n", - val->string, bond->params.arp_all_targets); + arp_all_targets = READ_ONCE(bond->params.arp_all_targets); + val = bond_opt_get_val(BOND_OPT_ARP_ALL_TARGETS, arp_all_targets); + return sysfs_emit(buf, "%s %d\n", val->string, arp_all_targets); } static DEVICE_ATTR(arp_all_targets, 0644, bonding_show_arp_all_targets, bonding_sysfs_store_option); @@ -260,11 +263,12 @@ static ssize_t bonding_show_fail_over_mac(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int fail_over_mac; - val = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC, - bond->params.fail_over_mac); + fail_over_mac = READ_ONCE(bond->params.fail_over_mac); + val = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC, fail_over_mac); - return sysfs_emit(buf, "%s %d\n", val->string, bond->params.fail_over_mac); + return sysfs_emit(buf, "%s %d\n", val->string, fail_over_mac); } static DEVICE_ATTR(fail_over_mac, 0644, bonding_show_fail_over_mac, bonding_sysfs_store_option); @@ -276,7 +280,7 @@ static ssize_t bonding_show_arp_interval(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.arp_interval); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.arp_interval)); } static DEVICE_ATTR(arp_interval, 0644, bonding_show_arp_interval, bonding_sysfs_store_option); @@ -290,9 +294,10 @@ static ssize_t bonding_show_arp_targets(struct device *d, int i, res = 0; for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { - if (bond->params.arp_targets[i]) - res += sysfs_emit_at(buf, res, "%pI4 ", - &bond->params.arp_targets[i]); + __be32 t = READ_ONCE(bond->params.arp_targets[i]); + + if (t) + res += sysfs_emit_at(buf, res, "%pI4 ", &t); } if (res) buf[res-1] = '\n'; /* eat the leftover space */ @@ -309,7 +314,7 @@ static ssize_t bonding_show_missed_max(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%u\n", bond->params.missed_max); + return sysfs_emit(buf, "%u\n", READ_ONCE(bond->params.missed_max)); } static DEVICE_ATTR(arp_missed_max, 0644, bonding_show_missed_max, bonding_sysfs_store_option); @@ -321,7 +326,8 @@ static ssize_t bonding_show_downdelay(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.downdelay * bond->params.miimon); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.downdelay) * + READ_ONCE(bond->params.miimon)); } static DEVICE_ATTR(downdelay, 0644, bonding_show_downdelay, bonding_sysfs_store_option); @@ -332,7 +338,8 @@ static ssize_t bonding_show_updelay(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.updelay * bond->params.miimon); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.updelay) * + READ_ONCE(bond->params.miimon)); } static DEVICE_ATTR(updelay, 0644, @@ -345,7 +352,8 @@ static ssize_t bonding_show_peer_notif_delay(struct device *d, struct bonding *bond = to_bond(d); return sysfs_emit(buf, "%d\n", - bond->params.peer_notif_delay * bond->params.miimon); + READ_ONCE(bond->params.peer_notif_delay) * + READ_ONCE(bond->params.miimon)); } static DEVICE_ATTR(peer_notif_delay, 0644, bonding_show_peer_notif_delay, bonding_sysfs_store_option); @@ -357,10 +365,12 @@ static ssize_t bonding_show_lacp_active(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int lacp_active; - val = bond_opt_get_val(BOND_OPT_LACP_ACTIVE, bond->params.lacp_active); + lacp_active = READ_ONCE(bond->params.lacp_active); + val = bond_opt_get_val(BOND_OPT_LACP_ACTIVE, lacp_active); - return sysfs_emit(buf, "%s %d\n", val->string, bond->params.lacp_active); + return sysfs_emit(buf, "%s %d\n", val->string, lacp_active); } static DEVICE_ATTR(lacp_active, 0644, bonding_show_lacp_active, bonding_sysfs_store_option); @@ -371,10 +381,12 @@ static ssize_t bonding_show_lacp_rate(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int lacp_fast; - val = bond_opt_get_val(BOND_OPT_LACP_RATE, bond->params.lacp_fast); + lacp_fast = READ_ONCE(bond->params.lacp_fast); + val = bond_opt_get_val(BOND_OPT_LACP_RATE, lacp_fast); - return sysfs_emit(buf, "%s %d\n", val->string, bond->params.lacp_fast); + return sysfs_emit(buf, "%s %d\n", val->string, lacp_fast); } static DEVICE_ATTR(lacp_rate, 0644, bonding_show_lacp_rate, bonding_sysfs_store_option); @@ -385,7 +397,7 @@ static ssize_t bonding_show_min_links(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%u\n", bond->params.min_links); + return sysfs_emit(buf, "%u\n", READ_ONCE(bond->params.min_links)); } static DEVICE_ATTR(min_links, 0644, bonding_show_min_links, bonding_sysfs_store_option); @@ -396,10 +408,12 @@ static ssize_t bonding_show_ad_select(struct device *d, { struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int ad_select; - val = bond_opt_get_val(BOND_OPT_AD_SELECT, bond->params.ad_select); + ad_select = READ_ONCE(bond->params.ad_select); + val = bond_opt_get_val(BOND_OPT_AD_SELECT, ad_select); - return sysfs_emit(buf, "%s %d\n", val->string, bond->params.ad_select); + return sysfs_emit(buf, "%s %d\n", val->string, ad_select); } static DEVICE_ATTR(ad_select, 0644, bonding_show_ad_select, bonding_sysfs_store_option); @@ -411,7 +425,7 @@ static ssize_t bonding_show_num_peer_notif(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.num_peer_notif); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.num_peer_notif)); } static DEVICE_ATTR(num_grat_arp, 0644, bonding_show_num_peer_notif, bonding_sysfs_store_option); @@ -425,7 +439,7 @@ static ssize_t bonding_show_miimon(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.miimon); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.miimon)); } static DEVICE_ATTR(miimon, 0644, bonding_show_miimon, bonding_sysfs_store_option); @@ -455,14 +469,15 @@ static ssize_t bonding_show_primary_reselect(struct device *d, struct device_attribute *attr, char *buf) { - struct bonding *bond = to_bond(d); + const struct bonding *bond = to_bond(d); const struct bond_opt_value *val; + int primary_reselect; - val = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, - bond->params.primary_reselect); + primary_reselect = READ_ONCE(bond->params.primary_reselect); - return sysfs_emit(buf, "%s %d\n", - val->string, bond->params.primary_reselect); + val = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT, primary_reselect); + + return sysfs_emit(buf, "%s %d\n", val->string, primary_reselect); } static DEVICE_ATTR(primary_reselect, 0644, bonding_show_primary_reselect, bonding_sysfs_store_option); @@ -655,7 +670,7 @@ static ssize_t bonding_show_slaves_active(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.all_slaves_active); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.all_slaves_active)); } static DEVICE_ATTR(all_slaves_active, 0644, bonding_show_slaves_active, bonding_sysfs_store_option); @@ -667,7 +682,7 @@ static ssize_t bonding_show_resend_igmp(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.resend_igmp); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.resend_igmp)); } static DEVICE_ATTR(resend_igmp, 0644, bonding_show_resend_igmp, bonding_sysfs_store_option); @@ -679,7 +694,7 @@ static ssize_t bonding_show_lp_interval(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.lp_interval); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.lp_interval)); } static DEVICE_ATTR(lp_interval, 0644, bonding_show_lp_interval, bonding_sysfs_store_option); @@ -690,7 +705,7 @@ static ssize_t bonding_show_tlb_dynamic_lb(struct device *d, { struct bonding *bond = to_bond(d); - return sysfs_emit(buf, "%d\n", bond->params.tlb_dynamic_lb); + return sysfs_emit(buf, "%d\n", READ_ONCE(bond->params.tlb_dynamic_lb)); } static DEVICE_ATTR(tlb_dynamic_lb, 0644, bonding_show_tlb_dynamic_lb, bonding_sysfs_store_option); @@ -700,7 +715,7 @@ static ssize_t bonding_show_packets_per_slave(struct device *d, char *buf) { struct bonding *bond = to_bond(d); - unsigned int packets_per_slave = bond->params.packets_per_slave; + unsigned int packets_per_slave = READ_ONCE(bond->params.packets_per_slave); return sysfs_emit(buf, "%u\n", packets_per_slave); } @@ -714,7 +729,7 @@ static ssize_t bonding_show_ad_actor_sys_prio(struct device *d, struct bonding *bond = to_bond(d); if (BOND_MODE(bond) == BOND_MODE_8023AD && capable(CAP_NET_ADMIN)) - return sysfs_emit(buf, "%hu\n", bond->params.ad_actor_sys_prio); + return sysfs_emit(buf, "%hu\n", READ_ONCE(bond->params.ad_actor_sys_prio)); return 0; } @@ -743,7 +758,8 @@ static ssize_t bonding_show_ad_user_port_key(struct device *d, struct bonding *bond = to_bond(d); if (BOND_MODE(bond) == BOND_MODE_8023AD && capable(CAP_NET_ADMIN)) - return sysfs_emit(buf, "%hu\n", bond->params.ad_user_port_key); + return sysfs_emit(buf, "%hu\n", + READ_ONCE(bond->params.ad_user_port_key)); return 0; } diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c index 08183833c9bc..a03553b80a5d 100644 --- a/drivers/net/can/sja1000/plx_pci.c +++ b/drivers/net/can/sja1000/plx_pci.c @@ -272,124 +272,89 @@ static struct plx_pci_card_info plx_pci_card_info_asem_dual_can = { static const struct pci_device_id plx_pci_tbl[] = { { /* Adlink PCI-7841/cPCI-7841 */ - ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_NETWORK_OTHER << 8, ~0, - (kernel_ulong_t)&plx_pci_card_info_adlink - }, - { + PCI_DEVICE(ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID), + .class = PCI_CLASS_NETWORK_OTHER << 8, + .class_mask = ~0, + .driver_data = (kernel_ulong_t)&plx_pci_card_info_adlink, + }, { /* Adlink PCI-7841/cPCI-7841 SE */ - ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_OTHER << 8, ~0, - (kernel_ulong_t)&plx_pci_card_info_adlink_se - }, - { + PCI_DEVICE(ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID), + .class = PCI_CLASS_COMMUNICATION_OTHER << 8, + .class_mask = ~0, + .driver_data = (kernel_ulong_t)&plx_pci_card_info_adlink_se, + }, { /* esd CAN-PCI/200 */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI200, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_esd200 - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI200), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd200, + }, { /* esd CAN-CPCI/200 */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_CPCI200, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_esd200 - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_CPCI200), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd200, + }, { /* esd CAN-PCI104/200 */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI104200, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_esd200 - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI104200), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd200, + }, { /* esd CAN-PCI/266 */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056, - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI266, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_esd266 - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9056, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI266), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd266, + }, { /* esd CAN-PMC/266 */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056, - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PMC266, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_esd266 - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9056, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PMC266), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd266, + }, { /* esd CAN-PCIE/2000 */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056, - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCIE2000, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_esd2000 - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9056, + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCIE2000), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd2000, + }, { /* IXXAT PC-I 04/PCI card */ - IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, - PCI_ANY_ID, IXXAT_PCI_SUB_SYS_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_ixxat - }, - { + PCI_DEVICE_SUB(IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, + PCI_ANY_ID, IXXAT_PCI_SUB_SYS_ID), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_ixxat, + }, { /* Marathon CAN-bus-PCI card */ - PCI_VENDOR_ID_PLX, MARATHON_PCI_DEVICE_ID, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_marathon_pci - }, - { + PCI_VDEVICE(PLX, MARATHON_PCI_DEVICE_ID), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_marathon_pci, + }, { /* Marathon CAN-bus-PCIe card */ - PCI_VENDOR_ID_PLX, MARATHON_PCIE_DEVICE_ID, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_marathon_pcie - }, - { + PCI_VDEVICE(PLX, MARATHON_PCIE_DEVICE_ID), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_marathon_pcie, + }, { /* TEWS TECHNOLOGIES TPMC810 card */ - TEWS_PCI_VENDOR_ID, TEWS_PCI_DEVICE_ID_TMPC810, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_tews - }, - { + PCI_DEVICE(TEWS_PCI_VENDOR_ID, TEWS_PCI_DEVICE_ID_TMPC810), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_tews, + }, { /* Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card */ - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_CONNECT_TECH, CTI_PCI_DEVICE_ID_CRG001, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_cti - }, - { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_CONNECT_TECH, CTI_PCI_DEVICE_ID_CRG001), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_cti, + }, { /* Elcus CAN-200-PCI */ - CAN200PCI_VENDOR_ID, CAN200PCI_DEVICE_ID, - CAN200PCI_SUB_VENDOR_ID, CAN200PCI_SUB_DEVICE_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_elcus - }, - { + PCI_DEVICE_SUB(CAN200PCI_VENDOR_ID, CAN200PCI_DEVICE_ID, + CAN200PCI_SUB_VENDOR_ID, CAN200PCI_SUB_DEVICE_ID), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_elcus, + }, { /* moxa */ - MOXA_PCI_VENDOR_ID, MOXA_PCI_DEVICE_ID, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_moxa - }, - { + PCI_DEVICE(MOXA_PCI_VENDOR_ID, MOXA_PCI_DEVICE_ID), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_moxa, + }, { /* ASEM Dual CAN raw */ - ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID, - ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_asem_dual_can - }, - { + PCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID, + ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_asem_dual_can, + }, { /* ASEM Dual CAN raw -new model */ - ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID, - ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS, - 0, 0, - (kernel_ulong_t)&plx_pci_card_info_asem_dual_can + PCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID, + ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS), + .driver_data = (kernel_ulong_t)&plx_pci_card_info_asem_dual_can, }, - { 0,} + { } }; MODULE_DEVICE_TABLE(pci, plx_pci_tbl); diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 39fb8ead16b5..4ab567c5bbaf 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -76,6 +76,8 @@ source "drivers/net/dsa/mv88e6xxx/Kconfig" source "drivers/net/dsa/mxl862xx/Kconfig" +source "drivers/net/dsa/netc/Kconfig" + source "drivers/net/dsa/ocelot/Kconfig" source "drivers/net/dsa/qca/Kconfig" diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index f5a463b87ec2..d2975badffc0 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -21,6 +21,7 @@ obj-y += lantiq/ obj-y += microchip/ obj-y += mv88e6xxx/ obj-y += mxl862xx/ +obj-y += netc/ obj-y += ocelot/ obj-y += qca/ obj-y += realtek/ diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 2c4131ed7e30..3f5b9592794d 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -965,17 +965,17 @@ EXPORT_SYMBOL(b53_configure_vlan); static void b53_switch_reset_gpio(struct b53_device *dev) { - int gpio = dev->reset_gpio; + struct gpio_desc *gpio = dev->reset_gpio; - if (gpio < 0) + if (IS_ERR(gpio)) return; /* Reset sequence: RESET low(50ms)->high(20ms) */ - gpio_set_value(gpio, 0); + gpiod_set_value(gpio, 0); mdelay(50); - gpio_set_value(gpio, 1); + gpiod_set_value(gpio, 1); mdelay(20); dev->current_page = 0xff; @@ -1225,6 +1225,7 @@ int b53_get_sset_count(struct dsa_switch *ds, int port, int sset) EXPORT_SYMBOL(b53_get_sset_count); enum b53_devlink_resource_id { + B53_DEVLINK_PARAM_ID_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */ B53_DEVLINK_PARAM_ID_VLAN_TABLE, }; @@ -3092,7 +3093,6 @@ static int b53_switch_init(struct b53_device *dev) { u32 chip_id = dev->chip_id; unsigned int i; - int ret; if (is63xx(dev)) chip_id = BCM63XX_DEVICE_ID; @@ -3173,12 +3173,9 @@ static int b53_switch_init(struct b53_device *dev) return -ENOMEM; dev->reset_gpio = b53_switch_get_reset_gpio(dev); - if (dev->reset_gpio >= 0) { - ret = devm_gpio_request_one(dev->dev, dev->reset_gpio, - GPIOF_OUT_INIT_HIGH, "robo_reset"); - if (ret) - return ret; - } + + if (PTR_ERR(dev->reset_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; return 0; } diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index bd6849e5bb93..cd27a7344e89 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -148,7 +148,7 @@ struct b53_device { u8 duplex_reg; u8 jumbo_pm_reg; u8 jumbo_size_reg; - int reset_gpio; + struct gpio_desc *reset_gpio; u8 num_arl_bins; u16 num_arl_buckets; enum dsa_tag_protocol tag_protocol; @@ -467,23 +467,36 @@ static inline void b53_arl_search_read(struct b53_device *dev, u8 idx, #ifdef CONFIG_BCM47XX #include +#include #include -static inline int b53_switch_get_reset_gpio(struct b53_device *dev) +static inline struct gpio_desc *b53_switch_get_reset_gpio(struct b53_device *dev) { enum bcm47xx_board board = bcm47xx_board_get(); + int gpio, ret; switch (board) { case BCM47XX_BOARD_LINKSYS_WRT300NV11: case BCM47XX_BOARD_LINKSYS_WRT310NV1: - return 8; + gpio = 8; + break; default: - return bcm47xx_nvram_gpio_pin("robo_reset"); + gpio = bcm47xx_nvram_gpio_pin("robo_reset"); } + + if (!gpio_is_valid(gpio)) + return ERR_PTR(-EINVAL); + + ret = devm_gpio_request_one(dev->dev, gpio, + GPIOF_OUT_INIT_HIGH, "robo_reset"); + if (ret) + return ERR_PTR(ret); + + return gpio_to_desc(gpio); } #else -static inline int b53_switch_get_reset_gpio(struct b53_device *dev) +static inline struct gpio_desc *b53_switch_get_reset_gpio(struct b53_device *dev) { - return -ENOENT; + return ERR_PTR(-ENODEV); } #endif diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index 7058faf23592..0e35e9841b9b 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -75,6 +75,7 @@ static struct phy_device *phydevs[PHY_MAX_ADDR]; static struct mdio_device *switch_mdiodev; enum dsa_loop_devlink_resource_id { + DSA_LOOP_DEVLINK_PARAM_ID_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */ DSA_LOOP_DEVLINK_PARAM_ID_VTU, }; diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index b369c1cc89e8..2c6cabf61143 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -1932,29 +1932,25 @@ static const struct dsa_switch_ops hellcreek_ds_ops = { static int hellcreek_probe(struct platform_device *pdev) { + const struct hellcreek_platform_data *data; struct device *dev = &pdev->dev; struct hellcreek *hellcreek; struct resource *res; int ret, i; - hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL); + data = of_device_get_match_data(dev); + hellcreek = devm_kzalloc(dev, struct_size(hellcreek, ports, data->num_ports), GFP_KERNEL); if (!hellcreek) return -ENOMEM; + hellcreek->pdata = data; + hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID, sizeof(*hellcreek->vidmbrcfg), GFP_KERNEL); if (!hellcreek->vidmbrcfg) return -ENOMEM; - hellcreek->pdata = of_device_get_match_data(dev); - - hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports, - sizeof(*hellcreek->ports), - GFP_KERNEL); - if (!hellcreek->ports) - return -ENOMEM; - for (i = 0; i < hellcreek->pdata->num_ports; ++i) { struct hellcreek_port *port = &hellcreek->ports[i]; diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h index bebf0d3ff330..b8b59f3b10e1 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.h +++ b/drivers/net/dsa/hirschmann/hellcreek.h @@ -280,7 +280,6 @@ struct hellcreek { struct dsa_switch *ds; struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; - struct hellcreek_port *ports; struct delayed_work overflow_work; struct led_classdev led_is_gm; struct led_classdev led_sync_good; @@ -297,6 +296,7 @@ struct hellcreek { u64 last_ts; /* Used for overflow detection */ u16 status_out; /* ptp.status_out shadow */ size_t fdb_entries; + struct hellcreek_port ports[]; }; /* A Qbv schedule can only started up to 8 seconds in the future. If the delta @@ -311,6 +311,7 @@ struct hellcreek { /* Devlink resources */ enum hellcreek_devlink_resource_id { + HELLCREEK_DEVLINK_PARAM_ID_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */ HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE, HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE, }; diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index c62d27cdc117..3a09f3907f44 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -89,7 +89,7 @@ static void lan9303_i2c_shutdown(struct i2c_client *client) /*-------------------------------------------------------------------------*/ static const struct i2c_device_id lan9303_i2c_id[] = { - { "lan9303" }, + { .name = "lan9303" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, lan9303_i2c_id); diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index c354abdafc1b..138f2ab0774e 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include "ksz_common.h" +#include "ksz_dcb.h" #include "ksz8_reg.h" #include "ksz8.h" @@ -114,26 +116,26 @@ static int ksz8_ind_read8(struct ksz_device *dev, u8 table, u16 addr, u8 *val) return ret; } -int ksz8_pme_write8(struct ksz_device *dev, u32 reg, u8 value) +static int ksz8_pme_write8(struct ksz_device *dev, u32 reg, u8 value) { return ksz8_ind_write8(dev, (u8)(reg >> 8), (u8)(reg), value); } -int ksz8_pme_pread8(struct ksz_device *dev, int port, int offset, u8 *data) +static int ksz8_pme_pread8(struct ksz_device *dev, int port, int offset, u8 *data) { u8 table = (u8)(offset >> 8 | (port + 1)); return ksz8_ind_read8(dev, table, (u8)(offset), data); } -int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 data) +static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 data) { u8 table = (u8)(offset >> 8 | (port + 1)); return ksz8_ind_write8(dev, table, (u8)(offset), data); } -int ksz8_reset_switch(struct ksz_device *dev) +static int ksz8_reset_switch(struct ksz_device *dev) { if (ksz_is_ksz88x3(dev)) { /* reset switch */ @@ -186,8 +188,9 @@ static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size) return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2); } -int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu) +static int ksz8_change_mtu(struct dsa_switch *ds, int port, int mtu) { + struct ksz_device *dev = ds->priv; u16 frame_size; if (!dsa_is_cpu_port(dev->ds, port)) @@ -267,22 +270,7 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) return ksz_prmw8(dev, port, reg_2q, mask_2q, data_2q); } -int ksz8_all_queues_split(struct ksz_device *dev, int queues) -{ - struct dsa_switch *ds = dev->ds; - const struct dsa_port *dp; - - dsa_switch_for_each_port(dp, ds) { - int ret = ksz8_port_queue_split(dev, dp->index, queues); - - if (ret) - return ret; - } - - return 0; -} - -void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) +static void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) { const u32 *masks; const u16 *regs; @@ -405,8 +393,8 @@ static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, } } -void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, - u64 *dropped, u64 *cnt) +static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt) { if (is_ksz88xx(dev)) ksz8863_r_mib_pkt(dev, port, addr, dropped, cnt); @@ -414,7 +402,7 @@ void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, ksz8795_r_mib_pkt(dev, port, addr, dropped, cnt); } -void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) +static void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) { if (is_ksz88xx(dev)) return; @@ -429,7 +417,7 @@ void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false); } -void ksz8_port_init_cnt(struct ksz_device *dev, int port) +static void ksz8_port_init_cnt(struct ksz_device *dev, int port) { struct ksz_port_mib *mib = &dev->ports[port].mib; u64 *dropped; @@ -810,6 +798,40 @@ static int ksz879x_set_loopback(struct ksz_device *dev, u16 port, u16 val) stat3); } +static int ksz87xx_apply_low_loss_preset(struct ksz_device *dev, bool enable) +{ + /* Apply the Microchip erratum short-cable preset (LPF 62 MHz, EQ init 0) + * providing a conservative configuration for short or low-loss cables. + */ + u8 lpf_bw, eq_init; + int ret; + + lpf_bw = KSZ87XX_PHY_LPF_62MHZ; + eq_init = KSZ87XX_DSP_EQ_INIT_LOW_LOSS; + + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + + if (!enable) { + /* Restore default values (LPF 90 MHz, EQ init 15). */ + lpf_bw = KSZ87XX_PHY_LPF_90MHZ; + eq_init = KSZ87XX_DSP_EQ_INIT_FACTORY; + } + + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF, lpf_bw); + if (ret) + return ret; + + dev->lpf_bw = lpf_bw; + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_DSP_EQ, eq_init); + if (ret) + return ret; + + dev->eq_init = eq_init; + + return ret; +} + /** * ksz8_r_phy_ctrl - Translates and reads from the SMI interface to a MIIM PHY * Control register (Reg. 31). @@ -954,7 +976,7 @@ static int ksz8_r_phy_bmcr(struct ksz_device *dev, u16 port, u16 *val) return 0; } -int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) +static int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) { u8 ctrl, link, val1, val2; int processed = true; @@ -1058,6 +1080,22 @@ int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) if (ret) return ret; + break; + case PHY_REG_KSZ87XX_SHORT_CABLE: + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + data = !!(dev->lpf_bw == KSZ87XX_PHY_LPF_62MHZ && + dev->eq_init == KSZ87XX_DSP_EQ_INIT_LOW_LOSS); + break; + case PHY_REG_KSZ87XX_LPF_BW: + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + data = dev->lpf_bw; + break; + case PHY_REG_KSZ87XX_EQ_INIT: + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + data = dev->eq_init; break; default: processed = false; @@ -1069,6 +1107,19 @@ int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) return 0; } +static int ksz8_phy_read16(struct dsa_switch *ds, int addr, int reg) +{ + struct ksz_device *dev = ds->priv; + u16 val = 0xffff; + int ret; + + ret = ksz8_r_phy(dev, addr, reg, &val); + if (ret) + return ret; + + return val; +} + /** * ksz8_w_phy_ctrl - Translates and writes to the SMI interface from a MIIM PHY * Control register (Reg. 31). @@ -1218,7 +1269,7 @@ static int ksz8_w_phy_bmcr(struct ksz_device *dev, u16 port, u16 val) restart); } -int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) +static int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) { const u16 *regs; u8 ctrl, data; @@ -1271,6 +1322,41 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) if (ret) return ret; break; + case PHY_REG_KSZ87XX_SHORT_CABLE: + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + dev_info_once(dev->dev, + "KSZ87xx low-loss tuning is global, applied switch-wide\n"); + ret = ksz87xx_apply_low_loss_preset(dev, !!val); + if (ret) + return ret; + break; + case PHY_REG_KSZ87XX_LPF_BW: + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + dev_info_once(dev->dev, + "KSZ87xx low-loss tuning is global, applied switch-wide\n"); + /* Only accept LPF bandwidth bits [7:6] */ + if (val & ~KSZ87XX_PHY_LPF_MASK) + return -EINVAL; + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF, (u8)val); + if (ret) + return ret; + dev->lpf_bw = val; + break; + case PHY_REG_KSZ87XX_EQ_INIT: + if (!ksz_is_ksz87xx(dev)) + return -EOPNOTSUPP; + dev_info_once(dev->dev, + "KSZ87xx low-loss tuning is global, applied switch-wide\n"); + /* Only accept DSP EQ initial value bits [5:0] */ + if (val & ~KSZ87XX_DSP_EQ_VALID_MASK) + return -EINVAL; + ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_DSP_EQ, (u8)val); + if (ret) + return ret; + dev->eq_init = val; + break; default: break; } @@ -1278,7 +1364,19 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) return 0; } -void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) +static int ksz8_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) +{ + struct ksz_device *dev = ds->priv; + int ret; + + ret = ksz8_w_phy(dev, addr, reg, val); + if (ret) + return ret; + + return 0; +} + +static void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) { int offset = P_MIRROR_CTRL; u8 data; @@ -1291,8 +1389,9 @@ void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) ksz_pwrite8(dev, port, offset, data); } -void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) +static void ksz8_flush_dyn_mac_table(struct dsa_switch *ds, int port) { + struct ksz_device *dev = ds->priv; u8 learn[DSA_MAX_PORTS]; int first, index, cnt; const u16 *regs; @@ -1326,9 +1425,10 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) } } -int ksz8_fdb_dump(struct ksz_device *dev, int port, - dsa_fdb_dump_cb_t *cb, void *data) +static int ksz8_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) { + struct ksz_device *dev = ds->priv; u8 mac[ETH_ALEN]; u8 src_port, fid; u16 entries = 0; @@ -1431,34 +1531,38 @@ static int ksz8_del_sta_mac(struct ksz_device *dev, int port, return ksz8_w_sta_mac_table(dev, index, &alu); } -int ksz8_mdb_add(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +static int ksz8_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { - return ksz8_add_sta_mac(dev, port, mdb->addr, mdb->vid); + return ksz8_add_sta_mac(ds->priv, port, mdb->addr, mdb->vid); } -int ksz8_mdb_del(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +static int ksz8_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { - return ksz8_del_sta_mac(dev, port, mdb->addr, mdb->vid); + return ksz8_del_sta_mac(ds->priv, port, mdb->addr, mdb->vid); } -int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, - u16 vid, struct dsa_db db) +static int ksz8_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) { - return ksz8_add_sta_mac(dev, port, addr, vid); + return ksz8_add_sta_mac(ds->priv, port, addr, vid); } -int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, - u16 vid, struct dsa_db db) +static int ksz8_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) { - return ksz8_del_sta_mac(dev, port, addr, vid); + return ksz8_del_sta_mac(ds->priv, port, addr, vid); } -int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, - struct netlink_ext_ack *extack) +static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, + struct netlink_ext_ack *extack) { - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) + struct ksz_device *dev = ds->priv; + + if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; /* Discard packets with VID not enabled on the switch */ @@ -1485,16 +1589,17 @@ static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state) } } -int ksz8_port_vlan_add(struct ksz_device *dev, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) +static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + struct ksz_device *dev = ds->priv; struct ksz_port *p = &dev->ports[port]; u16 data, new_pvid = 0; u8 fid, member, valid; - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) + if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; /* If a VLAN is added with untagged flag different from the @@ -1557,13 +1662,14 @@ int ksz8_port_vlan_add(struct ksz_device *dev, int port, return 0; } -int ksz8_port_vlan_del(struct ksz_device *dev, int port, - const struct switchdev_obj_port_vlan *vlan) +static int ksz8_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { - u16 data, pvid; + struct ksz_device *dev = ds->priv; u8 fid, member, valid; + u16 data, pvid; - if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) + if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid); @@ -1589,10 +1695,11 @@ int ksz8_port_vlan_del(struct ksz_device *dev, int port, return 0; } -int ksz8_port_mirror_add(struct ksz_device *dev, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress, struct netlink_ext_ack *extack) +static int ksz8_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) { + struct ksz_device *dev = ds->priv; int offset = P_MIRROR_CTRL; if (ksz_is_ksz8463(dev)) @@ -1615,9 +1722,10 @@ int ksz8_port_mirror_add(struct ksz_device *dev, int port, return 0; } -void ksz8_port_mirror_del(struct ksz_device *dev, int port, - struct dsa_mall_mirror_tc_entry *mirror) +static void ksz8_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) { + struct ksz_device *dev = ds->priv; int offset = P_MIRROR_CTRL; u8 data; @@ -1654,7 +1762,7 @@ static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) } } -void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) +static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) { const u16 *regs = dev->info->regs; struct dsa_switch *ds = dev->ds; @@ -1694,6 +1802,17 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz8_pme_pwrite8(dev, port, regs[REG_PORT_PME_CTRL], 0); } +static int ksz8_dsa_port_setup(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (!dsa_is_user_port(ds, port)) + return 0; + + ksz8_port_setup(dev, port, false); + return ksz_dcb_init_port(dev, port); +} + static void ksz88x3_config_rmii_clk(struct ksz_device *dev) { struct dsa_port *cpu_dp = dsa_to_port(dev->ds, dev->cpu_port); @@ -1709,7 +1828,7 @@ static void ksz88x3_config_rmii_clk(struct ksz_device *dev) KSZ88X3_PORT3_RMII_CLK_INTERNAL, rmii_clk_internal); } -void ksz8_config_cpu_port(struct dsa_switch *ds) +static void ksz8_config_cpu_port(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; struct ksz_port *p; @@ -1883,10 +2002,12 @@ static void ksz8_cpu_port_link_up(struct ksz_device *dev, int speed, int duplex, SW_10_MBIT, ctrl); } -void ksz8_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, unsigned int mode, - phy_interface_t interface, int speed, int duplex, - bool tx_pause, bool rx_pause) +static void ksz8_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct dsa_port *dp = dsa_phylink_to_port(config); struct ksz_device *dev = dp->ds->priv; @@ -1918,7 +2039,7 @@ static int ksz8_handle_global_errata(struct dsa_switch *ds) return ret; } -int ksz8_enable_stp_addr(struct ksz_device *dev) +static int ksz8_enable_stp_addr(struct ksz_device *dev) { struct alu_struct alu; @@ -1932,12 +2053,55 @@ int ksz8_enable_stp_addr(struct ksz_device *dev) return ksz8_w_sta_mac_table(dev, 0, &alu); } -int ksz8_setup(struct dsa_switch *ds) +static int ksz8_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; - const u16 *regs = dev->info->regs; - int i, ret = 0; + u16 storm_mask, storm_rate; + struct dsa_port *dp; + struct ksz_port *p; + const u16 *regs; + int i, ret; + regs = dev->info->regs; + + dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), + dev->info->num_vlans, GFP_KERNEL); + if (!dev->vlan_cache) + return -ENOMEM; + + ret = ksz8_reset_switch(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); + return ret; + } + + ret = ksz_parse_drive_strength(dev); + if (ret) + return ret; + + /* set broadcast storm protection 10% rate */ + storm_mask = BROADCAST_STORM_RATE; + storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100; + if (ksz_is_ksz8463(dev)) { + storm_mask = swab16(storm_mask); + storm_rate = swab16(storm_rate); + } + regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], + storm_mask, storm_rate); + + ksz8_config_cpu_port(ds); + + ksz8_enable_stp_addr(dev); + + ds->num_tx_queues = dev->info->num_tx_queues; + + regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL], + MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE); + + ksz_init_mib_timer(dev); + + ds->configure_vlan_while_not_filtering = false; + ds->dscp_prio_mapping_is_global = true; ds->mtu_enforcement_ingress = true; /* We rely on software untagging on the CPU port, so that we @@ -1987,17 +2151,91 @@ int ksz8_setup(struct dsa_switch *ds) ret = ksz8_pme_write8(dev, regs[REG_SW_PME_CTRL], 0); if (!ret) ret = ksz_rmw8(dev, REG_INT_ENABLE, INT_PME, 0); + if (ret) + return ret; } - if (!ret) - return ksz8_handle_global_errata(ds); - else + /* Initialize KSZ87xx short-cable preset control */ + dev->eq_init = KSZ87XX_DSP_EQ_INIT_FACTORY; + dev->lpf_bw = KSZ87XX_PHY_LPF_90MHZ; + + ret = ksz8_handle_global_errata(ds); + if (ret) return ret; + + /* Start with learning disabled on standalone user ports, and enabled + * on the CPU port. In lack of other finer mechanisms, learning on the + * CPU port will avoid flooding bridge local addresses on the network + * in some cases. + */ + p = &dev->ports[dev->cpu_port]; + p->learning = true; + + if (dev->irq > 0) { + ret = ksz_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = ksz_pirq_setup(dev, dp->index); + if (ret) + goto port_release; + + if (dev->info->ptp_capable) { + ret = ksz_ptp_irq_setup(ds, dp->index); + if (ret) + goto pirq_release; + } + } + } + + if (dev->info->ptp_capable) { + ret = ksz_ptp_clock_register(ds); + if (ret) { + dev_err(dev->dev, "Failed to register PTP clock: %d\n", + ret); + goto port_release; + } + } + + ret = ksz_mdio_register(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to register the mdio"); + goto out_ptp_clock_unregister; + } + + ret = ksz_dcb_init(dev); + if (ret) + goto out_ptp_clock_unregister; + + /* start switch */ + regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL], + SW_START, SW_START); + + return 0; + +out_ptp_clock_unregister: + if (dev->info->ptp_capable) + ksz_ptp_clock_unregister(ds); +port_release: + if (dev->irq > 0) { + dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) { + if (dev->info->ptp_capable) + ksz_ptp_irq_free(ds, dp->index); +pirq_release: + ksz_irq_free(&dev->ports[dp->index].pirq); + } + ksz_irq_free(&dev->girq); + } + + return ret; } -void ksz8_get_caps(struct ksz_device *dev, int port, - struct phylink_config *config) +static void ksz8_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { + struct ksz_device *dev = ds->priv; + config->mac_capabilities = MAC_10 | MAC_100; /* Silicon Errata Sheet (DS80000830A): @@ -2011,14 +2249,16 @@ void ksz8_get_caps(struct ksz_device *dev, int port, /* Asym pause is not supported on KSZ8863 and KSZ8873 */ if (!ksz_is_ksz88x3(dev)) config->mac_capabilities |= MAC_ASYM_PAUSE; + + ksz_phylink_get_caps(ds, port, config); } -u32 ksz8_get_port_addr(int port, int offset) +static u32 ksz8_get_port_addr(int port, int offset) { return PORT_CTRL_ADDR(port, offset); } -u32 ksz8463_get_port_addr(int port, int offset) +static u32 ksz8463_get_port_addr(int port, int offset) { return offset + 0x18 * port; } @@ -2028,7 +2268,7 @@ static u16 ksz8463_get_phy_addr(u16 phy, u16 reg, u16 offset) return offset + reg * 2 + phy * (P2MBCR - P1MBCR); } -int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) +static int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) { u16 sw_reg = 0; u16 data = 0; @@ -2068,7 +2308,20 @@ int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) return 0; } -int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) +static int ksz8463_phy_read16(struct dsa_switch *ds, int addr, int reg) +{ + struct ksz_device *dev = ds->priv; + u16 val = 0xffff; + int ret; + + ret = ksz8463_r_phy(dev, addr, reg, &val); + if (ret) + return ret; + + return val; +} + +static int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) { u16 sw_reg = 0; int ret; @@ -2096,7 +2349,37 @@ int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) return 0; } -int ksz8_switch_init(struct ksz_device *dev) +static int ksz8463_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) +{ + struct ksz_device *dev = ds->priv; + int ret; + + ret = ksz8463_w_phy(dev, addr, reg, val); + if (ret) + return ret; + + return 0; +} + +static u32 ksz88xx_get_phy_flags(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + switch (dev->chip_id) { + case KSZ88X3_CHIP_ID: + /* Silicon Errata Sheet (DS80000830A): + * Port 1 does not work with LinkMD Cable-Testing. + * Port 1 does not respond to received PAUSE control frames. + */ + if (!port) + return MICREL_KSZ8_P1_ERRATA; + break; + } + + return 0; +} + +static int ksz8_switch_init(struct ksz_device *dev) { dev->cpu_port = fls(dev->info->cpu_ports) - 1; dev->phy_port_cnt = dev->info->port_cnt - 1; @@ -2105,11 +2388,291 @@ int ksz8_switch_init(struct ksz_device *dev) return 0; } -void ksz8_switch_exit(struct ksz_device *dev) +static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) { - ksz8_reset_switch(dev); + return DSA_TAG_PROTO_KSZ9893; } +static int ksz8463_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct ksz_tagger_data *tagger_data; + + if (proto != DSA_TAG_PROTO_KSZ9893) + return -EPROTONOSUPPORT; + + tagger_data = ksz_tagger_data(ds); + tagger_data->xmit_work_fn = ksz_port_deferred_xmit; + + return 0; +} + +static enum dsa_tag_protocol ksz87xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) +{ + return DSA_TAG_PROTO_KSZ8795; +} + +static int ksz87xx_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + if (proto != DSA_TAG_PROTO_KSZ8795) + return -EPROTONOSUPPORT; + + return 0; +} + +static enum dsa_tag_protocol ksz88xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) +{ + struct ksz_device *dev = ds->priv; + + if (ksz_is_8895_family(dev)) /* KSZ8864, KSZ8895 */ + return DSA_TAG_PROTO_KSZ8795; + + return DSA_TAG_PROTO_KSZ9893; +} + +static int ksz88xx_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct ksz_tagger_data *tagger_data; + + if (ksz_is_8895_family(ds->priv)) { /* KSZ8864, KSZ8895 */ + if (proto != DSA_TAG_PROTO_KSZ8795) + return -EPROTONOSUPPORT; + + return 0; + } + + if (proto != DSA_TAG_PROTO_KSZ9893) + return -EPROTONOSUPPORT; + + tagger_data = ksz_tagger_data(ds); + tagger_data->xmit_work_fn = ksz_port_deferred_xmit; + + return 0; +} + +static void ksz88x3_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + + dev->ports[dp->index].manual_flow = !(state->pause & MLO_PAUSE_AN); +} + +const struct phylink_mac_ops ksz88x3_phylink_mac_ops = { + .mac_config = ksz88x3_phylink_mac_config, + .mac_link_down = ksz_phylink_mac_link_down, + .mac_link_up = ksz8_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, +}; + +const struct phylink_mac_ops ksz8_phylink_mac_ops = { + .mac_config = ksz_phylink_mac_config, + .mac_link_down = ksz_phylink_mac_link_down, + .mac_link_up = ksz8_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, +}; + +const struct ksz_dev_ops ksz8463_dev_ops = { + .get_port_addr = ksz8463_get_port_addr, + .cfg_port_member = ksz8_cfg_port_member, + .r_mib_cnt = ksz8_r_mib_cnt, + .r_mib_pkt = ksz8_r_mib_pkt, + .r_mib_stat64 = ksz88xx_r_mib_stats64, + .freeze_mib = ksz8_freeze_mib, + .port_init_cnt = ksz8_port_init_cnt, + .init = ksz8_switch_init, +}; + +const struct ksz_dev_ops ksz87xx_dev_ops = { + .get_port_addr = ksz8_get_port_addr, + .cfg_port_member = ksz8_cfg_port_member, + .r_mib_cnt = ksz8_r_mib_cnt, + .r_mib_pkt = ksz8_r_mib_pkt, + .r_mib_stat64 = ksz_r_mib_stats64, + .freeze_mib = ksz8_freeze_mib, + .port_init_cnt = ksz8_port_init_cnt, + .init = ksz8_switch_init, + .pme_write8 = ksz8_pme_write8, + .pme_pread8 = ksz8_pme_pread8, + .pme_pwrite8 = ksz8_pme_pwrite8, +}; + +const struct ksz_dev_ops ksz88xx_dev_ops = { + .get_port_addr = ksz8_get_port_addr, + .cfg_port_member = ksz8_cfg_port_member, + .r_mib_cnt = ksz8_r_mib_cnt, + .r_mib_pkt = ksz8_r_mib_pkt, + .r_mib_stat64 = ksz88xx_r_mib_stats64, + .freeze_mib = ksz8_freeze_mib, + .port_init_cnt = ksz8_port_init_cnt, + .init = ksz8_switch_init, + .pme_write8 = ksz8_pme_write8, + .pme_pread8 = ksz8_pme_pread8, + .pme_pwrite8 = ksz8_pme_pwrite8, +}; + +const struct dsa_switch_ops ksz8463_switch_ops = { + .get_tag_protocol = ksz8463_get_tag_protocol, + .connect_tag_protocol = ksz8463_connect_tag_protocol, + .setup = ksz8_setup, + .teardown = ksz_teardown, + .phy_read = ksz8463_phy_read16, + .phy_write = ksz8463_phy_write16, + .phylink_get_caps = ksz8_phylink_get_caps, + .port_setup = ksz8_dsa_port_setup, + .get_strings = ksz_get_strings, + .get_ethtool_stats = ksz_get_ethtool_stats, + .get_sset_count = ksz_sset_count, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .port_set_mac_address = ksz_port_set_mac_address, + .port_stp_state_set = ksz_port_stp_state_set, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, + .port_fast_age = ksz8_flush_dyn_mac_table, + .port_fdb_dump = ksz8_fdb_dump, + .port_fdb_add = ksz8_fdb_add, + .port_fdb_del = ksz8_fdb_del, + .port_mdb_add = ksz8_mdb_add, + .port_mdb_del = ksz8_mdb_del, + .port_mirror_add = ksz8_port_mirror_add, + .port_mirror_del = ksz8_port_mirror_del, + .get_stats64 = ksz_get_stats64, + .get_pause_stats = ksz_get_pause_stats, + .port_change_mtu = ksz8_change_mtu, + .port_max_mtu = ksz_max_mtu, + .suspend = ksz_suspend, + .resume = ksz_resume, + .get_ts_info = ksz_get_ts_info, + .port_hwtstamp_get = ksz_hwtstamp_get, + .port_hwtstamp_set = ksz_hwtstamp_set, + .port_txtstamp = ksz_port_txtstamp, + .port_rxtstamp = ksz_port_rxtstamp, + .port_setup_tc = ksz_setup_tc, + .port_get_default_prio = ksz_port_get_default_prio, + .port_set_default_prio = ksz_port_set_default_prio, + .port_get_dscp_prio = ksz_port_get_dscp_prio, + .port_add_dscp_prio = ksz_port_add_dscp_prio, + .port_del_dscp_prio = ksz_port_del_dscp_prio, + .port_get_apptrust = ksz_port_get_apptrust, + .port_set_apptrust = ksz_port_set_apptrust, +}; + +const struct dsa_switch_ops ksz87xx_switch_ops = { + .get_tag_protocol = ksz87xx_get_tag_protocol, + .connect_tag_protocol = ksz87xx_connect_tag_protocol, + .setup = ksz8_setup, + .teardown = ksz_teardown, + .phy_read = ksz8_phy_read16, + .phy_write = ksz8_phy_write16, + .phylink_get_caps = ksz8_phylink_get_caps, + .port_setup = ksz8_dsa_port_setup, + .get_strings = ksz_get_strings, + .get_ethtool_stats = ksz_get_ethtool_stats, + .get_sset_count = ksz_sset_count, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .port_set_mac_address = ksz_port_set_mac_address, + .port_stp_state_set = ksz_port_stp_state_set, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, + .port_fast_age = ksz8_flush_dyn_mac_table, + .port_vlan_filtering = ksz8_port_vlan_filtering, + .port_vlan_add = ksz8_port_vlan_add, + .port_vlan_del = ksz8_port_vlan_del, + .port_fdb_dump = ksz8_fdb_dump, + .port_fdb_add = ksz8_fdb_add, + .port_fdb_del = ksz8_fdb_del, + .port_mdb_add = ksz8_mdb_add, + .port_mdb_del = ksz8_mdb_del, + .port_mirror_add = ksz8_port_mirror_add, + .port_mirror_del = ksz8_port_mirror_del, + .get_stats64 = ksz_get_stats64, + .get_pause_stats = ksz_get_pause_stats, + .port_change_mtu = ksz8_change_mtu, + .port_max_mtu = ksz_max_mtu, + .suspend = ksz_suspend, + .resume = ksz_resume, + .get_ts_info = ksz_get_ts_info, + .port_hwtstamp_get = ksz_hwtstamp_get, + .port_hwtstamp_set = ksz_hwtstamp_set, + .port_txtstamp = ksz_port_txtstamp, + .port_rxtstamp = ksz_port_rxtstamp, + .port_setup_tc = ksz_setup_tc, + .port_get_default_prio = ksz_port_get_default_prio, + .port_set_default_prio = ksz_port_set_default_prio, + .port_get_dscp_prio = ksz_port_get_dscp_prio, + .port_add_dscp_prio = ksz_port_add_dscp_prio, + .port_del_dscp_prio = ksz_port_del_dscp_prio, + .port_get_apptrust = ksz_port_get_apptrust, + .port_set_apptrust = ksz_port_set_apptrust, +}; + +const struct dsa_switch_ops ksz88xx_switch_ops = { + .get_tag_protocol = ksz88xx_get_tag_protocol, + .connect_tag_protocol = ksz88xx_connect_tag_protocol, + .get_phy_flags = ksz88xx_get_phy_flags, + .setup = ksz8_setup, + .teardown = ksz_teardown, + .phy_read = ksz8_phy_read16, + .phy_write = ksz8_phy_write16, + .phylink_get_caps = ksz8_phylink_get_caps, + .port_setup = ksz8_dsa_port_setup, + .get_strings = ksz_get_strings, + .get_ethtool_stats = ksz_get_ethtool_stats, + .get_sset_count = ksz_sset_count, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .port_set_mac_address = ksz_port_set_mac_address, + .port_stp_state_set = ksz_port_stp_state_set, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, + .port_fast_age = ksz8_flush_dyn_mac_table, + .port_vlan_filtering = ksz8_port_vlan_filtering, + .port_vlan_add = ksz8_port_vlan_add, + .port_vlan_del = ksz8_port_vlan_del, + .port_fdb_dump = ksz8_fdb_dump, + .port_fdb_add = ksz8_fdb_add, + .port_fdb_del = ksz8_fdb_del, + .port_mdb_add = ksz8_mdb_add, + .port_mdb_del = ksz8_mdb_del, + .port_mirror_add = ksz8_port_mirror_add, + .port_mirror_del = ksz8_port_mirror_del, + .get_stats64 = ksz_get_stats64, + .get_pause_stats = ksz_get_pause_stats, + .port_change_mtu = ksz8_change_mtu, + .port_max_mtu = ksz_max_mtu, + .get_wol = ksz_get_wol, + .set_wol = ksz_set_wol, + .suspend = ksz_suspend, + .resume = ksz_resume, + .get_ts_info = ksz_get_ts_info, + .port_hwtstamp_get = ksz_hwtstamp_get, + .port_hwtstamp_set = ksz_hwtstamp_set, + .port_txtstamp = ksz_port_txtstamp, + .port_rxtstamp = ksz_port_rxtstamp, + .port_setup_tc = ksz_setup_tc, + .port_get_default_prio = ksz_port_get_default_prio, + .port_set_default_prio = ksz_port_set_default_prio, + .port_get_dscp_prio = ksz_port_get_dscp_prio, + .port_add_dscp_prio = ksz_port_add_dscp_prio, + .port_del_dscp_prio = ksz_port_del_dscp_prio, + .port_get_apptrust = ksz_port_get_apptrust, + .port_set_apptrust = ksz_port_set_apptrust, +}; + MODULE_AUTHOR("Tristram Ha "); MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index 0f2cd1474b44..bc371cc26c6f 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -12,59 +12,13 @@ #include #include "ksz_common.h" -int ksz8_setup(struct dsa_switch *ds); -u32 ksz8_get_port_addr(int port, int offset); -void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member); -void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port); -void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port); -int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); -int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val); -void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt); -void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, - u64 *dropped, u64 *cnt); -void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze); -void ksz8_port_init_cnt(struct ksz_device *dev, int port); -int ksz8_fdb_dump(struct ksz_device *dev, int port, - dsa_fdb_dump_cb_t *cb, void *data); -int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, - u16 vid, struct dsa_db db); -int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, - u16 vid, struct dsa_db db); -int ksz8_mdb_add(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); -int ksz8_mdb_del(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); -int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, - struct netlink_ext_ack *extack); -int ksz8_port_vlan_add(struct ksz_device *dev, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack); -int ksz8_port_vlan_del(struct ksz_device *dev, int port, - const struct switchdev_obj_port_vlan *vlan); -int ksz8_port_mirror_add(struct ksz_device *dev, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress, struct netlink_ext_ack *extack); -void ksz8_port_mirror_del(struct ksz_device *dev, int port, - struct dsa_mall_mirror_tc_entry *mirror); -void ksz8_get_caps(struct ksz_device *dev, int port, - struct phylink_config *config); -void ksz8_config_cpu_port(struct dsa_switch *ds); -int ksz8_enable_stp_addr(struct ksz_device *dev); -int ksz8_reset_switch(struct ksz_device *dev); -int ksz8_switch_init(struct ksz_device *dev); -void ksz8_switch_exit(struct ksz_device *dev); -int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu); -int ksz8_pme_write8(struct ksz_device *dev, u32 reg, u8 value); -int ksz8_pme_pread8(struct ksz_device *dev, int port, int offset, u8 *data); -int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 data); -void ksz8_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, unsigned int mode, - phy_interface_t interface, int speed, int duplex, - bool tx_pause, bool rx_pause); -int ksz8_all_queues_split(struct ksz_device *dev, int queues); - -u32 ksz8463_get_port_addr(int port, int offset); -int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); -int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val); +extern const struct ksz_dev_ops ksz8463_dev_ops; +extern const struct ksz_dev_ops ksz87xx_dev_ops; +extern const struct ksz_dev_ops ksz88xx_dev_ops; +extern const struct phylink_mac_ops ksz88x3_phylink_mac_ops; +extern const struct phylink_mac_ops ksz8_phylink_mac_ops; +extern const struct dsa_switch_ops ksz8463_switch_ops; +extern const struct dsa_switch_ops ksz87xx_switch_ops; +extern const struct dsa_switch_ops ksz88xx_switch_ops; #endif diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index a8bfcd917bf7..ba08d2cf8e99 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -140,14 +140,14 @@ static int ksz8863_smi_probe(struct mdio_device *mdiodev) int ret; int i; - dev = ksz_switch_alloc(&mdiodev->dev, mdiodev); - if (!dev) - return -ENOMEM; - chip = device_get_match_data(ddev); if (!chip) return -EINVAL; + dev = ksz_switch_alloc(&mdiodev->dev, chip, mdiodev); + if (!dev) + return -ENOMEM; + for (i = 0; i < __KSZ_NUM_REGMAPS; i++) { rc = ksz8863_regmap_config[i]; rc.lock_arg = &dev->regmap_mutex; diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h index 332408567b47..981ab441d9b7 100644 --- a/drivers/net/dsa/microchip/ksz8_reg.h +++ b/drivers/net/dsa/microchip/ksz8_reg.h @@ -202,6 +202,10 @@ #define REG_PORT_3_STATUS_0 0x38 #define REG_PORT_4_STATUS_0 0x48 +/* KSZ87xx LinkMD registers (TABLE_LINK_MD_V) */ +#define KSZ87XX_REG_DSP_EQ 0x08 /* DSP EQ initial value */ +#define KSZ87XX_REG_PHY_LPF 0x4C /* RX LPF bandwidth */ + /* For KSZ8765. */ #define PORT_REMOTE_ASYM_PAUSE BIT(5) #define PORT_REMOTE_SYM_PAUSE BIT(4) @@ -342,7 +346,7 @@ #define TABLE_EEE (TABLE_EEE_V << TABLE_EXT_SELECT_S) #define TABLE_ACL (TABLE_ACL_V << TABLE_EXT_SELECT_S) #define TABLE_PME (TABLE_PME_V << TABLE_EXT_SELECT_S) -#define TABLE_LINK_MD (TABLE_LINK_MD << TABLE_EXT_SELECT_S) +#define TABLE_LINK_MD (TABLE_LINK_MD_V << TABLE_EXT_SELECT_S) #define TABLE_READ BIT(4) #define TABLE_SELECT_S 2 #define TABLE_STATIC_MAC_V 0 @@ -729,6 +733,23 @@ #define PHY_POWER_SAVING_ENABLE BIT(2) #define PHY_REMOTE_LOOPBACK BIT(1) +/* Vendor-specific Clause 22 PHY registers (virtualized) */ +#define PHY_REG_KSZ87XX_SHORT_CABLE 0x1A +#define PHY_REG_KSZ87XX_LPF_BW 0x1B +#define PHY_REG_KSZ87XX_EQ_INIT 0x1C + +/* LPF bandwidth bits [7:6]: 00 = 90MHz (default), 01 = 62MHz, 10 = 55MHz, 11 = 44MHz */ +#define KSZ87XX_PHY_LPF_MASK GENMASK(7, 6) +#define KSZ87XX_PHY_LPF_90MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 0) +#define KSZ87XX_PHY_LPF_62MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 1) +#define KSZ87XX_PHY_LPF_55MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 2) +#define KSZ87XX_PHY_LPF_44MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 3) + +/* Low-loss workaround DSP EQ INIT VALUE */ +#define KSZ87XX_DSP_EQ_VALID_MASK GENMASK(5, 0) +#define KSZ87XX_DSP_EQ_INIT_LOW_LOSS 0x00 +#define KSZ87XX_DSP_EQ_INIT_FACTORY 0x0F + /* KSZ8463 specific registers. */ #define P1MBCR 0x4C #define P1MBSR 0x4E diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index d3c23dcaea8c..f3f0c98dfb5a 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -5,18 +5,21 @@ * Copyright (C) 2017-2025 Microchip Technology Inc. */ +#include #include #include #include #include #include #include +#include #include #include #include #include "ksz9477_reg.h" #include "ksz_common.h" +#include "ksz_dcb.h" #include "ksz9477.h" static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) @@ -43,8 +46,9 @@ static void ksz9477_port_cfg32(struct ksz_device *dev, int port, int offset, bits, set ? bits : 0); } -int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu) +static int ksz9477_change_mtu(struct dsa_switch *ds, int port, int mtu) { + struct ksz_device *dev = ds->priv; u16 frame_size; if (!dsa_is_cpu_port(dev->ds, port)) @@ -308,7 +312,7 @@ static int ksz9477_pcs_write(struct mii_bus *bus, int phy, int mmd, int reg, return 0; } -int ksz9477_pcs_create(struct ksz_device *dev) +static int ksz9477_pcs_create(struct ksz_device *dev) { int port = ksz_get_sgmii_port(dev); struct ksz_port *p = &dev->ports[port]; @@ -341,7 +345,7 @@ int ksz9477_pcs_create(struct ksz_device *dev) return 0; } -int ksz9477_reset_switch(struct ksz_device *dev) +static int ksz9477_reset_switch(struct ksz_device *dev) { u8 data8; u32 data32; @@ -516,7 +520,7 @@ static void ksz9477_r_phy_quirks(struct ksz_device *dev, u16 addr, u16 reg, *data &= ~(BMSR_ESTATEN | BMSR_ERCAP); } -int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data) +static int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data) { u16 val = 0xffff; int ret; @@ -572,7 +576,20 @@ int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data) return 0; } -int ksz9477_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val) +static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) +{ + struct ksz_device *dev = ds->priv; + u16 val = 0xffff; + int ret; + + ret = ksz9477_r_phy(dev, addr, reg, &val); + if (ret) + return ret; + + return val; +} + +static int ksz9477_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val) { u32 mask, val32; @@ -597,13 +614,26 @@ int ksz9477_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val) return ksz_prmw32(dev, addr, 0x100 + (reg << 1), mask, val32); } +static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) +{ + struct ksz_device *dev = ds->priv; + int ret; + + ret = ksz9477_w_phy(dev, addr, reg, val); + if (ret) + return ret; + + return 0; +} + void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member) { ksz_pwrite32(dev, port, REG_PORT_VLAN_MEMBERSHIP__4, member); } -void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) +void ksz9477_flush_dyn_mac_table(struct dsa_switch *ds, int port) { + struct ksz_device *dev = ds->priv; const u16 *regs = dev->info->regs; u8 data; @@ -625,9 +655,11 @@ void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) } } -int ksz9477_port_vlan_filtering(struct ksz_device *dev, int port, +int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, struct netlink_ext_ack *extack) { + struct ksz_device *dev = ds->priv; + if (flag) { ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, PORT_VLAN_LOOKUP_VID_0, true); @@ -641,12 +673,13 @@ int ksz9477_port_vlan_filtering(struct ksz_device *dev, int port, return 0; } -int ksz9477_port_vlan_add(struct ksz_device *dev, int port, +int ksz9477_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, struct netlink_ext_ack *extack) { - u32 vlan_table[3]; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + struct ksz_device *dev = ds->priv; + u32 vlan_table[3]; int err; err = ksz9477_get_vlan_table(dev, vlan->vid, vlan_table); @@ -677,10 +710,11 @@ int ksz9477_port_vlan_add(struct ksz_device *dev, int port, return 0; } -int ksz9477_port_vlan_del(struct ksz_device *dev, int port, +int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + struct ksz_device *dev = ds->priv; u32 vlan_table[3]; u16 pvid; @@ -710,9 +744,10 @@ int ksz9477_port_vlan_del(struct ksz_device *dev, int port, return 0; } -int ksz9477_fdb_add(struct ksz_device *dev, int port, +int ksz9477_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db) { + struct ksz_device *dev = ds->priv; u32 alu_table[4]; u32 data; int ret = 0; @@ -766,9 +801,10 @@ int ksz9477_fdb_add(struct ksz_device *dev, int port, return ret; } -int ksz9477_fdb_del(struct ksz_device *dev, int port, +int ksz9477_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db) { + struct ksz_device *dev = ds->priv; u32 alu_table[4]; u32 data; int ret = 0; @@ -855,13 +891,14 @@ static void ksz9477_convert_alu(struct alu_struct *alu, u32 *alu_table) alu->mac[5] = alu_table[3] & 0xFF; } -int ksz9477_fdb_dump(struct ksz_device *dev, int port, +int ksz9477_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { - int ret = 0; - u32 ksz_data; - u32 alu_table[4]; + struct ksz_device *dev = ds->priv; struct alu_struct alu; + u32 alu_table[4]; + u32 ksz_data; + int ret = 0; int timeout; mutex_lock(&dev->alu_mutex); @@ -909,9 +946,10 @@ int ksz9477_fdb_dump(struct ksz_device *dev, int port, return ret; } -int ksz9477_mdb_add(struct ksz_device *dev, int port, +int ksz9477_mdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { + struct ksz_device *dev = ds->priv; u32 static_table[4]; const u8 *shifts; const u32 *masks; @@ -988,16 +1026,17 @@ int ksz9477_mdb_add(struct ksz_device *dev, int port, return err; } -int ksz9477_mdb_del(struct ksz_device *dev, int port, +int ksz9477_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { + struct ksz_device *dev = ds->priv; u32 static_table[4]; + u32 mac_hi, mac_lo; const u8 *shifts; const u32 *masks; - u32 data; - int index; int ret = 0; - u32 mac_hi, mac_lo; + int index; + u32 data; shifts = dev->info->shifts; masks = dev->info->masks; @@ -1067,10 +1106,11 @@ int ksz9477_mdb_del(struct ksz_device *dev, int port, return ret; } -int ksz9477_port_mirror_add(struct ksz_device *dev, int port, +int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress, struct netlink_ext_ack *extack) { + struct ksz_device *dev = ds->priv; u8 data; int p; @@ -1106,9 +1146,10 @@ int ksz9477_port_mirror_add(struct ksz_device *dev, int port, return 0; } -void ksz9477_port_mirror_del(struct ksz_device *dev, int port, +void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror) { + struct ksz_device *dev = ds->priv; bool in_use = false; u8 data; int p; @@ -1150,9 +1191,11 @@ static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port) return interface; } -void ksz9477_get_caps(struct ksz_device *dev, int port, - struct phylink_config *config) +static void ksz9477_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { + struct ksz_device *dev = ds->priv; + config->mac_capabilities = MAC_10 | MAC_100 | MAC_ASYM_PAUSE | MAC_SYM_PAUSE; @@ -1166,10 +1209,13 @@ void ksz9477_get_caps(struct ksz_device *dev, int port, config->supported_interfaces, p->pcs->supported_interfaces); } + + ksz_phylink_get_caps(ds, port, config); } -int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs) +static int ksz9477_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) { + struct ksz_device *dev = ds->priv; u32 secs = msecs / 1000; u8 data, mult, value; u32 max_val; @@ -1234,7 +1280,7 @@ void ksz9477_port_queue_split(struct ksz_device *dev, int port) ksz_prmw8(dev, port, REG_PORT_CTRL_0, PORT_QUEUE_SPLIT_MASK, data); } -void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) +static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) { const u16 *regs = dev->info->regs; struct dsa_switch *ds = dev->ds; @@ -1289,7 +1335,24 @@ void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz_pwrite8(dev, port, regs[REG_PORT_PME_CTRL], 0); } -void ksz9477_config_cpu_port(struct dsa_switch *ds) +static int ksz9477_dsa_port_setup(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + int ret; + + if (!dsa_is_user_port(ds, port)) + return 0; + + ksz9477_port_setup(dev, port, false); + + ret = ksz9477_set_default_prio_queue_mapping(dev, port); + if (ret) + return ret; + + return ksz_dcb_init_port(dev, port); +} + +static void ksz9477_config_cpu_port(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; struct ksz_port *p; @@ -1454,12 +1517,57 @@ int ksz9477_enable_stp_addr(struct ksz_device *dev) return 0; } -int ksz9477_setup(struct dsa_switch *ds) +static int ksz9477_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; - const u16 *regs = dev->info->regs; - int ret = 0; + u16 storm_mask, storm_rate; + struct dsa_port *dp; + struct ksz_port *p; + const u16 *regs; + int ret; + regs = dev->info->regs; + + dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), + dev->info->num_vlans, GFP_KERNEL); + if (!dev->vlan_cache) + return -ENOMEM; + + ret = ksz9477_reset_switch(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); + return ret; + } + + ret = ksz_parse_drive_strength(dev); + if (ret) + return ret; + + if (ksz_has_sgmii_port(dev)) { + ret = ksz9477_pcs_create(dev); + if (ret) + return ret; + } + + /* set broadcast storm protection 10% rate */ + storm_mask = BROADCAST_STORM_RATE; + storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100; + regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], + storm_mask, storm_rate); + + ksz9477_config_cpu_port(ds); + + ksz9477_enable_stp_addr(dev); + + ds->num_tx_queues = dev->info->num_tx_queues; + + regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL], + MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE); + + ksz_init_mib_timer(dev); + + ds->configure_vlan_while_not_filtering = false; + ds->dscp_prio_mapping_is_global = true; ds->mtu_enforcement_ingress = true; /* Required for port partitioning. */ @@ -1492,7 +1600,76 @@ int ksz9477_setup(struct dsa_switch *ds) * be enabled by ksz_wol_pre_shutdown(). Otherwise, some PMICs * do not like PME events changes before shutdown. */ - return ksz_write8(dev, regs[REG_SW_PME_CTRL], 0); + ret = ksz_write8(dev, regs[REG_SW_PME_CTRL], 0); + if (ret < 0) + return ret; + + /* Start with learning disabled on standalone user ports, and enabled + * on the CPU port. In lack of other finer mechanisms, learning on the + * CPU port will avoid flooding bridge local addresses on the network + * in some cases. + */ + p = &dev->ports[dev->cpu_port]; + p->learning = true; + + if (dev->irq > 0) { + ret = ksz_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = ksz_pirq_setup(dev, dp->index); + if (ret) + goto port_release; + + if (dev->info->ptp_capable) { + ret = ksz_ptp_irq_setup(ds, dp->index); + if (ret) + goto pirq_release; + } + } + } + + if (dev->info->ptp_capable) { + ret = ksz_ptp_clock_register(ds); + if (ret) { + dev_err(dev->dev, "Failed to register PTP clock: %d\n", + ret); + goto port_release; + } + } + + ret = ksz_mdio_register(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to register the mdio"); + goto out_ptp_clock_unregister; + } + + ret = ksz_dcb_init(dev); + if (ret) + goto out_ptp_clock_unregister; + + /* start switch */ + regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL], + SW_START, SW_START); + + return 0; + +out_ptp_clock_unregister: + if (dev->info->ptp_capable) + ksz_ptp_clock_unregister(ds); +port_release: + if (dev->irq > 0) { + dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) { + if (dev->info->ptp_capable) + ksz_ptp_irq_free(ds, dp->index); +pirq_release: + ksz_irq_free(&dev->ports[dp->index].pirq); + } + ksz_irq_free(&dev->girq); + } + + return ret; } u32 ksz9477_get_port_addr(int port, int offset) @@ -1500,7 +1677,7 @@ u32 ksz9477_get_port_addr(int port, int offset) return PORT_CTRL_ADDR(port, offset); } -int ksz9477_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) +static int ksz9477_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) { val = val >> 8; @@ -1531,12 +1708,52 @@ int ksz9477_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) */ #define KSZ9477_SUPPORTED_HSR_FEATURES (NETIF_F_HW_HSR_DUP | NETIF_F_HW_HSR_FWD) -void ksz9477_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr) +static int ksz9477_hsr_join(struct dsa_switch *ds, int port, + struct net_device *hsr, + struct netlink_ext_ack *extack) { struct ksz_device *dev = ds->priv; struct net_device *user; struct dsa_port *hsr_dp; u8 data, hsr_ports = 0; + enum hsr_version ver; + int ret; + + ret = hsr_get_version(hsr, &ver); + if (ret) + return ret; + + if (dev->chip_id != KSZ9477_CHIP_ID) { + NL_SET_ERR_MSG_MOD(extack, "Chip does not support HSR offload"); + return -EOPNOTSUPP; + } + + /* KSZ9477 can support HW offloading of only 1 HSR device */ + if (dev->hsr_dev && hsr != dev->hsr_dev) { + NL_SET_ERR_MSG_MOD(extack, + "Offload supported for a single HSR"); + return -EOPNOTSUPP; + } + + /* KSZ9477 only supports HSR v0 and v1 */ + if (!(ver == HSR_V0 || ver == HSR_V1)) { + NL_SET_ERR_MSG_MOD(extack, "Only HSR v0 and v1 supported"); + return -EOPNOTSUPP; + } + + /* KSZ9477 can only perform HSR offloading for up to two ports */ + if (hweight8(dev->hsr_ports) >= 2) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot offload more than two ports - using software HSR"); + return -EOPNOTSUPP; + } + + /* Self MAC address filtering, to avoid frames traversing + * the HSR ring more than once. + */ + ret = ksz_switch_macaddr_get(ds, port, extack); + if (ret) + return ret; /* Program which port(s) shall support HSR */ ksz_rmw32(dev, REG_HSR_PORT_MAP__4, BIT(port), BIT(port)); @@ -1568,12 +1785,20 @@ void ksz9477_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr) /* Setup HW supported features for lan HSR ports */ user = dsa_to_port(ds, port)->user; user->features |= KSZ9477_SUPPORTED_HSR_FEATURES; + + dev->hsr_dev = hsr; + dev->hsr_ports |= BIT(port); + + return 0; } -void ksz9477_hsr_leave(struct dsa_switch *ds, int port, struct net_device *hsr) +static int ksz9477_hsr_leave(struct dsa_switch *ds, int port, + struct net_device *hsr) { struct ksz_device *dev = ds->priv; + WARN_ON(dev->chip_id != KSZ9477_CHIP_ID); + /* Clear port HSR support */ ksz_rmw32(dev, REG_HSR_PORT_MAP__4, BIT(port), 0); @@ -1582,9 +1807,17 @@ void ksz9477_hsr_leave(struct dsa_switch *ds, int port, struct net_device *hsr) /* Disable per port self-address filtering */ ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, PORT_SRC_ADDR_FILTER, false); + + dev->hsr_ports &= ~BIT(port); + if (!dev->hsr_ports) + dev->hsr_dev = NULL; + + ksz_switch_macaddr_put(ds); + + return 0; } -int ksz9477_switch_init(struct ksz_device *dev) +static int ksz9477_switch_init(struct ksz_device *dev) { u8 data8; int ret; @@ -1604,11 +1837,291 @@ int ksz9477_switch_init(struct ksz_device *dev) return 0; } -void ksz9477_switch_exit(struct ksz_device *dev) +static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) { - ksz9477_reset_switch(dev); + struct ksz_device *dev = ds->priv; + + if (dev->chip_id == KSZ8563_CHIP_ID || + dev->chip_id == KSZ9893_CHIP_ID || + dev->chip_id == KSZ9563_CHIP_ID) + return DSA_TAG_PROTO_KSZ9893; + + return DSA_TAG_PROTO_KSZ9477; } +static int ksz9477_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct ksz_tagger_data *tagger_data; + + if (proto != DSA_TAG_PROTO_KSZ9893 && proto != DSA_TAG_PROTO_KSZ9477) + return -EPROTONOSUPPORT; + + tagger_data = ksz_tagger_data(ds); + tagger_data->xmit_work_fn = ksz_port_deferred_xmit; + + return 0; +} + +static void ksz9477_set_gbit(struct ksz_device *dev, int port, bool gbit) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + data8 &= ~P_GMII_1GBIT_M; + + if (gbit) + data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_1GBIT]); + else + data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_NOT_1GBIT]); + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); +} + +static void ksz9477_set_100_10mbit(struct ksz_device *dev, int port, int speed) +{ + const u8 *bitval = dev->info->xmii_ctrl0; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_0], &data8); + + data8 &= ~P_MII_100MBIT_M; + + if (speed == SPEED_100) + data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_100MBIT]); + else + data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_10MBIT]); + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_0], data8); +} + +static void ksz9477_port_set_xmii_speed(struct ksz_device *dev, int port, + int speed) +{ + if (speed == SPEED_1000) + ksz9477_set_gbit(dev, port, true); + else + ksz9477_set_gbit(dev, port, false); + + if (speed == SPEED_100 || speed == SPEED_10) + ksz9477_set_100_10mbit(dev, port, speed); +} + +static void ksz9477_duplex_flowctrl(struct ksz_device *dev, int port, int duplex, + bool tx_pause, bool rx_pause) +{ + const u8 *bitval = dev->info->xmii_ctrl0; + const u32 *masks = dev->info->masks; + const u16 *regs = dev->info->regs; + u8 mask; + u8 val; + + mask = P_MII_DUPLEX_M | masks[P_MII_TX_FLOW_CTRL] | + masks[P_MII_RX_FLOW_CTRL]; + + if (duplex == DUPLEX_FULL) + val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_FULL_DUPLEX]); + else + val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_HALF_DUPLEX]); + + if (tx_pause) + val |= masks[P_MII_TX_FLOW_CTRL]; + + if (rx_pause) + val |= masks[P_MII_RX_FLOW_CTRL]; + + ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val); +} + +static void ksz9477_port_teardown(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (dsa_is_user_port(ds, port)) + ksz9477_port_acl_free(dev, port); +} + +void ksz9477_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, bool tx_pause, + bool rx_pause) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + int port = dp->index; + struct ksz_port *p; + + p = &dev->ports[port]; + + /* Internal PHYs */ + if (dev->info->internal_phy[port]) + return; + + p->speed = speed; + + ksz9477_port_set_xmii_speed(dev, port, speed); + + ksz9477_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause); +} + +/** + * ksz9477_support_eee - Determine Energy Efficient Ethernet (EEE) support for a + * port + * @ds: Pointer to the DSA switch structure + * @port: Port number to check + * + * This function also documents devices where EEE was initially advertised but + * later withdrawn due to reliability issues, as described in official errata + * documents. These devices are explicitly listed to record known limitations, + * even if there is no technical necessity for runtime checks. + * + * Returns: true if the internal PHY on the given port supports fully + * operational EEE, false otherwise. + */ +static bool ksz9477_support_eee(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->info->internal_phy[port]) + return false; + + switch (dev->chip_id) { + case KSZ8563_CHIP_ID: + case KSZ9563_CHIP_ID: + case KSZ9893_CHIP_ID: + return true; + default: + /* KSZ8567R Errata DS80000752C Module 4 */ + /* KSZ9477S Errata DS80000754A Module 4 */ + /* KSZ9567S Errata DS80000756A Module 4 */ + /* KSZ9896C Errata DS80000757A Module 3 */ + /* KSZ9897R Errata DS80000758C Module 4 */ + /* Energy Efficient Ethernet (EEE) feature select must be + * manually disabled + * The EEE feature is enabled by default, but it is not fully + * operational. It must be manually disabled through register + * controls. If not disabled, the PHY ports can auto-negotiate + * to enable EEE, and this feature can cause link drops when + * linked to another device supporting EEE. + * + * The same item appears in the errata for all switches above. + */ + break; + } + + return false; +} + +static struct phylink_pcs * +ksz9477_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + struct ksz_port *p = &dev->ports[dp->index]; + + if (ksz_is_sgmii_port(dev, dp->index) && + (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX)) + return p->pcs; + + return NULL; +} + +const struct phylink_mac_ops ksz9477_phylink_mac_ops = { + .mac_config = ksz_phylink_mac_config, + .mac_link_down = ksz_phylink_mac_link_down, + .mac_link_up = ksz9477_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, + .mac_select_pcs = ksz9477_phylink_mac_select_pcs, +}; + +const struct ksz_dev_ops ksz9477_dev_ops = { + .get_port_addr = ksz9477_get_port_addr, + .cfg_port_member = ksz9477_cfg_port_member, + .r_mib_cnt = ksz9477_r_mib_cnt, + .r_mib_pkt = ksz9477_r_mib_pkt, + .r_mib_stat64 = ksz_r_mib_stats64, + .freeze_mib = ksz9477_freeze_mib, + .port_init_cnt = ksz9477_port_init_cnt, + .pme_write8 = ksz_write8, + .pme_pread8 = ksz_pread8, + .pme_pwrite8 = ksz_pwrite8, + .tc_cbs_set_cinc = ksz9477_tc_cbs_set_cinc, + .init = ksz9477_switch_init, +}; + +const struct dsa_switch_ops ksz9477_switch_ops = { + .get_tag_protocol = ksz9477_get_tag_protocol, + .connect_tag_protocol = ksz9477_connect_tag_protocol, + .setup = ksz9477_setup, + .teardown = ksz_teardown, + .phy_read = ksz9477_phy_read16, + .phy_write = ksz9477_phy_write16, + .phylink_get_caps = ksz9477_phylink_get_caps, + .port_setup = ksz9477_dsa_port_setup, + .set_ageing_time = ksz9477_set_ageing_time, + .get_strings = ksz_get_strings, + .get_ethtool_stats = ksz_get_ethtool_stats, + .get_sset_count = ksz_sset_count, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .port_hsr_join = ksz9477_hsr_join, + .port_hsr_leave = ksz9477_hsr_leave, + .port_set_mac_address = ksz_port_set_mac_address, + .port_stp_state_set = ksz_port_stp_state_set, + .port_teardown = ksz9477_port_teardown, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, + .port_fast_age = ksz9477_flush_dyn_mac_table, + .port_vlan_filtering = ksz9477_port_vlan_filtering, + .port_vlan_add = ksz9477_port_vlan_add, + .port_vlan_del = ksz9477_port_vlan_del, + .port_fdb_dump = ksz9477_fdb_dump, + .port_fdb_add = ksz9477_fdb_add, + .port_fdb_del = ksz9477_fdb_del, + .port_mdb_add = ksz9477_mdb_add, + .port_mdb_del = ksz9477_mdb_del, + .port_mirror_add = ksz9477_port_mirror_add, + .port_mirror_del = ksz9477_port_mirror_del, + .get_stats64 = ksz_get_stats64, + .get_pause_stats = ksz_get_pause_stats, + .port_change_mtu = ksz9477_change_mtu, + .port_max_mtu = ksz_max_mtu, + .get_wol = ksz_get_wol, + .set_wol = ksz_set_wol, + .suspend = ksz_suspend, + .resume = ksz_resume, + .get_ts_info = ksz_get_ts_info, + .port_hwtstamp_get = ksz_hwtstamp_get, + .port_hwtstamp_set = ksz_hwtstamp_set, + .port_txtstamp = ksz_port_txtstamp, + .port_rxtstamp = ksz_port_rxtstamp, + .cls_flower_add = ksz9477_cls_flower_add, + .cls_flower_del = ksz9477_cls_flower_del, + .port_setup_tc = ksz_setup_tc, + .support_eee = ksz9477_support_eee, + .set_mac_eee = ksz_set_mac_eee, + .port_get_default_prio = ksz_port_get_default_prio, + .port_set_default_prio = ksz_port_set_default_prio, + .port_get_dscp_prio = ksz_port_get_dscp_prio, + .port_add_dscp_prio = ksz_port_add_dscp_prio, + .port_del_dscp_prio = ksz_port_del_dscp_prio, + .port_get_apptrust = ksz_port_get_apptrust, + .port_set_apptrust = ksz_port_set_apptrust, +}; + MODULE_AUTHOR("Woojung Huh "); MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch DSA Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h index 0d1a6dfda23e..92a1d889224d 100644 --- a/drivers/net/dsa/microchip/ksz9477.h +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -11,55 +11,40 @@ #include #include "ksz_common.h" -int ksz9477_setup(struct dsa_switch *ds); u32 ksz9477_get_port_addr(int port, int offset); void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member); -void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port); -void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port); -int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs); -int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data); -int ksz9477_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val); +void ksz9477_flush_dyn_mac_table(struct dsa_switch *ds, int port); void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt); void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt); void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze); void ksz9477_port_init_cnt(struct ksz_device *dev, int port); -int ksz9477_port_vlan_filtering(struct ksz_device *dev, int port, +int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, struct netlink_ext_ack *extack); -int ksz9477_port_vlan_add(struct ksz_device *dev, int port, +int ksz9477_port_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan, struct netlink_ext_ack *extack); -int ksz9477_port_vlan_del(struct ksz_device *dev, int port, +int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); -int ksz9477_port_mirror_add(struct ksz_device *dev, int port, +int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress, struct netlink_ext_ack *extack); -void ksz9477_port_mirror_del(struct ksz_device *dev, int port, +void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); int ksz9477_errata_monitor(struct ksz_device *dev, int port, u64 tx_late_col); -void ksz9477_get_caps(struct ksz_device *dev, int port, - struct phylink_config *config); -int ksz9477_fdb_dump(struct ksz_device *dev, int port, +int ksz9477_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data); -int ksz9477_fdb_add(struct ksz_device *dev, int port, +int ksz9477_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db); -int ksz9477_fdb_del(struct ksz_device *dev, int port, +int ksz9477_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid, struct dsa_db db); -int ksz9477_mdb_add(struct ksz_device *dev, int port, +int ksz9477_mdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); -int ksz9477_mdb_del(struct ksz_device *dev, int port, +int ksz9477_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); -int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu); -void ksz9477_config_cpu_port(struct dsa_switch *ds); -int ksz9477_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val); int ksz9477_enable_stp_addr(struct ksz_device *dev); -int ksz9477_reset_switch(struct ksz_device *dev); -int ksz9477_switch_init(struct ksz_device *dev); -void ksz9477_switch_exit(struct ksz_device *dev); void ksz9477_port_queue_split(struct ksz_device *dev, int port); -void ksz9477_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr); -void ksz9477_hsr_leave(struct dsa_switch *ds, int port, struct net_device *hsr); int ksz9477_port_acl_init(struct ksz_device *dev, int port); void ksz9477_port_acl_free(struct ksz_device *dev, int port); @@ -97,6 +82,15 @@ void ksz9477_acl_match_process_l2(struct ksz_device *dev, int port, u16 ethtype, u8 *src_mac, u8 *dst_mac, unsigned long cookie, u32 prio); -int ksz9477_pcs_create(struct ksz_device *dev); +void ksz9477_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, bool tx_pause, + bool rx_pause); + +extern const struct ksz_dev_ops ksz9477_dev_ops; +extern const struct phylink_mac_ops ksz9477_phylink_mac_ops; +extern const struct dsa_switch_ops ksz9477_switch_ops; #endif diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index a2beb27459f1..ec0c9cab3def 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -22,14 +22,14 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c) struct ksz_device *dev; int i, ret; - dev = ksz_switch_alloc(&i2c->dev, i2c); - if (!dev) - return -ENOMEM; - chip = device_get_match_data(ddev); if (!chip) return -EINVAL; + dev = ksz_switch_alloc(&i2c->dev, chip, i2c); + if (!dev) + return -ENOMEM; + /* Save chip id to do special initialization when probing. */ dev->chip_id = chip->chip_id; for (i = 0; i < __KSZ_NUM_REGMAPS; i++) { @@ -80,8 +80,8 @@ static void ksz9477_i2c_shutdown(struct i2c_client *i2c) } static const struct i2c_device_id ksz9477_i2c_id[] = { - { "ksz9477-switch" }, - {} + { .name = "ksz9477-switch" }, + { } }; MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 144373e13bea..d1726778bb48 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -256,16 +255,6 @@ static const struct ksz_drive_strength ksz88x3_drive_strengths[] = { { KSZ8873_DRIVE_STRENGTH_16MA, 16000 }, }; -static void ksz88x3_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state); -static void ksz_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state); -static void ksz_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface); - /** * ksz_phylink_mac_disable_tx_lpi() - Callback to signal LPI support (Dummy) * @config: phylink config structure @@ -273,7 +262,7 @@ static void ksz_phylink_mac_link_down(struct phylink_config *config, * This function is a dummy handler. See ksz_phylink_mac_enable_tx_lpi() for * a detailed explanation of EEE/LPI handling in KSZ switches. */ -static void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config) +void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config) { } @@ -310,246 +299,12 @@ static void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config) * * Returns: 0 (Always success) */ -static int ksz_phylink_mac_enable_tx_lpi(struct phylink_config *config, - u32 timer, bool tx_clock_stop) +int ksz_phylink_mac_enable_tx_lpi(struct phylink_config *config, + u32 timer, bool tx_clock_stop) { return 0; } -static const struct phylink_mac_ops ksz88x3_phylink_mac_ops = { - .mac_config = ksz88x3_phylink_mac_config, - .mac_link_down = ksz_phylink_mac_link_down, - .mac_link_up = ksz8_phylink_mac_link_up, - .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, - .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, -}; - -static const struct phylink_mac_ops ksz8_phylink_mac_ops = { - .mac_config = ksz_phylink_mac_config, - .mac_link_down = ksz_phylink_mac_link_down, - .mac_link_up = ksz8_phylink_mac_link_up, - .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, - .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, -}; - -static const struct ksz_dev_ops ksz8463_dev_ops = { - .setup = ksz8_setup, - .get_port_addr = ksz8463_get_port_addr, - .cfg_port_member = ksz8_cfg_port_member, - .flush_dyn_mac_table = ksz8_flush_dyn_mac_table, - .port_setup = ksz8_port_setup, - .r_phy = ksz8463_r_phy, - .w_phy = ksz8463_w_phy, - .r_mib_cnt = ksz8_r_mib_cnt, - .r_mib_pkt = ksz8_r_mib_pkt, - .r_mib_stat64 = ksz88xx_r_mib_stats64, - .freeze_mib = ksz8_freeze_mib, - .port_init_cnt = ksz8_port_init_cnt, - .fdb_dump = ksz8_fdb_dump, - .fdb_add = ksz8_fdb_add, - .fdb_del = ksz8_fdb_del, - .mdb_add = ksz8_mdb_add, - .mdb_del = ksz8_mdb_del, - .vlan_filtering = ksz8_port_vlan_filtering, - .vlan_add = ksz8_port_vlan_add, - .vlan_del = ksz8_port_vlan_del, - .mirror_add = ksz8_port_mirror_add, - .mirror_del = ksz8_port_mirror_del, - .get_caps = ksz8_get_caps, - .config_cpu_port = ksz8_config_cpu_port, - .enable_stp_addr = ksz8_enable_stp_addr, - .reset = ksz8_reset_switch, - .init = ksz8_switch_init, - .exit = ksz8_switch_exit, - .change_mtu = ksz8_change_mtu, -}; - -static const struct ksz_dev_ops ksz88xx_dev_ops = { - .setup = ksz8_setup, - .get_port_addr = ksz8_get_port_addr, - .cfg_port_member = ksz8_cfg_port_member, - .flush_dyn_mac_table = ksz8_flush_dyn_mac_table, - .port_setup = ksz8_port_setup, - .r_phy = ksz8_r_phy, - .w_phy = ksz8_w_phy, - .r_mib_cnt = ksz8_r_mib_cnt, - .r_mib_pkt = ksz8_r_mib_pkt, - .r_mib_stat64 = ksz88xx_r_mib_stats64, - .freeze_mib = ksz8_freeze_mib, - .port_init_cnt = ksz8_port_init_cnt, - .fdb_dump = ksz8_fdb_dump, - .fdb_add = ksz8_fdb_add, - .fdb_del = ksz8_fdb_del, - .mdb_add = ksz8_mdb_add, - .mdb_del = ksz8_mdb_del, - .vlan_filtering = ksz8_port_vlan_filtering, - .vlan_add = ksz8_port_vlan_add, - .vlan_del = ksz8_port_vlan_del, - .mirror_add = ksz8_port_mirror_add, - .mirror_del = ksz8_port_mirror_del, - .get_caps = ksz8_get_caps, - .config_cpu_port = ksz8_config_cpu_port, - .enable_stp_addr = ksz8_enable_stp_addr, - .reset = ksz8_reset_switch, - .init = ksz8_switch_init, - .exit = ksz8_switch_exit, - .change_mtu = ksz8_change_mtu, - .pme_write8 = ksz8_pme_write8, - .pme_pread8 = ksz8_pme_pread8, - .pme_pwrite8 = ksz8_pme_pwrite8, -}; - -static const struct ksz_dev_ops ksz87xx_dev_ops = { - .setup = ksz8_setup, - .get_port_addr = ksz8_get_port_addr, - .cfg_port_member = ksz8_cfg_port_member, - .flush_dyn_mac_table = ksz8_flush_dyn_mac_table, - .port_setup = ksz8_port_setup, - .r_phy = ksz8_r_phy, - .w_phy = ksz8_w_phy, - .r_mib_cnt = ksz8_r_mib_cnt, - .r_mib_pkt = ksz8_r_mib_pkt, - .r_mib_stat64 = ksz_r_mib_stats64, - .freeze_mib = ksz8_freeze_mib, - .port_init_cnt = ksz8_port_init_cnt, - .fdb_dump = ksz8_fdb_dump, - .fdb_add = ksz8_fdb_add, - .fdb_del = ksz8_fdb_del, - .mdb_add = ksz8_mdb_add, - .mdb_del = ksz8_mdb_del, - .vlan_filtering = ksz8_port_vlan_filtering, - .vlan_add = ksz8_port_vlan_add, - .vlan_del = ksz8_port_vlan_del, - .mirror_add = ksz8_port_mirror_add, - .mirror_del = ksz8_port_mirror_del, - .get_caps = ksz8_get_caps, - .config_cpu_port = ksz8_config_cpu_port, - .enable_stp_addr = ksz8_enable_stp_addr, - .reset = ksz8_reset_switch, - .init = ksz8_switch_init, - .exit = ksz8_switch_exit, - .change_mtu = ksz8_change_mtu, - .pme_write8 = ksz8_pme_write8, - .pme_pread8 = ksz8_pme_pread8, - .pme_pwrite8 = ksz8_pme_pwrite8, -}; - -static void ksz9477_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, - unsigned int mode, - phy_interface_t interface, - int speed, int duplex, bool tx_pause, - bool rx_pause); - -static struct phylink_pcs * -ksz_phylink_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct ksz_device *dev = dp->ds->priv; - struct ksz_port *p = &dev->ports[dp->index]; - - if (ksz_is_sgmii_port(dev, dp->index) && - (interface == PHY_INTERFACE_MODE_SGMII || - interface == PHY_INTERFACE_MODE_1000BASEX)) - return p->pcs; - - return NULL; -} - -static const struct phylink_mac_ops ksz9477_phylink_mac_ops = { - .mac_config = ksz_phylink_mac_config, - .mac_link_down = ksz_phylink_mac_link_down, - .mac_link_up = ksz9477_phylink_mac_link_up, - .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, - .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, - .mac_select_pcs = ksz_phylink_mac_select_pcs, -}; - -static const struct ksz_dev_ops ksz9477_dev_ops = { - .setup = ksz9477_setup, - .get_port_addr = ksz9477_get_port_addr, - .cfg_port_member = ksz9477_cfg_port_member, - .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, - .port_setup = ksz9477_port_setup, - .set_ageing_time = ksz9477_set_ageing_time, - .r_phy = ksz9477_r_phy, - .w_phy = ksz9477_w_phy, - .r_mib_cnt = ksz9477_r_mib_cnt, - .r_mib_pkt = ksz9477_r_mib_pkt, - .r_mib_stat64 = ksz_r_mib_stats64, - .freeze_mib = ksz9477_freeze_mib, - .port_init_cnt = ksz9477_port_init_cnt, - .vlan_filtering = ksz9477_port_vlan_filtering, - .vlan_add = ksz9477_port_vlan_add, - .vlan_del = ksz9477_port_vlan_del, - .mirror_add = ksz9477_port_mirror_add, - .mirror_del = ksz9477_port_mirror_del, - .get_caps = ksz9477_get_caps, - .fdb_dump = ksz9477_fdb_dump, - .fdb_add = ksz9477_fdb_add, - .fdb_del = ksz9477_fdb_del, - .mdb_add = ksz9477_mdb_add, - .mdb_del = ksz9477_mdb_del, - .change_mtu = ksz9477_change_mtu, - .pme_write8 = ksz_write8, - .pme_pread8 = ksz_pread8, - .pme_pwrite8 = ksz_pwrite8, - .config_cpu_port = ksz9477_config_cpu_port, - .tc_cbs_set_cinc = ksz9477_tc_cbs_set_cinc, - .enable_stp_addr = ksz9477_enable_stp_addr, - .reset = ksz9477_reset_switch, - .init = ksz9477_switch_init, - .exit = ksz9477_switch_exit, - .pcs_create = ksz9477_pcs_create, -}; - -static const struct phylink_mac_ops lan937x_phylink_mac_ops = { - .mac_config = ksz_phylink_mac_config, - .mac_link_down = ksz_phylink_mac_link_down, - .mac_link_up = ksz9477_phylink_mac_link_up, - .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, - .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, -}; - -static const struct ksz_dev_ops lan937x_dev_ops = { - .setup = lan937x_setup, - .teardown = lan937x_teardown, - .get_port_addr = ksz9477_get_port_addr, - .cfg_port_member = ksz9477_cfg_port_member, - .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, - .port_setup = lan937x_port_setup, - .set_ageing_time = lan937x_set_ageing_time, - .mdio_bus_preinit = lan937x_mdio_bus_preinit, - .create_phy_addr_map = lan937x_create_phy_addr_map, - .r_phy = lan937x_r_phy, - .w_phy = lan937x_w_phy, - .r_mib_cnt = ksz9477_r_mib_cnt, - .r_mib_pkt = ksz9477_r_mib_pkt, - .r_mib_stat64 = ksz_r_mib_stats64, - .freeze_mib = ksz9477_freeze_mib, - .port_init_cnt = ksz9477_port_init_cnt, - .vlan_filtering = ksz9477_port_vlan_filtering, - .vlan_add = ksz9477_port_vlan_add, - .vlan_del = ksz9477_port_vlan_del, - .mirror_add = ksz9477_port_mirror_add, - .mirror_del = ksz9477_port_mirror_del, - .get_caps = lan937x_phylink_get_caps, - .setup_rgmii_delay = lan937x_setup_rgmii_delay, - .fdb_dump = ksz9477_fdb_dump, - .fdb_add = ksz9477_fdb_add, - .fdb_del = ksz9477_fdb_del, - .mdb_add = ksz9477_mdb_add, - .mdb_del = ksz9477_mdb_del, - .change_mtu = lan937x_change_mtu, - .config_cpu_port = lan937x_config_cpu_port, - .tc_cbs_set_cinc = lan937x_tc_cbs_set_cinc, - .enable_stp_addr = ksz9477_enable_stp_addr, - .reset = lan937x_reset_switch, - .init = lan937x_switch_init, - .exit = lan937x_switch_exit, -}; - static const u16 ksz8463_regs[] = { [REG_SW_MAC_ADDR] = 0x10, [REG_IND_CTRL_0] = 0x30, @@ -1502,6 +1257,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz8463_dev_ops, + .switch_ops = &ksz8463_switch_ops, .phylink_mac_ops = &ksz88x3_phylink_mac_ops, .mib_names = ksz88xx_mib_names, .mib_cnt = ARRAY_SIZE(ksz88xx_mib_names), @@ -1527,6 +1283,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_ipms = 8, .tc_cbs_supported = true, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1557,6 +1314,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz87xx_dev_ops, + .switch_ops = &ksz87xx_switch_ops, .phylink_mac_ops = &ksz8_phylink_mac_ops, .ksz87xx_eee_link_erratum = true, .mib_names = ksz9477_mib_names, @@ -1598,6 +1356,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz87xx_dev_ops, + .switch_ops = &ksz87xx_switch_ops, .phylink_mac_ops = &ksz8_phylink_mac_ops, .ksz87xx_eee_link_erratum = true, .mib_names = ksz9477_mib_names, @@ -1625,6 +1384,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz87xx_dev_ops, + .switch_ops = &ksz87xx_switch_ops, .phylink_mac_ops = &ksz8_phylink_mac_ops, .ksz87xx_eee_link_erratum = true, .mib_names = ksz9477_mib_names, @@ -1652,6 +1412,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz88xx_dev_ops, + .switch_ops = &ksz88xx_switch_ops, .phylink_mac_ops = &ksz88x3_phylink_mac_ops, .mib_names = ksz88xx_mib_names, .mib_cnt = ARRAY_SIZE(ksz88xx_mib_names), @@ -1686,6 +1447,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz88xx_dev_ops, + .switch_ops = &ksz88xx_switch_ops, .phylink_mac_ops = &ksz88x3_phylink_mac_ops, .mib_names = ksz88xx_mib_names, .mib_cnt = ARRAY_SIZE(ksz88xx_mib_names), @@ -1709,6 +1471,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 4, .ops = &ksz88xx_dev_ops, + .switch_ops = &ksz88xx_switch_ops, .phylink_mac_ops = &ksz88x3_phylink_mac_ops, .mib_names = ksz88xx_mib_names, .mib_cnt = ARRAY_SIZE(ksz88xx_mib_names), @@ -1734,6 +1497,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_ipms = 8, .tc_cbs_supported = true, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1771,6 +1535,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 8, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1806,6 +1571,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 8, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1839,6 +1605,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 8, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1868,6 +1635,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_ipms = 8, .tc_cbs_supported = true, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1898,6 +1666,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_ipms = 8, .tc_cbs_supported = true, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1934,6 +1703,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_ipms = 8, .tc_cbs_supported = true, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), .reg_mib_cnt = MIB_COUNTER_NUM, @@ -1968,6 +1738,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .tc_cbs_supported = true, .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, + .switch_ops = &lan937x_switch_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1998,6 +1769,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .tc_cbs_supported = true, .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, + .switch_ops = &lan937x_switch_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -2028,6 +1800,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .tc_cbs_supported = true, .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, + .switch_ops = &lan937x_switch_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -2062,6 +1835,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .tc_cbs_supported = true, .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, + .switch_ops = &lan937x_switch_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -2096,6 +1870,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .tc_cbs_supported = true, .phy_side_mdio_supported = true, .ops = &lan937x_dev_ops, + .switch_ops = &lan937x_switch_ops, .phylink_mac_ops = &lan937x_phylink_mac_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -2128,6 +1903,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .num_tx_queues = 4, .num_ipms = 8, .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, .phylink_mac_ops = &ksz9477_phylink_mac_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -2193,8 +1969,8 @@ static int ksz_check_device_id(struct ksz_device *dev) return 0; } -static void ksz_phylink_get_caps(struct dsa_switch *ds, int port, - struct phylink_config *config) +void ksz_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { struct ksz_device *dev = ds->priv; @@ -2218,9 +1994,6 @@ static void ksz_phylink_get_caps(struct dsa_switch *ds, int port, config->supported_interfaces); } - if (dev->dev_ops->get_caps) - dev->dev_ops->get_caps(dev, port, config); - if (ds->ops->support_eee && ds->ops->support_eee(ds, port)) { memcpy(config->lpi_interfaces, config->supported_interfaces, sizeof(config->lpi_interfaces)); @@ -2339,8 +2112,8 @@ void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port) spin_unlock(&mib->stats64_lock); } -static void ksz_get_stats64(struct dsa_switch *ds, int port, - struct rtnl_link_stats64 *s) +void ksz_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) { struct ksz_device *dev = ds->priv; struct ksz_port_mib *mib; @@ -2352,8 +2125,8 @@ static void ksz_get_stats64(struct dsa_switch *ds, int port, spin_unlock(&mib->stats64_lock); } -static void ksz_get_pause_stats(struct dsa_switch *ds, int port, - struct ethtool_pause_stats *pause_stats) +void ksz_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) { struct ksz_device *dev = ds->priv; struct ksz_port_mib *mib; @@ -2365,8 +2138,8 @@ static void ksz_get_pause_stats(struct dsa_switch *ds, int port, spin_unlock(&mib->stats64_lock); } -static void ksz_get_strings(struct dsa_switch *ds, int port, - u32 stringset, uint8_t *buf) +void ksz_get_strings(struct dsa_switch *ds, int port, + u32 stringset, uint8_t *buf) { struct ksz_device *dev = ds->priv; int i; @@ -2483,25 +2256,20 @@ static void ksz_update_port_member(struct ksz_device *dev, int port) dev->dev_ops->cfg_port_member(dev, port, port_member | cpu_port); } -static int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) +int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) { struct ksz_device *dev = bus->priv; - u16 val; - int ret; + struct dsa_switch *ds = dev->ds; - ret = dev->dev_ops->r_phy(dev, addr, regnum, &val); - if (ret < 0) - return ret; - - return val; + return ds->ops->phy_read(ds, addr, regnum); } -static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) +int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) { struct ksz_device *dev = bus->priv; + struct dsa_switch *ds = dev->ds; - return dev->dev_ops->w_phy(dev, addr, regnum, val); + return ds->ops->phy_write(ds, addr, regnum, val); } /** @@ -2517,7 +2285,7 @@ static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, * * Return: Value of the PHY register, or a negative error code on failure. */ -static int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) +int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) { struct ksz_device *dev = bus->priv; @@ -2537,8 +2305,7 @@ static int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum) * * Return: 0 on success, or a negative error code on failure. */ -static int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, - u16 val) +int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) { struct ksz_device *dev = bus->priv; @@ -2646,8 +2413,8 @@ static void ksz_irq_phy_free(struct ksz_device *dev) * * Return: 0 on success, or a negative error code on failure. */ -static int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, - struct device_node *mdio_np) +int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, + struct device_node *mdio_np) { struct device_node *phy_node, *phy_parent_node; bool phys_are_valid = true; @@ -2714,7 +2481,7 @@ static int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, * * Return: 0 on success, or a negative error code on failure. */ -static int ksz_mdio_register(struct ksz_device *dev) +int ksz_mdio_register(struct ksz_device *dev) { struct device_node *parent_bus_node; struct mii_bus *parent_bus = NULL; @@ -2750,20 +2517,8 @@ static int ksz_mdio_register(struct ksz_device *dev) goto put_mdio_node; } - if (dev->dev_ops->mdio_bus_preinit) { - ret = dev->dev_ops->mdio_bus_preinit(dev, !!parent_bus); - if (ret) - goto put_mdio_node; - } - - if (dev->dev_ops->create_phy_addr_map) { - ret = dev->dev_ops->create_phy_addr_map(dev, !!parent_bus); - if (ret) - goto put_mdio_node; - } else { - for (i = 0; i < dev->info->port_cnt; i++) - dev->phy_addr_map[i] = i; - } + for (i = 0; i < dev->info->port_cnt; i++) + dev->phy_addr_map[i] = i; bus->priv = dev; if (parent_bus) { @@ -2870,7 +2625,7 @@ static const struct irq_domain_ops ksz_irq_domain_ops = { .xlate = irq_domain_xlate_twocell, }; -static void ksz_irq_free(struct ksz_irq *kirq) +void ksz_irq_free(struct ksz_irq *kirq) { int irq, virq; @@ -2939,7 +2694,7 @@ static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq) return ret; } -static int ksz_girq_setup(struct ksz_device *dev) +int ksz_girq_setup(struct ksz_device *dev) { struct ksz_irq *girq = &dev->girq; @@ -2954,7 +2709,7 @@ static int ksz_girq_setup(struct ksz_device *dev) return ksz_irq_common_setup(dev, girq); } -static int ksz_pirq_setup(struct ksz_device *dev, u8 p) +int ksz_pirq_setup(struct ksz_device *dev, u8 p) { struct ksz_irq *pirq = &dev->ports[p].pirq; @@ -2971,139 +2726,7 @@ static int ksz_pirq_setup(struct ksz_device *dev, u8 p) return ksz_irq_common_setup(dev, pirq); } -static int ksz_parse_drive_strength(struct ksz_device *dev); - -static int ksz_setup(struct dsa_switch *ds) -{ - struct ksz_device *dev = ds->priv; - u16 storm_mask, storm_rate; - struct dsa_port *dp; - struct ksz_port *p; - const u16 *regs; - int ret; - - regs = dev->info->regs; - - dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), - dev->info->num_vlans, GFP_KERNEL); - if (!dev->vlan_cache) - return -ENOMEM; - - ret = dev->dev_ops->reset(dev); - if (ret) { - dev_err(ds->dev, "failed to reset switch\n"); - return ret; - } - - ret = ksz_parse_drive_strength(dev); - if (ret) - return ret; - - if (ksz_has_sgmii_port(dev) && dev->dev_ops->pcs_create) { - ret = dev->dev_ops->pcs_create(dev); - if (ret) - return ret; - } - - /* set broadcast storm protection 10% rate */ - storm_mask = BROADCAST_STORM_RATE; - storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100; - if (ksz_is_ksz8463(dev)) { - storm_mask = swab16(storm_mask); - storm_rate = swab16(storm_rate); - } - regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], - storm_mask, storm_rate); - - dev->dev_ops->config_cpu_port(ds); - - dev->dev_ops->enable_stp_addr(dev); - - ds->num_tx_queues = dev->info->num_tx_queues; - - regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL], - MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE); - - ksz_init_mib_timer(dev); - - ds->configure_vlan_while_not_filtering = false; - ds->dscp_prio_mapping_is_global = true; - - if (dev->dev_ops->setup) { - ret = dev->dev_ops->setup(ds); - if (ret) - return ret; - } - - /* Start with learning disabled on standalone user ports, and enabled - * on the CPU port. In lack of other finer mechanisms, learning on the - * CPU port will avoid flooding bridge local addresses on the network - * in some cases. - */ - p = &dev->ports[dev->cpu_port]; - p->learning = true; - - if (dev->irq > 0) { - ret = ksz_girq_setup(dev); - if (ret) - return ret; - - dsa_switch_for_each_user_port(dp, dev->ds) { - ret = ksz_pirq_setup(dev, dp->index); - if (ret) - goto port_release; - - if (dev->info->ptp_capable) { - ret = ksz_ptp_irq_setup(ds, dp->index); - if (ret) - goto pirq_release; - } - } - } - - if (dev->info->ptp_capable) { - ret = ksz_ptp_clock_register(ds); - if (ret) { - dev_err(dev->dev, "Failed to register PTP clock: %d\n", - ret); - goto port_release; - } - } - - ret = ksz_mdio_register(dev); - if (ret < 0) { - dev_err(dev->dev, "failed to register the mdio"); - goto out_ptp_clock_unregister; - } - - ret = ksz_dcb_init(dev); - if (ret) - goto out_ptp_clock_unregister; - - /* start switch */ - regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL], - SW_START, SW_START); - - return 0; - -out_ptp_clock_unregister: - if (dev->info->ptp_capable) - ksz_ptp_clock_unregister(ds); -port_release: - if (dev->irq > 0) { - dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) { - if (dev->info->ptp_capable) - ksz_ptp_irq_free(ds, dp->index); -pirq_release: - ksz_irq_free(&dev->ports[dp->index].pirq); - } - ksz_irq_free(&dev->girq); - } - - return ret; -} - -static void ksz_teardown(struct dsa_switch *ds) +void ksz_teardown(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; struct dsa_port *dp; @@ -3121,9 +2744,6 @@ static void ksz_teardown(struct dsa_switch *ds) ksz_irq_free(&dev->girq); } - - if (dev->dev_ops->teardown) - dev->dev_ops->teardown(ds); } static void port_r_cnt(struct ksz_device *dev, int port) @@ -3203,52 +2823,9 @@ void ksz_init_mib_timer(struct ksz_device *dev) } } -static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) -{ - struct ksz_device *dev = ds->priv; - u16 val = 0xffff; - int ret; - - ret = dev->dev_ops->r_phy(dev, addr, reg, &val); - if (ret) - return ret; - - return val; -} - -static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) -{ - struct ksz_device *dev = ds->priv; - int ret; - - ret = dev->dev_ops->w_phy(dev, addr, reg, val); - if (ret) - return ret; - - return 0; -} - -static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ88X3_CHIP_ID: - /* Silicon Errata Sheet (DS80000830A): - * Port 1 does not work with LinkMD Cable-Testing. - * Port 1 does not respond to received PAUSE control frames. - */ - if (!port) - return MICREL_KSZ8_P1_ERRATA; - break; - } - - return 0; -} - -static void ksz_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface) +void ksz_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) { struct dsa_port *dp = dsa_phylink_to_port(config); struct ksz_device *dev = dp->ds->priv; @@ -3260,7 +2837,7 @@ static void ksz_phylink_mac_link_down(struct phylink_config *config, schedule_delayed_work(&dev->mib_read, 0); } -static int ksz_sset_count(struct dsa_switch *ds, int port, int sset) +int ksz_sset_count(struct dsa_switch *ds, int port, int sset) { struct ksz_device *dev = ds->priv; @@ -3270,8 +2847,8 @@ static int ksz_sset_count(struct dsa_switch *ds, int port, int sset) return dev->info->mib_cnt; } -static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, - uint64_t *buf) +void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *buf) { const struct dsa_port *dp = dsa_to_port(ds, port); struct ksz_device *dev = ds->priv; @@ -3288,10 +2865,10 @@ static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, mutex_unlock(&mib->cnt_mutex); } -static int ksz_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, - bool *tx_fwd_offload, - struct netlink_ext_ack *extack) +int ksz_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + 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. @@ -3300,92 +2877,15 @@ static int ksz_port_bridge_join(struct dsa_switch *ds, int port, return 0; } -static void ksz_port_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_bridge bridge) +void ksz_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) { /* port_stp_state_set() will be called after to put the port in * forwarding state so there is no need to do anything. */ } -static void ksz_port_fast_age(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - dev->dev_ops->flush_dyn_mac_table(dev, port); -} - -static int ksz_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->set_ageing_time) - return -EOPNOTSUPP; - - return dev->dev_ops->set_ageing_time(dev, msecs); -} - -static int ksz_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->fdb_add) - return -EOPNOTSUPP; - - return dev->dev_ops->fdb_add(dev, port, addr, vid, db); -} - -static int ksz_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, - u16 vid, struct dsa_db db) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->fdb_del) - return -EOPNOTSUPP; - - return dev->dev_ops->fdb_del(dev, port, addr, vid, db); -} - -static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, - dsa_fdb_dump_cb_t *cb, void *data) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->fdb_dump) - return -EOPNOTSUPP; - - return dev->dev_ops->fdb_dump(dev, port, cb, data); -} - -static int ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->mdb_add) - return -EOPNOTSUPP; - - return dev->dev_ops->mdb_add(dev, port, mdb, db); -} - -static int ksz_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->mdb_del) - return -EOPNOTSUPP; - - return dev->dev_ops->mdb_del(dev, port, mdb, db); -} - -static int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, - int port) +int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port) { u32 queue_map = 0; int ipm; @@ -3407,30 +2907,6 @@ static int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); } -static int ksz_port_setup(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - int ret; - - if (!dsa_is_user_port(ds, port)) - return 0; - - /* setup user port */ - dev->dev_ops->port_setup(dev, port, false); - - if (!is_ksz8(dev)) { - ret = ksz9477_set_default_prio_queue_mapping(dev, port); - if (ret) - return ret; - } - - /* port_stp_state_set() will be called after to enable the port so - * there is no need to do anything. - */ - - return ksz_dcb_init_port(dev, port); -} - void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) { struct ksz_device *dev = ds->priv; @@ -3477,28 +2953,9 @@ void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ksz_update_port_member(dev, port); } -static void ksz_port_teardown(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ8567_CHIP_ID: - case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: - case KSZ9896_CHIP_ID: - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - if (dsa_is_user_port(ds, port)) - ksz9477_port_acl_free(dev, port); - } -} - -static int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack) +int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) { if (flags.mask & ~(BR_LEARNING | BR_ISOLATED)) return -EINVAL; @@ -3506,9 +2963,9 @@ static int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, return 0; } -static int ksz_port_bridge_flags(struct dsa_switch *ds, int port, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack) +int ksz_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) { struct ksz_device *dev = ds->priv; struct ksz_port *p = &dev->ports[port]; @@ -3527,122 +2984,7 @@ static int ksz_port_bridge_flags(struct dsa_switch *ds, int port, return 0; } -static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, - int port, - enum dsa_tag_protocol mp) -{ - struct ksz_device *dev = ds->priv; - enum dsa_tag_protocol proto = DSA_TAG_PROTO_NONE; - - if (ksz_is_ksz87xx(dev) || ksz_is_8895_family(dev)) - proto = DSA_TAG_PROTO_KSZ8795; - - if (dev->chip_id == KSZ88X3_CHIP_ID || - dev->chip_id == KSZ8463_CHIP_ID || - dev->chip_id == KSZ8563_CHIP_ID || - dev->chip_id == KSZ9893_CHIP_ID || - dev->chip_id == KSZ9563_CHIP_ID) - proto = DSA_TAG_PROTO_KSZ9893; - - if (dev->chip_id == KSZ8567_CHIP_ID || - dev->chip_id == KSZ9477_CHIP_ID || - dev->chip_id == KSZ9896_CHIP_ID || - dev->chip_id == KSZ9897_CHIP_ID || - dev->chip_id == KSZ9567_CHIP_ID || - dev->chip_id == LAN9646_CHIP_ID) - proto = DSA_TAG_PROTO_KSZ9477; - - if (is_lan937x(dev)) - proto = DSA_TAG_PROTO_LAN937X; - - return proto; -} - -static int ksz_connect_tag_protocol(struct dsa_switch *ds, - enum dsa_tag_protocol proto) -{ - struct ksz_tagger_data *tagger_data; - - switch (proto) { - case DSA_TAG_PROTO_KSZ8795: - return 0; - case DSA_TAG_PROTO_KSZ9893: - case DSA_TAG_PROTO_KSZ9477: - case DSA_TAG_PROTO_LAN937X: - tagger_data = ksz_tagger_data(ds); - tagger_data->xmit_work_fn = ksz_port_deferred_xmit; - return 0; - default: - return -EPROTONOSUPPORT; - } -} - -static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, - bool flag, struct netlink_ext_ack *extack) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->vlan_filtering) - return -EOPNOTSUPP; - - return dev->dev_ops->vlan_filtering(dev, port, flag, extack); -} - -static int ksz_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->vlan_add) - return -EOPNOTSUPP; - - return dev->dev_ops->vlan_add(dev, port, vlan, extack); -} - -static int ksz_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->vlan_del) - return -EOPNOTSUPP; - - return dev->dev_ops->vlan_del(dev, port, vlan); -} - -static int ksz_port_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress, struct netlink_ext_ack *extack) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->mirror_add) - return -EOPNOTSUPP; - - return dev->dev_ops->mirror_add(dev, port, mirror, ingress, extack); -} - -static void ksz_port_mirror_del(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror) -{ - struct ksz_device *dev = ds->priv; - - if (dev->dev_ops->mirror_del) - dev->dev_ops->mirror_del(dev, port, mirror); -} - -static int ksz_change_mtu(struct dsa_switch *ds, int port, int mtu) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->dev_ops->change_mtu) - return -EOPNOTSUPP; - - return dev->dev_ops->change_mtu(dev, port, mtu); -} - -static int ksz_max_mtu(struct dsa_switch *ds, int port) +int ksz_max_mtu(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; @@ -3676,65 +3018,8 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) return -EOPNOTSUPP; } -/** - * ksz_support_eee - Determine Energy Efficient Ethernet (EEE) support for a - * port - * @ds: Pointer to the DSA switch structure - * @port: Port number to check - * - * This function also documents devices where EEE was initially advertised but - * later withdrawn due to reliability issues, as described in official errata - * documents. These devices are explicitly listed to record known limitations, - * even if there is no technical necessity for runtime checks. - * - * Returns: true if the internal PHY on the given port supports fully - * operational EEE, false otherwise. - */ -static bool ksz_support_eee(struct dsa_switch *ds, int port) -{ - struct ksz_device *dev = ds->priv; - - if (!dev->info->internal_phy[port]) - return false; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9893_CHIP_ID: - return true; - case KSZ8567_CHIP_ID: - /* KSZ8567R Errata DS80000752C Module 4 */ - case KSZ8765_CHIP_ID: - case KSZ8794_CHIP_ID: - case KSZ8795_CHIP_ID: - /* KSZ879x/KSZ877x/KSZ876x Errata DS80000687C Module 2 */ - case KSZ9477_CHIP_ID: - /* KSZ9477S Errata DS80000754A Module 4 */ - case KSZ9567_CHIP_ID: - /* KSZ9567S Errata DS80000756A Module 4 */ - case KSZ9896_CHIP_ID: - /* KSZ9896C Errata DS80000757A Module 3 */ - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - /* KSZ9897R Errata DS80000758C Module 4 */ - /* Energy Efficient Ethernet (EEE) feature select must be - * manually disabled - * The EEE feature is enabled by default, but it is not fully - * operational. It must be manually disabled through register - * controls. If not disabled, the PHY ports can auto-negotiate - * to enable EEE, and this feature can cause link drops when - * linked to another device supporting EEE. - * - * The same item appears in the errata for all switches above. - */ - break; - } - - return false; -} - -static int ksz_set_mac_eee(struct dsa_switch *ds, int port, - struct ethtool_keee *e) +int ksz_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_keee *e) { struct ksz_device *dev = ds->priv; @@ -3751,8 +3036,7 @@ static int ksz_set_mac_eee(struct dsa_switch *ds, int port, return 0; } -static void ksz_set_xmii(struct ksz_device *dev, int port, - phy_interface_t interface) +void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface) { const u8 *bitval = dev->info->xmii_ctrl1; struct ksz_port *p = &dev->ports[port]; @@ -3835,19 +3119,8 @@ phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit) return interface; } -static void ksz88x3_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct ksz_device *dev = dp->ds->priv; - - dev->ports[dp->index].manual_flow = !(state->pause & MLO_PAUSE_AN); -} - -static void ksz_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state) +bool ksz_phylink_need_config(struct phylink_config *config, + unsigned int mode) { struct dsa_port *dp = dsa_phylink_to_port(config); struct ksz_device *dev = dp->ds->priv; @@ -3855,21 +3128,30 @@ static void ksz_phylink_mac_config(struct phylink_config *config, /* Internal PHYs */ if (dev->info->internal_phy[port]) - return; + return false; /* No need to configure XMII control register when using SGMII. */ if (ksz_is_sgmii_port(dev, port)) - return; + return false; if (phylink_autoneg_inband(mode)) { dev_err(dev->dev, "In-band AN not supported!\n"); - return; + return false; } - ksz_set_xmii(dev, port, state->interface); + return true; +} - if (dev->dev_ops->setup_rgmii_delay) - dev->dev_ops->setup_rgmii_delay(dev, port); +void ksz_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + int port = dp->index; + + if (ksz_phylink_need_config(config, mode)) + ksz_set_xmii(dev, port, state->interface); } bool ksz_get_gbit(struct ksz_device *dev, int port) @@ -3890,106 +3172,6 @@ bool ksz_get_gbit(struct ksz_device *dev, int port) return gbit; } -static void ksz_set_gbit(struct ksz_device *dev, int port, bool gbit) -{ - const u8 *bitval = dev->info->xmii_ctrl1; - const u16 *regs = dev->info->regs; - u8 data8; - - ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); - - data8 &= ~P_GMII_1GBIT_M; - - if (gbit) - data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_1GBIT]); - else - data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_NOT_1GBIT]); - - /* Write the updated value */ - ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); -} - -static void ksz_set_100_10mbit(struct ksz_device *dev, int port, int speed) -{ - const u8 *bitval = dev->info->xmii_ctrl0; - const u16 *regs = dev->info->regs; - u8 data8; - - ksz_pread8(dev, port, regs[P_XMII_CTRL_0], &data8); - - data8 &= ~P_MII_100MBIT_M; - - if (speed == SPEED_100) - data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_100MBIT]); - else - data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_10MBIT]); - - /* Write the updated value */ - ksz_pwrite8(dev, port, regs[P_XMII_CTRL_0], data8); -} - -static void ksz_port_set_xmii_speed(struct ksz_device *dev, int port, int speed) -{ - if (speed == SPEED_1000) - ksz_set_gbit(dev, port, true); - else - ksz_set_gbit(dev, port, false); - - if (speed == SPEED_100 || speed == SPEED_10) - ksz_set_100_10mbit(dev, port, speed); -} - -static void ksz_duplex_flowctrl(struct ksz_device *dev, int port, int duplex, - bool tx_pause, bool rx_pause) -{ - const u8 *bitval = dev->info->xmii_ctrl0; - const u32 *masks = dev->info->masks; - const u16 *regs = dev->info->regs; - u8 mask; - u8 val; - - mask = P_MII_DUPLEX_M | masks[P_MII_TX_FLOW_CTRL] | - masks[P_MII_RX_FLOW_CTRL]; - - if (duplex == DUPLEX_FULL) - val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_FULL_DUPLEX]); - else - val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_HALF_DUPLEX]); - - if (tx_pause) - val |= masks[P_MII_TX_FLOW_CTRL]; - - if (rx_pause) - val |= masks[P_MII_RX_FLOW_CTRL]; - - ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val); -} - -static void ksz9477_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, - unsigned int mode, - phy_interface_t interface, - int speed, int duplex, bool tx_pause, - bool rx_pause) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct ksz_device *dev = dp->ds->priv; - int port = dp->index; - struct ksz_port *p; - - p = &dev->ports[port]; - - /* Internal PHYs */ - if (dev->info->internal_phy[port]) - return; - - p->speed = speed; - - ksz_port_set_xmii_speed(dev, port, speed); - - ksz_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause); -} - static int ksz_switch_detect(struct ksz_device *dev) { u8 id1, id2, id4; @@ -4089,48 +3271,6 @@ static int ksz_switch_detect(struct ksz_device *dev) return 0; } -static int ksz_cls_flower_add(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ8567_CHIP_ID: - case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: - case KSZ9896_CHIP_ID: - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - return ksz9477_cls_flower_add(ds, port, cls, ingress); - } - - return -EOPNOTSUPP; -} - -static int ksz_cls_flower_del(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress) -{ - struct ksz_device *dev = ds->priv; - - switch (dev->chip_id) { - case KSZ8563_CHIP_ID: - case KSZ8567_CHIP_ID: - case KSZ9477_CHIP_ID: - case KSZ9563_CHIP_ID: - case KSZ9567_CHIP_ID: - case KSZ9893_CHIP_ID: - case KSZ9896_CHIP_ID: - case KSZ9897_CHIP_ID: - case LAN9646_CHIP_ID: - return ksz9477_cls_flower_del(ds, port, cls, ingress); - } - - return -EOPNOTSUPP; -} - /* Bandwidth is calculated by idle slope/transmission speed. Then the Bandwidth * is converted to Hex-decimal using the successive multiplication method. On * every step, integer part is taken and decimal part is carry forwarded. @@ -4514,8 +3654,8 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, return -EOPNOTSUPP; } -static int ksz_setup_tc(struct dsa_switch *ds, int port, - enum tc_setup_type type, void *type_data) +int ksz_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data) { switch (type) { case TC_SETUP_QDISC_CBS: @@ -4574,17 +3714,14 @@ int ksz_handle_wake_reason(struct ksz_device *dev, int port) * If enabled and supported, it sets the supported and active WoL * flags. */ -static void ksz_get_wol(struct dsa_switch *ds, int port, - struct ethtool_wolinfo *wol) +void ksz_get_wol(struct dsa_switch *ds, int port, + struct ethtool_wolinfo *wol) { struct ksz_device *dev = ds->priv; const u16 *regs = dev->info->regs; u8 pme_ctrl; int ret; - if (!is_ksz9477(dev) && !ksz_is_ksz87xx(dev)) - return; - if (!dev->wakeup_source) return; @@ -4622,8 +3759,8 @@ static void ksz_get_wol(struct dsa_switch *ds, int port, * * Return: 0 on success, or other error codes on failure. */ -static int ksz_set_wol(struct dsa_switch *ds, int port, - struct ethtool_wolinfo *wol) +int ksz_set_wol(struct dsa_switch *ds, int port, + struct ethtool_wolinfo *wol) { u8 pme_ctrl = 0, pme_ctrl_old = 0; struct ksz_device *dev = ds->priv; @@ -4635,9 +3772,6 @@ static int ksz_set_wol(struct dsa_switch *ds, int port, if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC)) return -EINVAL; - if (!is_ksz9477(dev) && !ksz_is_ksz87xx(dev)) - return -EOPNOTSUPP; - if (!dev->wakeup_source) return -EOPNOTSUPP; @@ -4689,25 +3823,21 @@ static int ksz_set_wol(struct dsa_switch *ds, int port, * ksz_wol_pre_shutdown - Prepares the switch device for shutdown while * considering Wake-on-LAN (WoL) settings. * @dev: The switch device structure. - * @wol_enabled: Pointer to a boolean which will be set to true if WoL is - * enabled on any port. * * This function prepares the switch device for a safe shutdown while taking - * into account the Wake-on-LAN (WoL) settings on the user ports. It updates - * the wol_enabled flag accordingly to reflect whether WoL is active on any - * port. + * into account the Wake-on-LAN (WoL) settings on the user ports. */ -static void ksz_wol_pre_shutdown(struct ksz_device *dev, bool *wol_enabled) +static void ksz_wol_pre_shutdown(struct ksz_device *dev) { const struct ksz_dev_ops *ops = dev->dev_ops; const u16 *regs = dev->info->regs; + struct dsa_switch *ds = dev->ds; u8 pme_pin_en = PME_ENABLE; + bool wol_enabled = false; struct dsa_port *dp; int ret; - *wol_enabled = false; - - if (!is_ksz9477(dev) && !ksz_is_ksz87xx(dev)) + if (!ds->ops->set_wol) return; if (!dev->wakeup_source) @@ -4719,7 +3849,7 @@ static void ksz_wol_pre_shutdown(struct ksz_device *dev, bool *wol_enabled) ret = ops->pme_pread8(dev, dp->index, regs[REG_PORT_PME_CTRL], &pme_ctrl); if (!ret && pme_ctrl) - *wol_enabled = true; + wol_enabled = true; /* make sure there are no pending wake events which would * prevent the device from going to sleep/shutdown. @@ -4728,7 +3858,7 @@ static void ksz_wol_pre_shutdown(struct ksz_device *dev, bool *wol_enabled) } /* Now we are save to enable PME pin. */ - if (*wol_enabled) { + if (wol_enabled) { if (dev->pme_active_high) pme_pin_en |= PME_POLARITY; ops->pme_write8(dev, regs[REG_SW_PME_CTRL], pme_pin_en); @@ -4737,8 +3867,8 @@ static void ksz_wol_pre_shutdown(struct ksz_device *dev, bool *wol_enabled) } } -static int ksz_port_set_mac_address(struct dsa_switch *ds, int port, - const unsigned char *addr) +int ksz_port_set_mac_address(struct dsa_switch *ds, int port, + const unsigned char *addr) { struct dsa_port *dp = dsa_to_port(ds, port); struct ethtool_wolinfo wol; @@ -4755,7 +3885,8 @@ static int ksz_port_set_mac_address(struct dsa_switch *ds, int port, */ wol.wolopts = 0; - ksz_get_wol(ds, dp->index, &wol); + if (ds->ops->get_wol) + ds->ops->get_wol(ds, dp->index, &wol); if (wol.wolopts & WAKE_MAGIC) { dev_err(ds->dev, "Cannot change MAC address on port %d with active Wake on Magic Packet\n", @@ -4890,73 +4021,7 @@ void ksz_switch_macaddr_put(struct dsa_switch *ds) kfree(switch_macaddr); } -static int ksz_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr, - struct netlink_ext_ack *extack) -{ - struct ksz_device *dev = ds->priv; - enum hsr_version ver; - int ret; - - ret = hsr_get_version(hsr, &ver); - if (ret) - return ret; - - if (dev->chip_id != KSZ9477_CHIP_ID) { - NL_SET_ERR_MSG_MOD(extack, "Chip does not support HSR offload"); - return -EOPNOTSUPP; - } - - /* KSZ9477 can support HW offloading of only 1 HSR device */ - if (dev->hsr_dev && hsr != dev->hsr_dev) { - NL_SET_ERR_MSG_MOD(extack, "Offload supported for a single HSR"); - return -EOPNOTSUPP; - } - - /* KSZ9477 only supports HSR v0 and v1 */ - if (!(ver == HSR_V0 || ver == HSR_V1)) { - NL_SET_ERR_MSG_MOD(extack, "Only HSR v0 and v1 supported"); - return -EOPNOTSUPP; - } - - /* KSZ9477 can only perform HSR offloading for up to two ports */ - if (hweight8(dev->hsr_ports) >= 2) { - NL_SET_ERR_MSG_MOD(extack, - "Cannot offload more than two ports - using software HSR"); - return -EOPNOTSUPP; - } - - /* Self MAC address filtering, to avoid frames traversing - * the HSR ring more than once. - */ - ret = ksz_switch_macaddr_get(ds, port, extack); - if (ret) - return ret; - - ksz9477_hsr_join(ds, port, hsr); - dev->hsr_dev = hsr; - dev->hsr_ports |= BIT(port); - - return 0; -} - -static int ksz_hsr_leave(struct dsa_switch *ds, int port, - struct net_device *hsr) -{ - struct ksz_device *dev = ds->priv; - - WARN_ON(dev->chip_id != KSZ9477_CHIP_ID); - - ksz9477_hsr_leave(ds, port, hsr); - dev->hsr_ports &= ~BIT(port); - if (!dev->hsr_ports) - dev->hsr_dev = NULL; - - ksz_switch_macaddr_put(ds); - - return 0; -} - -static int ksz_suspend(struct dsa_switch *ds) +int ksz_suspend(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -4964,7 +4029,7 @@ static int ksz_suspend(struct dsa_switch *ds) return 0; } -static int ksz_resume(struct dsa_switch *ds) +int ksz_resume(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; @@ -4973,68 +4038,9 @@ static int ksz_resume(struct dsa_switch *ds) return 0; } -static const struct dsa_switch_ops ksz_switch_ops = { - .get_tag_protocol = ksz_get_tag_protocol, - .connect_tag_protocol = ksz_connect_tag_protocol, - .get_phy_flags = ksz_get_phy_flags, - .setup = ksz_setup, - .teardown = ksz_teardown, - .phy_read = ksz_phy_read16, - .phy_write = ksz_phy_write16, - .phylink_get_caps = ksz_phylink_get_caps, - .port_setup = ksz_port_setup, - .set_ageing_time = ksz_set_ageing_time, - .get_strings = ksz_get_strings, - .get_ethtool_stats = ksz_get_ethtool_stats, - .get_sset_count = ksz_sset_count, - .port_bridge_join = ksz_port_bridge_join, - .port_bridge_leave = ksz_port_bridge_leave, - .port_hsr_join = ksz_hsr_join, - .port_hsr_leave = ksz_hsr_leave, - .port_set_mac_address = ksz_port_set_mac_address, - .port_stp_state_set = ksz_port_stp_state_set, - .port_teardown = ksz_port_teardown, - .port_pre_bridge_flags = ksz_port_pre_bridge_flags, - .port_bridge_flags = ksz_port_bridge_flags, - .port_fast_age = ksz_port_fast_age, - .port_vlan_filtering = ksz_port_vlan_filtering, - .port_vlan_add = ksz_port_vlan_add, - .port_vlan_del = ksz_port_vlan_del, - .port_fdb_dump = ksz_port_fdb_dump, - .port_fdb_add = ksz_port_fdb_add, - .port_fdb_del = ksz_port_fdb_del, - .port_mdb_add = ksz_port_mdb_add, - .port_mdb_del = ksz_port_mdb_del, - .port_mirror_add = ksz_port_mirror_add, - .port_mirror_del = ksz_port_mirror_del, - .get_stats64 = ksz_get_stats64, - .get_pause_stats = ksz_get_pause_stats, - .port_change_mtu = ksz_change_mtu, - .port_max_mtu = ksz_max_mtu, - .get_wol = ksz_get_wol, - .set_wol = ksz_set_wol, - .suspend = ksz_suspend, - .resume = ksz_resume, - .get_ts_info = ksz_get_ts_info, - .port_hwtstamp_get = ksz_hwtstamp_get, - .port_hwtstamp_set = ksz_hwtstamp_set, - .port_txtstamp = ksz_port_txtstamp, - .port_rxtstamp = ksz_port_rxtstamp, - .cls_flower_add = ksz_cls_flower_add, - .cls_flower_del = ksz_cls_flower_del, - .port_setup_tc = ksz_setup_tc, - .support_eee = ksz_support_eee, - .set_mac_eee = ksz_set_mac_eee, - .port_get_default_prio = ksz_port_get_default_prio, - .port_set_default_prio = ksz_port_set_default_prio, - .port_get_dscp_prio = ksz_port_get_dscp_prio, - .port_add_dscp_prio = ksz_port_add_dscp_prio, - .port_del_dscp_prio = ksz_port_del_dscp_prio, - .port_get_apptrust = ksz_port_get_apptrust, - .port_set_apptrust = ksz_port_set_apptrust, -}; - -struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) +struct ksz_device *ksz_switch_alloc(struct device *base, + const struct ksz_chip_data *chip, + void *priv) { struct dsa_switch *ds; struct ksz_device *swdev; @@ -5045,7 +4051,7 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) ds->dev = base; ds->num_ports = DSA_MAX_PORTS; - ds->ops = &ksz_switch_ops; + ds->ops = chip->switch_ops; swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); if (!swdev) @@ -5066,20 +4072,12 @@ EXPORT_SYMBOL(ksz_switch_alloc); * @dev: The switch device structure. * * This function is responsible for initiating a shutdown sequence for the - * switch device. It invokes the reset operation defined in the device - * operations, if available, to reset the switch. Subsequently, it calls the - * DSA framework's shutdown function to ensure a proper shutdown of the DSA - * switch. + * switch device. Subsequently, it calls the DSA framework's shutdown function + * to ensure a proper shutdown of the DSA switch. */ void ksz_switch_shutdown(struct ksz_device *dev) { - bool wol_enabled = false; - - ksz_wol_pre_shutdown(dev, &wol_enabled); - - if (dev->dev_ops->reset && !wol_enabled) - dev->dev_ops->reset(dev); - + ksz_wol_pre_shutdown(dev); dsa_switch_shutdown(dev->ds); } EXPORT_SYMBOL(ksz_switch_shutdown); @@ -5293,7 +4291,7 @@ static int ksz88x3_drive_strength_write(struct ksz_device *dev, * * Return: 0 on success, error code otherwise */ -static int ksz_parse_drive_strength(struct ksz_device *dev) +int ksz_parse_drive_strength(struct ksz_device *dev) { struct ksz_driver_strength_prop of_props[] = { [KSZ_DRIVER_STRENGTH_HI] = { @@ -5529,10 +4527,8 @@ int ksz_switch_register(struct ksz_device *dev) } ret = dsa_register_switch(dev->ds); - if (ret) { - dev->dev_ops->exit(dev); + if (ret) return ret; - } /* Read MIB counters every 30 seconds to avoid overflow. */ dev->mib_read_interval = msecs_to_jiffies(5000); @@ -5552,12 +4548,7 @@ void ksz_switch_remove(struct ksz_device *dev) cancel_delayed_work_sync(&dev->mib_read); } - dev->dev_ops->exit(dev); dsa_unregister_switch(dev->ds); - - if (dev->reset_gpio) - gpiod_set_value_cansleep(dev->reset_gpio, 1); - } EXPORT_SYMBOL(ksz_switch_remove); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 18f13ee9c7b6..b4a5673ba365 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -73,6 +73,7 @@ struct ksz_chip_data { */ bool phy_side_mdio_supported; const struct ksz_dev_ops *ops; + const struct dsa_switch_ops *switch_ops; const struct phylink_mac_ops *phylink_mac_ops; bool phy_errata_9477; bool ksz87xx_eee_link_erratum; @@ -221,6 +222,10 @@ struct ksz_device { * the switch’s internal PHYs, bypassing the main SPI interface. */ struct mii_bus *parent_mdio_bus; + + /* KSZ87xx low-loss tuning state */ + u8 lpf_bw; /* KSZ87XX_PHY_LPF_* */ + u8 eq_init; /* DSP EQ initial value */ }; /* List of supported models */ @@ -358,85 +363,14 @@ struct alu_struct { }; struct ksz_dev_ops { - int (*setup)(struct dsa_switch *ds); - void (*teardown)(struct dsa_switch *ds); u32 (*get_port_addr)(int port, int offset); void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member); - void (*flush_dyn_mac_table)(struct ksz_device *dev, int port); - void (*port_cleanup)(struct ksz_device *dev, int port); - void (*port_setup)(struct ksz_device *dev, int port, bool cpu_port); - int (*set_ageing_time)(struct ksz_device *dev, unsigned int msecs); - /** - * @mdio_bus_preinit: Function pointer to pre-initialize the MDIO bus - * for accessing PHYs. - * @dev: Pointer to device structure. - * @side_mdio: Boolean indicating if the PHYs are accessed over a side - * MDIO bus. - * - * This function pointer is used to configure the MDIO bus for PHY - * access before initiating regular PHY operations. It enables either - * SPI/I2C or side MDIO access modes by unlocking necessary registers - * and setting up access permissions for the selected mode. - * - * Return: - * - 0 on success. - * - Negative error code on failure. - */ - int (*mdio_bus_preinit)(struct ksz_device *dev, bool side_mdio); - - /** - * @create_phy_addr_map: Function pointer to create a port-to-PHY - * address map. - * @dev: Pointer to device structure. - * @side_mdio: Boolean indicating if the PHYs are accessed over a side - * MDIO bus. - * - * This function pointer is responsible for mapping switch ports to PHY - * addresses according to the configured access mode (SPI or side MDIO) - * and the device’s strap configuration. The mapping setup may vary - * depending on the chip variant and configuration. Ensures the correct - * address mapping for PHY communication. - * - * Return: - * - 0 on success. - * - Negative error code on failure (e.g., invalid configuration). - */ - int (*create_phy_addr_map)(struct ksz_device *dev, bool side_mdio); - int (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); - int (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val); void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr, 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); - int (*vlan_filtering)(struct ksz_device *dev, int port, - bool flag, struct netlink_ext_ack *extack); - int (*vlan_add)(struct ksz_device *dev, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack); - int (*vlan_del)(struct ksz_device *dev, int port, - const struct switchdev_obj_port_vlan *vlan); - int (*mirror_add)(struct ksz_device *dev, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress, struct netlink_ext_ack *extack); - void (*mirror_del)(struct ksz_device *dev, int port, - struct dsa_mall_mirror_tc_entry *mirror); - int (*fdb_add)(struct ksz_device *dev, int port, - const unsigned char *addr, u16 vid, struct dsa_db db); - int (*fdb_del)(struct ksz_device *dev, int port, - const unsigned char *addr, u16 vid, struct dsa_db db); - int (*fdb_dump)(struct ksz_device *dev, int port, - dsa_fdb_dump_cb_t *cb, void *data); - int (*mdb_add)(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db); - int (*mdb_del)(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db); - void (*get_caps)(struct ksz_device *dev, int port, - struct phylink_config *config); - int (*change_mtu)(struct ksz_device *dev, int port, int mtu); int (*pme_write8)(struct ksz_device *dev, u32 reg, u8 value); int (*pme_pread8)(struct ksz_device *dev, int port, int offset, u8 *data); @@ -444,28 +378,20 @@ struct ksz_dev_ops { u8 data); void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze); void (*port_init_cnt)(struct ksz_device *dev, int port); - void (*phylink_mac_link_up)(struct ksz_device *dev, int port, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phydev, int speed, - int duplex, bool tx_pause, bool rx_pause); - void (*setup_rgmii_delay)(struct ksz_device *dev, int port); int (*tc_cbs_set_cinc)(struct ksz_device *dev, int port, u32 val); - void (*config_cpu_port)(struct dsa_switch *ds); - int (*enable_stp_addr)(struct ksz_device *dev); - int (*reset)(struct ksz_device *dev); int (*init)(struct ksz_device *dev); - void (*exit)(struct ksz_device *dev); - - int (*pcs_create)(struct ksz_device *dev); }; -struct ksz_device *ksz_switch_alloc(struct device *base, void *priv); +struct ksz_device *ksz_switch_alloc(struct device *base, + const struct ksz_chip_data *chip, + void *priv); int ksz_switch_register(struct ksz_device *dev); void ksz_switch_remove(struct ksz_device *dev); int ksz_switch_suspend(struct device *dev); int ksz_switch_resume(struct device *dev); +void ksz_teardown(struct dsa_switch *ds); + void ksz_init_mib_timer(struct ksz_device *dev); bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port); void ksz_r_mib_stats64(struct ksz_device *dev, int port); @@ -480,6 +406,74 @@ void ksz_switch_macaddr_put(struct dsa_switch *ds); void ksz_switch_shutdown(struct ksz_device *dev); int ksz_handle_wake_reason(struct ksz_device *dev, int port); +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); +void ksz_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s); +void ksz_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats); +void ksz_get_strings(struct dsa_switch *ds, int port, + u32 stringset, uint8_t *buf); + +int ksz_port_bridge_join(struct dsa_switch *ds, int port, + 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); +int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); +int ksz_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); + +void ksz_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config); +void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config); +int ksz_phylink_mac_enable_tx_lpi(struct phylink_config *config, + u32 timer, bool tx_clock_stop); +void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface); +bool ksz_phylink_need_config(struct phylink_config *config, unsigned int mode); +void ksz_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state); +void ksz_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface); + +int ksz_max_mtu(struct dsa_switch *ds, int port); + +int ksz_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_keee *e); + +int ksz_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data); + +void ksz_get_wol(struct dsa_switch *ds, int port, + struct ethtool_wolinfo *wol); +int ksz_set_wol(struct dsa_switch *ds, int port, + struct ethtool_wolinfo *wol); +int ksz_port_set_mac_address(struct dsa_switch *ds, int port, + const unsigned char *addr); + +int ksz_suspend(struct dsa_switch *ds); +int ksz_resume(struct dsa_switch *ds); + +int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus, + struct device_node *mdio_np); +int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum); +int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val); +int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum); +int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val); +int ksz_mdio_register(struct ksz_device *dev); +int ksz_pirq_setup(struct ksz_device *dev, u8 p); +int ksz_girq_setup(struct ksz_device *dev); +void ksz_irq_free(struct ksz_irq *kirq); +int ksz_parse_drive_strength(struct ksz_device *dev); +int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port); + /* Common register access functions */ static inline struct regmap *ksz_regmap_8(struct ksz_device *dev) { diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index d8001734b057..373e9054947c 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -143,14 +143,14 @@ static int ksz_spi_probe(struct spi_device *spi) struct ksz_device *dev; int i, ret = 0; - dev = ksz_switch_alloc(&spi->dev, spi); - if (!dev) - return -ENOMEM; - chip = device_get_match_data(ddev); if (!chip) return -EINVAL; + dev = ksz_switch_alloc(&spi->dev, chip, spi); + if (!dev) + return -ENOMEM; + /* Save chip id to do special initialization when probing. */ dev->chip_id = chip->chip_id; if (chip->chip_id == KSZ88X3_CHIP_ID) diff --git a/drivers/net/dsa/microchip/lan937x.h b/drivers/net/dsa/microchip/lan937x.h index df13ebbd356f..d12483f9081d 100644 --- a/drivers/net/dsa/microchip/lan937x.h +++ b/drivers/net/dsa/microchip/lan937x.h @@ -6,21 +6,8 @@ #ifndef __LAN937X_CFG_H #define __LAN937X_CFG_H -int lan937x_reset_switch(struct ksz_device *dev); -int lan937x_setup(struct dsa_switch *ds); -void lan937x_teardown(struct dsa_switch *ds); -void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port); -void lan937x_config_cpu_port(struct dsa_switch *ds); -int lan937x_switch_init(struct ksz_device *dev); -void lan937x_switch_exit(struct ksz_device *dev); -int lan937x_mdio_bus_preinit(struct ksz_device *dev, bool side_mdio); -int lan937x_create_phy_addr_map(struct ksz_device *dev, bool side_mdio); -int lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data); -int lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val); -int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu); -void lan937x_phylink_get_caps(struct ksz_device *dev, int port, - struct phylink_config *config); -void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port); -int lan937x_set_ageing_time(struct ksz_device *dev, unsigned int msecs); -int lan937x_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val); +extern const struct ksz_dev_ops lan937x_dev_ops; +extern const struct phylink_mac_ops lan937x_phylink_mac_ops; +extern const struct dsa_switch_ops lan937x_switch_ops; + #endif diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 5a1496fff445..8eb5337b0c10 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -2,10 +2,12 @@ /* Microchip LAN937X switch driver main logic * Copyright (C) 2019-2024 Microchip Technology Inc. */ +#include #include #include #include #include +#include #include #include #include @@ -15,6 +17,7 @@ #include "lan937x_reg.h" #include "ksz_common.h" +#include "ksz_dcb.h" #include "ksz9477.h" #include "lan937x.h" @@ -137,7 +140,7 @@ static int lan937x_port_cfg(struct ksz_device *dev, int port, int offset, * * Return: 0 on success, error code on failure. */ -int lan937x_create_phy_addr_map(struct ksz_device *dev, bool side_mdio) +static int lan937x_create_phy_addr_map(struct ksz_device *dev, bool side_mdio) { static const u8 *phy_addr_map; u32 strap_val; @@ -221,7 +224,7 @@ int lan937x_create_phy_addr_map(struct ksz_device *dev, bool side_mdio) * * Return: 0 on success, error code on failure. */ -int lan937x_mdio_bus_preinit(struct ksz_device *dev, bool side_mdio) +static int lan937x_mdio_bus_preinit(struct ksz_device *dev, bool side_mdio) { u16 data16; int ret; @@ -332,17 +335,32 @@ static int lan937x_internal_phy_read(struct ksz_device *dev, int addr, int reg, return ksz_read16(dev, REG_VPHY_IND_DATA__2, val); } -int lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data) +static int lan937x_phy_read16(struct dsa_switch *ds, int addr, int reg) { - return lan937x_internal_phy_read(dev, addr, reg, data); + struct ksz_device *dev = ds->priv; + u16 val = 0xffff; + int ret; + + ret = lan937x_internal_phy_read(dev, addr, reg, &val); + if (ret) + return ret; + + return val; } -int lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val) +static int lan937x_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) { - return lan937x_internal_phy_write(dev, addr, reg, val); + struct ksz_device *dev = ds->priv; + int ret; + + ret = lan937x_internal_phy_write(dev, addr, reg, val); + if (ret) + return ret; + + return 0; } -int lan937x_reset_switch(struct ksz_device *dev) +static int lan937x_reset_switch(struct ksz_device *dev) { u32 data32; int ret; @@ -373,7 +391,7 @@ int lan937x_reset_switch(struct ksz_device *dev) return ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32); } -void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port) +static void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port) { const u32 *masks = dev->info->masks; const u16 *regs = dev->info->regs; @@ -409,7 +427,24 @@ void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port) dev->dev_ops->cfg_port_member(dev, port, member); } -void lan937x_config_cpu_port(struct dsa_switch *ds) +static int lan937x_dsa_port_setup(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + int ret; + + if (!dsa_is_user_port(ds, port)) + return 0; + + lan937x_port_setup(dev, port, false); + + ret = ksz9477_set_default_prio_queue_mapping(dev, port); + if (ret) + return ret; + + return ksz_dcb_init_port(dev, port); +} + +static void lan937x_config_cpu_port(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; struct dsa_port *dp; @@ -428,9 +463,9 @@ void lan937x_config_cpu_port(struct dsa_switch *ds) } } -int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu) +static int lan937x_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { - struct dsa_switch *ds = dev->ds; + struct ksz_device *dev = ds->priv; int ret; new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; @@ -459,8 +494,9 @@ int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu) return 0; } -int lan937x_set_ageing_time(struct ksz_device *dev, unsigned int msecs) +static int lan937x_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) { + struct ksz_device *dev = ds->priv; u8 data, mult, value8; bool in_msec = false; u32 max_val, value; @@ -572,9 +608,11 @@ static void lan937x_set_rgmii_rx_delay(struct ksz_device *dev, int port) lan937x_set_tune_adj(dev, port, REG_PORT_XMII_CTRL_4, val); } -void lan937x_phylink_get_caps(struct ksz_device *dev, int port, - struct phylink_config *config) +static void lan937x_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { + struct ksz_device *dev = ds->priv; + config->mac_capabilities = MAC_100FD; if (dev->info->supports_rgmii[port]) { @@ -585,9 +623,11 @@ void lan937x_phylink_get_caps(struct ksz_device *dev, int port, config->mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_100HD | MAC_10; } + + ksz_phylink_get_caps(ds, port, config); } -void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) +static void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) { struct ksz_port *p = &dev->ports[port]; @@ -604,23 +644,173 @@ void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) } } -int lan937x_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) +static void lan937x_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct ksz_device *dev = dp->ds->priv; + int port = dp->index; + + if (ksz_phylink_need_config(config, mode)) { + ksz_set_xmii(dev, port, state->interface); + lan937x_setup_rgmii_delay(dev, port); + } +} + +static int lan937x_tc_cbs_set_cinc(struct ksz_device *dev, int port, u32 val) { return ksz_pwrite32(dev, port, REG_PORT_MTI_CREDIT_INCREMENT, val); } -int lan937x_switch_init(struct ksz_device *dev) +static int lan937x_switch_init(struct ksz_device *dev) { dev->port_mask = (1 << dev->info->port_cnt) - 1; return 0; } -int lan937x_setup(struct dsa_switch *ds) +/** + * lan937x_mdio_register - Register and configure the MDIO bus for the LAN937x. + * @dev: Pointer to the KSZ device structure. + * + * This function sets up and registers an MDIO bus for a LAN937x switch, + * allowing access to its internal PHYs. If the device supports side MDIO, + * the function will configure the external MDIO controller specified by the + * "mdio-parent-bus" device tree property to directly manage internal PHYs. + * Otherwise, SPI or I2C access is set up for PHY access. + * + * Return: 0 on success, or a negative error code on failure. + */ +static int lan937x_mdio_register(struct ksz_device *dev) +{ + struct device_node *parent_bus_node; + struct mii_bus *parent_bus = NULL; + struct dsa_switch *ds = dev->ds; + struct device_node *mdio_np; + struct mii_bus *bus; + int ret; + + mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio"); + if (!mdio_np) + return 0; + + parent_bus_node = of_parse_phandle(mdio_np, "mdio-parent-bus", 0); + if (parent_bus_node && !dev->info->phy_side_mdio_supported) { + dev_err(dev->dev, "Side MDIO bus is not supported for this HW, ignoring 'mdio-parent-bus' property.\n"); + ret = -EINVAL; + + goto put_mdio_node; + } else if (parent_bus_node) { + parent_bus = of_mdio_find_bus(parent_bus_node); + if (!parent_bus) { + ret = -EPROBE_DEFER; + + goto put_mdio_node; + } + + dev->parent_mdio_bus = parent_bus; + } + + bus = devm_mdiobus_alloc(ds->dev); + if (!bus) { + ret = -ENOMEM; + goto put_mdio_node; + } + + ret = lan937x_mdio_bus_preinit(dev, !!parent_bus); + if (ret) + goto put_mdio_node; + + ret = lan937x_create_phy_addr_map(dev, !!parent_bus); + if (ret) + goto put_mdio_node; + + bus->priv = dev; + if (parent_bus) { + bus->read = ksz_parent_mdio_read; + bus->write = ksz_parent_mdio_write; + bus->name = "KSZ side MDIO"; + snprintf(bus->id, MII_BUS_ID_SIZE, "ksz-side-mdio-%d", + ds->index); + } else { + bus->read = ksz_sw_mdio_read; + bus->write = ksz_sw_mdio_write; + bus->name = "ksz user smi"; + if (ds->dst->index != 0) + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d-%d", + ds->dst->index, ds->index); + else + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index); + } + + ret = ksz_parse_dt_phy_config(dev, bus, mdio_np); + if (ret) + goto put_mdio_node; + + ds->phys_mii_mask = bus->phy_mask; + bus->parent = ds->dev; + + ds->user_mii_bus = bus; + + ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np); + if (ret) + dev_err(ds->dev, "unable to register MDIO bus %s\n", + bus->id); + +put_mdio_node: + of_node_put(mdio_np); + of_node_put(parent_bus_node); + + return ret; +} + +static int lan937x_setup(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; + u16 storm_mask, storm_rate; + struct dsa_port *dp; + struct ksz_port *p; + const u16 *regs; int ret; + regs = dev->info->regs; + + dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), + dev->info->num_vlans, GFP_KERNEL); + if (!dev->vlan_cache) + return -ENOMEM; + + ret = lan937x_reset_switch(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); + return ret; + } + + ret = ksz_parse_drive_strength(dev); + if (ret) + return ret; + + /* set broadcast storm protection 10% rate */ + storm_mask = BROADCAST_STORM_RATE; + storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100; + regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL], + storm_mask, storm_rate); + + lan937x_config_cpu_port(ds); + + ksz9477_enable_stp_addr(dev); + + ds->num_tx_queues = dev->info->num_tx_queues; + + regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL], + MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE); + + ksz_init_mib_timer(dev); + + ds->configure_vlan_while_not_filtering = false; + ds->dscp_prio_mapping_is_global = true; + /* The VLAN aware is a global setting. Mixed vlan * filterings are not supported. */ @@ -652,20 +842,165 @@ int lan937x_setup(struct dsa_switch *ds) return ret; /* Disable global VPHY support. Related to CPU interface only? */ - return ksz_rmw32(dev, REG_SW_CFG_STRAP_OVR, SW_VPHY_DISABLE, - SW_VPHY_DISABLE); + ret = ksz_rmw32(dev, REG_SW_CFG_STRAP_OVR, SW_VPHY_DISABLE, + SW_VPHY_DISABLE); + if (ret < 0) + return ret; + + /* Start with learning disabled on standalone user ports, and enabled + * on the CPU port. In lack of other finer mechanisms, learning on the + * CPU port will avoid flooding bridge local addresses on the network + * in some cases. + */ + p = &dev->ports[dev->cpu_port]; + p->learning = true; + + if (dev->irq > 0) { + ret = ksz_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = ksz_pirq_setup(dev, dp->index); + if (ret) + goto port_release; + + ret = ksz_ptp_irq_setup(ds, dp->index); + if (ret) + goto pirq_release; + } + } + + ret = ksz_ptp_clock_register(ds); + if (ret) { + dev_err(dev->dev, "Failed to register PTP clock: %d\n", + ret); + goto port_release; + } + + ret = lan937x_mdio_register(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to register the mdio"); + goto out_ptp_clock_unregister; + } + + ret = ksz_dcb_init(dev); + if (ret) + goto out_ptp_clock_unregister; + + /* start switch */ + regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL], + SW_START, SW_START); + + return 0; + +out_ptp_clock_unregister: + ksz_ptp_clock_unregister(ds); +port_release: + if (dev->irq > 0) { + dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) { + ksz_ptp_irq_free(ds, dp->index); +pirq_release: + ksz_irq_free(&dev->ports[dp->index].pirq); + } + ksz_irq_free(&dev->girq); + } + + return ret; } -void lan937x_teardown(struct dsa_switch *ds) +static enum dsa_tag_protocol lan937x_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) { - + return DSA_TAG_PROTO_LAN937X; } -void lan937x_switch_exit(struct ksz_device *dev) +static int lan937x_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) { - lan937x_reset_switch(dev); + struct ksz_tagger_data *tagger_data; + + if (proto != DSA_TAG_PROTO_LAN937X) + return -EPROTONOSUPPORT; + + tagger_data = ksz_tagger_data(ds); + tagger_data->xmit_work_fn = ksz_port_deferred_xmit; + + return 0; } +const struct phylink_mac_ops lan937x_phylink_mac_ops = { + .mac_config = lan937x_phylink_mac_config, + .mac_link_down = ksz_phylink_mac_link_down, + .mac_link_up = ksz9477_phylink_mac_link_up, + .mac_disable_tx_lpi = ksz_phylink_mac_disable_tx_lpi, + .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, +}; + +const struct ksz_dev_ops lan937x_dev_ops = { + .get_port_addr = ksz9477_get_port_addr, + .cfg_port_member = ksz9477_cfg_port_member, + .r_mib_cnt = ksz9477_r_mib_cnt, + .r_mib_pkt = ksz9477_r_mib_pkt, + .r_mib_stat64 = ksz_r_mib_stats64, + .freeze_mib = ksz9477_freeze_mib, + .port_init_cnt = ksz9477_port_init_cnt, + .tc_cbs_set_cinc = lan937x_tc_cbs_set_cinc, + .init = lan937x_switch_init, +}; + +const struct dsa_switch_ops lan937x_switch_ops = { + .get_tag_protocol = lan937x_get_tag_protocol, + .connect_tag_protocol = lan937x_connect_tag_protocol, + .setup = lan937x_setup, + .teardown = ksz_teardown, + .phy_read = lan937x_phy_read16, + .phy_write = lan937x_phy_write16, + .phylink_get_caps = lan937x_phylink_get_caps, + .port_setup = lan937x_dsa_port_setup, + .set_ageing_time = lan937x_set_ageing_time, + .get_strings = ksz_get_strings, + .get_ethtool_stats = ksz_get_ethtool_stats, + .get_sset_count = ksz_sset_count, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .port_set_mac_address = ksz_port_set_mac_address, + .port_stp_state_set = ksz_port_stp_state_set, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, + .port_fast_age = ksz9477_flush_dyn_mac_table, + .port_vlan_filtering = ksz9477_port_vlan_filtering, + .port_vlan_add = ksz9477_port_vlan_add, + .port_vlan_del = ksz9477_port_vlan_del, + .port_fdb_dump = ksz9477_fdb_dump, + .port_fdb_add = ksz9477_fdb_add, + .port_fdb_del = ksz9477_fdb_del, + .port_mdb_add = ksz9477_mdb_add, + .port_mdb_del = ksz9477_mdb_del, + .port_mirror_add = ksz9477_port_mirror_add, + .port_mirror_del = ksz9477_port_mirror_del, + .get_stats64 = ksz_get_stats64, + .get_pause_stats = ksz_get_pause_stats, + .port_change_mtu = lan937x_change_mtu, + .port_max_mtu = ksz_max_mtu, + .suspend = ksz_suspend, + .resume = ksz_resume, + .get_ts_info = ksz_get_ts_info, + .port_hwtstamp_get = ksz_hwtstamp_get, + .port_hwtstamp_set = ksz_hwtstamp_set, + .port_txtstamp = ksz_port_txtstamp, + .port_rxtstamp = ksz_port_rxtstamp, + .port_setup_tc = ksz_setup_tc, + .port_get_default_prio = ksz_port_get_default_prio, + .port_set_default_prio = ksz_port_set_default_prio, + .port_get_dscp_prio = ksz_port_get_dscp_prio, + .port_add_dscp_prio = ksz_port_add_dscp_prio, + .port_del_dscp_prio = ksz_port_del_dscp_prio, + .port_get_apptrust = ksz_port_get_apptrust, + .port_set_apptrust = ksz_port_set_apptrust, +}; + MODULE_AUTHOR("Arun Ramadoss "); MODULE_DESCRIPTION("Microchip LAN937x Series Switch DSA Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8ca5fd40df92..80b877c74513 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -685,9 +685,6 @@ static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, /* Port 4 supports automedia if the serdes is associated with it. */ if (port == 4) { 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) return; @@ -3736,6 +3733,20 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip) return mv88e6xxx_software_reset(chip); } +/* For MV88E6XXX_FAMILY_6352, perform reset on G1 control. + * Also, read and cache G2 scratch register. + */ +static int mv88e6352_reset(struct mv88e6xxx_chip *chip) +{ + int err; + + err = mv88e6352_g1_reset(chip); + if (err) + return err; + + return mv88e6352_g2_cache_global_scratch_config3(chip); +} + /* prod_id for switch families which do not have a PHY model number */ static const u16 family_prod_id_table[] = { [MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, @@ -4434,7 +4445,6 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6341_port_set_speed_duplex, - .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4654,7 +4664,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .pot_clear = mv88e6xxx_g2_pot_clear, .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, - .reset = mv88e6352_g1_reset, + .reset = mv88e6352_reset, .rmu_disable = mv88e6352_g1_rmu_disable, .atu_get_hash = mv88e6165_g1_atu_get_hash, .atu_set_hash = mv88e6165_g1_atu_set_hash, @@ -4662,6 +4672,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .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_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, @@ -4757,7 +4768,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .pot_clear = mv88e6xxx_g2_pot_clear, .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, - .reset = mv88e6352_g1_reset, + .reset = mv88e6352_reset, .rmu_disable = mv88e6352_g1_rmu_disable, .atu_get_hash = mv88e6165_g1_atu_get_hash, .atu_set_hash = mv88e6165_g1_atu_set_hash, @@ -4765,6 +4776,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .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_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, @@ -4826,7 +4838,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, - .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4886,7 +4897,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6390x_port_set_speed_duplex, - .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -4946,7 +4956,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, - .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, @@ -5032,7 +5041,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .pot_clear = mv88e6xxx_g2_pot_clear, .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, - .reset = mv88e6352_g1_reset, + .reset = mv88e6352_reset, .rmu_disable = mv88e6352_g1_rmu_disable, .atu_get_hash = mv88e6165_g1_atu_get_hash, .atu_set_hash = mv88e6165_g1_atu_set_hash, @@ -5040,6 +5049,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .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_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, @@ -5110,7 +5120,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, - .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -5173,7 +5182,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6320_port_set_rgmii_delay, - .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, + .port_set_speed_duplex = mv88e6320_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, @@ -5200,6 +5209,9 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, .reset = mv88e6352_g1_reset, + .rmu_disable = mv88e6352_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .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, @@ -5226,7 +5238,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6320_port_set_rgmii_delay, - .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, + .port_set_speed_duplex = mv88e6320_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, @@ -5249,17 +5261,25 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, .reset = mv88e6352_g1_reset, + .rmu_disable = mv88e6352_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .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 = mv88e6321_serdes_get_lane, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_get_caps = mv88e632x_phylink_get_caps, + .pcs_ops = &mv88e6352_pcs_ops, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -5278,7 +5298,6 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6341_port_set_speed_duplex, - .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -5463,7 +5482,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .pot_clear = mv88e6xxx_g2_pot_clear, .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, - .reset = mv88e6352_g1_reset, + .reset = mv88e6352_reset, .rmu_disable = mv88e6352_g1_rmu_disable, .atu_get_hash = mv88e6165_g1_atu_get_hash, .atu_set_hash = mv88e6165_g1_atu_set_hash, @@ -5475,6 +5494,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_get_sset_count = mv88e6352_serdes_get_sset_count, .serdes_get_strings = mv88e6352_serdes_get_strings, .serdes_get_stats = mv88e6352_serdes_get_stats, @@ -5499,7 +5519,6 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, - .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -5564,7 +5583,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6390x_port_set_speed_duplex, - .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -5627,7 +5645,6 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .port_sync_link = mv88e6xxx_port_sync_link, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, .port_set_speed_duplex = mv88e6393x_port_set_speed_duplex, - .port_max_speed_mode = mv88e6393x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_policy = mv88e6393x_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, @@ -6289,7 +6306,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .global2_addr = 0x1c, .age_time_coeff = 15000, - .g1_irqs = 8, + .g1_irqs = 9, .g2_irqs = 10, .stats_type = STATS_TYPE_BANK0 | STATS_TYPE_BANK1, .atu_move_port_mask = 0xf, @@ -6317,7 +6334,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .global2_addr = 0x1c, .age_time_coeff = 15000, - .g1_irqs = 8, + .g1_irqs = 9, .g2_irqs = 10, .stats_type = STATS_TYPE_BANK0 | STATS_TYPE_BANK1, .atu_move_port_mask = 0xf, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 2b235ac2c5df..e966e7c4cc5d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -454,6 +454,9 @@ struct mv88e6xxx_chip { /* TCAM entries */ struct mv88e6xxx_tcam tcam; + + /* Global2 scratch register config data3 */ + u8 g2_scratch_config3; }; #define TCAM_MATCH_SIZE 96 @@ -585,10 +588,6 @@ struct mv88e6xxx_ops { int (*port_set_speed_duplex)(struct mv88e6xxx_chip *chip, int port, int speed, int duplex); - /* What interface mode should be used for maximum speed? */ - phy_interface_t (*port_max_speed_mode)(struct mv88e6xxx_chip *chip, - int port); - int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); int (*port_set_policy)(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c index 0f84bffc8ef1..6f034841883c 100644 --- a/drivers/net/dsa/mv88e6xxx/devlink.c +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -91,6 +91,7 @@ void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds) } enum mv88e6xxx_devlink_resource_id { + MV88E6XXX_RESOURCE_ID_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */ MV88E6XXX_RESOURCE_ID_ATU, MV88E6XXX_RESOURCE_ID_ATU_BIN_0, MV88E6XXX_RESOURCE_ID_ATU_BIN_1, @@ -200,7 +201,7 @@ int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) err = dsa_devlink_resource_register(ds, "ATU_bin_0", mv88e6xxx_num_macs(chip) / 4, MV88E6XXX_RESOURCE_ID_ATU_BIN_0, - MV88E6XXX_RESOURCE_ID_ATU, + DEVLINK_RESOURCE_ID_PARENT_TOP, &size_params); if (err) goto out; @@ -208,7 +209,7 @@ int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) err = dsa_devlink_resource_register(ds, "ATU_bin_1", mv88e6xxx_num_macs(chip) / 4, MV88E6XXX_RESOURCE_ID_ATU_BIN_1, - MV88E6XXX_RESOURCE_ID_ATU, + DEVLINK_RESOURCE_ID_PARENT_TOP, &size_params); if (err) goto out; @@ -216,7 +217,7 @@ int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) err = dsa_devlink_resource_register(ds, "ATU_bin_2", mv88e6xxx_num_macs(chip) / 4, MV88E6XXX_RESOURCE_ID_ATU_BIN_2, - MV88E6XXX_RESOURCE_ID_ATU, + DEVLINK_RESOURCE_ID_PARENT_TOP, &size_params); if (err) goto out; @@ -224,7 +225,7 @@ int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) err = dsa_devlink_resource_register(ds, "ATU_bin_3", mv88e6xxx_num_macs(chip) / 4, MV88E6XXX_RESOURCE_ID_ATU_BIN_3, - MV88E6XXX_RESOURCE_ID_ATU, + DEVLINK_RESOURCE_ID_PARENT_TOP, &size_params); if (err) goto out; diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 82f9b410de0b..ff5adf7c9bc3 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -382,6 +382,7 @@ int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, bool external); int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, bool external); +int mv88e6352_g2_cache_global_scratch_config3(struct mv88e6xxx_chip *chip); 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 53a6d3ed63b3..f292ee4db113 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c +++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c @@ -322,18 +322,19 @@ int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, } /** - * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes + * mv88e6352_g2_cache_global_scratch_config3 - caches G2 CONFIG3 value * @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. + * Reads and stores config3 value of global2 scratch registers, which + * can be used to determine if the port is attached to a serdes. The + * value does not change once the switch is released from reset and + * represents the value of the pin strapping. + * + * Return: negative error number if the register read fails; otherwise, 0 */ -int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port) +int mv88e6352_g2_cache_global_scratch_config3(struct mv88e6xxx_chip *chip) { - u8 config3, p; + u8 config3; int err; err = mv88e6xxx_g2_scratch_read(chip, MV88E6352_G2_SCRATCH_CONFIG_DATA3, @@ -341,7 +342,26 @@ int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port) if (err) return err; - if (config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL) + chip->g2_scratch_config3 = config3; + + return 0; +} + +/** + * mv88e6352_g2_scratch_port_has_serdes - indicate if a port has 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, which is cached at reset (scratch config3). Returns 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 p; + + if (chip->g2_scratch_config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL) p = 5; else p = 4; diff --git a/drivers/net/dsa/mv88e6xxx/pcs-6352.c b/drivers/net/dsa/mv88e6xxx/pcs-6352.c index 9ebf0f89f817..4228ae5bb9db 100644 --- a/drivers/net/dsa/mv88e6xxx/pcs-6352.c +++ b/drivers/net/dsa/mv88e6xxx/pcs-6352.c @@ -324,19 +324,17 @@ static int mv88e6352_pcs_init(struct mv88e6xxx_chip *chip, int port) struct mii_bus *bus; struct device *dev; unsigned int irq; - int err; + int lane, err; - mv88e6xxx_reg_lock(chip); - err = mv88e6352_g2_scratch_port_has_serdes(chip, port); - mv88e6xxx_reg_unlock(chip); - if (err <= 0) - return err; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) + return 0; irq = mv88e6xxx_serdes_irq_mapping(chip, port); bus = mv88e6xxx_default_mdio_bus(chip); dev = chip->dev; - mpcs = marvell_c22_pcs_alloc(dev, bus, MV88E6352_ADDR_SERDES); + mpcs = marvell_c22_pcs_alloc(dev, bus, lane); if (!mpcs) return -ENOMEM; diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 49cd82930b7a..ea1fab71968a 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -327,6 +327,21 @@ int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, duplex); } +int mv88e6320_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex) +{ + bool has_200 = (port == 2 || port == 5 || port == 6); + + if (speed > 1000) + return -EOPNOTSUPP; + + if (speed == 200 && !has_200) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, has_200, + false, duplex); +} + /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */ int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex) @@ -344,15 +359,6 @@ int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, duplex); } -phy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port) -{ - if (port == 5) - return PHY_INTERFACE_MODE_2500BASEX; - - return PHY_INTERFACE_MODE_NA; -} - /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex) @@ -384,15 +390,6 @@ int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, duplex); } -phy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port) -{ - if (port == 9 || port == 10) - return PHY_INTERFACE_MODE_2500BASEX; - - return PHY_INTERFACE_MODE_NA; -} - /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex) @@ -407,15 +404,6 @@ int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, duplex); } -phy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port) -{ - if (port == 9 || port == 10) - return PHY_INTERFACE_MODE_XAUI; - - return PHY_INTERFACE_MODE_NA; -} - /* Support 10, 100, 200, 1000, 2500, 5000, 10000 Mbps (e.g. 88E6393X) * Function mv88e6xxx_port_set_speed_duplex() can't be used as the register * values for speeds 2500 & 5000 conflict. @@ -509,19 +497,6 @@ int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, return 0; } -phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port) -{ - - if (port != 0 && port != 9 && port != 10) - return PHY_INTERFACE_MODE_NA; - - if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361) - return PHY_INTERFACE_MODE_2500BASEX; - - return PHY_INTERFACE_MODE_10GBASER; -} - static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode, bool force) { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index a2492cf4d920..5b6cde9f7406 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -493,6 +493,8 @@ int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex); int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex); +int mv88e6320_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex); int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, @@ -504,15 +506,6 @@ int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, int speed, int duplex); -phy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port); -phy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port); -phy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port); -phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip, - int port); - int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map); diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index a936ee80ce00..4a394d47923e 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -17,10 +17,10 @@ #include "port.h" #include "serdes.h" -static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg, - u16 *val) +static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int lane, + int reg, u16 *val) { - return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, + return mv88e6xxx_phy_page_read(chip, lane, MV88E6352_SERDES_PAGE_FIBER, reg, val); } @@ -102,6 +102,17 @@ int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa, return 0; } +int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + int has_serdes; + + has_serdes = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (has_serdes == 0) + return -ENODEV; + + return MV88E6352_ADDR_SERDES; +} + struct mv88e6352_serdes_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -115,11 +126,11 @@ static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) { - int err; + int lane = -ENODEV; - err = mv88e6352_g2_scratch_port_has_serdes(chip, port); - if (err <= 0) - return err; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) + return 0; return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } @@ -128,11 +139,12 @@ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t **data) { struct mv88e6352_serdes_hw_stat *stat; - int err, i; + int lane = -ENODEV; + int i; - err = mv88e6352_g2_scratch_port_has_serdes(chip, port); - if (err <= 0) - return err; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) + return 0; for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { stat = &mv88e6352_serdes_hw_stats[i]; @@ -141,14 +153,14 @@ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } -static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, +static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane, struct mv88e6352_serdes_hw_stat *stat) { u64 val = 0; u16 reg; int err; - err = mv88e6352_serdes_read(chip, stat->reg, ®); + err = mv88e6352_serdes_read(chip, lane, stat->reg, ®); if (err) { dev_err(chip->dev, "failed to read statistic\n"); return 0; @@ -157,7 +169,7 @@ static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, val = reg; if (stat->sizeof_stat == 32) { - err = mv88e6352_serdes_read(chip, stat->reg + 1, ®); + err = mv88e6352_serdes_read(chip, lane, stat->reg + 1, ®); if (err) { dev_err(chip->dev, "failed to read statistic\n"); return 0; @@ -173,11 +185,12 @@ size_t 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; + int lane = -ENODEV; u64 value; + int i; - err = mv88e6352_g2_scratch_port_has_serdes(chip, port); - if (err <= 0) + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) return 0; BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > @@ -185,7 +198,7 @@ size_t mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { stat = &mv88e6352_serdes_hw_stats[i]; - value = mv88e6352_serdes_get_stat(chip, stat); + value = mv88e6352_serdes_get_stat(chip, lane, stat); mv88e6xxx_port->serdes_stats[i] += value; data[i] = mv88e6xxx_port->serdes_stats[i]; } @@ -200,35 +213,52 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) { - int err; + int lane = -ENODEV; - mv88e6xxx_reg_lock(chip); - err = mv88e6352_g2_scratch_port_has_serdes(chip, port); - mv88e6xxx_reg_unlock(chip); - if (err <= 0) - return err; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) + return 0; return 32 * sizeof(u16); } void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) { + int lane = -ENODEV; u16 *p = _p; u16 reg; int err; int i; - err = mv88e6352_g2_scratch_port_has_serdes(chip, port); - if (err <= 0) + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane < 0) return; for (i = 0 ; i < 32; i++) { - err = mv88e6352_serdes_read(chip, i, ®); + err = mv88e6352_serdes_read(chip, lane, i, ®); if (!err) p[i] = reg; } } +int mv88e6321_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + int lane = -ENODEV; + u8 cmode; + + if (port != 0 && port != 1) + return lane; + + cmode = chip->ports[port].cmode; + + if (cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX || + cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) + lane = port + MV88E6321_PORT0_LANE; + + return lane; +} + int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 17a3e85fabaa..e8f254909d6b 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -14,6 +14,8 @@ struct phylink_link_state; +#define MV88E6321_PORT0_LANE 0x0c + #define MV88E6352_ADDR_SERDES 0x0f #define MV88E6352_SERDES_PAGE_FIBER 0x01 #define MV88E6352_SERDES_IRQ 0x0b @@ -114,7 +116,9 @@ struct phylink_link_state; int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa, u16 status, struct phylink_link_state *state); +int mv88e6321_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); +int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/mxl862xx/Makefile b/drivers/net/dsa/mxl862xx/Makefile index d23dd3cd511d..a7be0e6669df 100644 --- a/drivers/net/dsa/mxl862xx/Makefile +++ b/drivers/net/dsa/mxl862xx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o -mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o +mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-api.h b/drivers/net/dsa/mxl862xx/mxl862xx-api.h index fb21ddc1bf1c..a180a5decffc 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h @@ -1366,4 +1366,219 @@ struct mxl862xx_rmon_port_cnt { __le64 tx_good_bytes; } __packed; +/* XPCS interface mode, MXL862XX_XPCS_*_INTERFACE field values */ +#define MXL862XX_XPCS_IF_SGMII 0 +#define MXL862XX_XPCS_IF_1000BASEX 1 +#define MXL862XX_XPCS_IF_2500BASEX 2 +#define MXL862XX_XPCS_IF_USXGMII 3 /* single or quad */ +#define MXL862XX_XPCS_IF_10GBASER 4 +#define MXL862XX_XPCS_IF_10GKR 5 /* 10GBASE-KR */ +#define MXL862XX_XPCS_IF_5GBASER 6 +#define MXL862XX_XPCS_IF_QSGMII 7 + +/* PCS negotiation mode, MXL862XX_XPCS_CFG_NEG_MODE field values */ +#define MXL862XX_XPCS_NEG_NONE 0 /* no inband negotiation */ +#define MXL862XX_XPCS_NEG_INBAND_AN_OFF 1 /* inband, AN disabled */ +#define MXL862XX_XPCS_NEG_INBAND_AN_ON 2 /* inband, AN enabled */ + +/* + * PCS protocol role, MXL862XX_XPCS_CFG_ROLE field value. Selects the role + * the XPCS plays in protocols with an asymmetric AN code word (Cisco SGMII + * / QSGMII / USXGMII), driving VR_MII_AN_CTRL.TX_CONFIG: MAC means the + * local end receives the partner's AN word, PHY means it sources one. + * Ignored for symmetric protocols (1000BASE-X, 2500BASE-X, 10GBASE-R/KR). + */ +#define MXL862XX_XPCS_ROLE_MAC 0 /* local end is MAC side */ +#define MXL862XX_XPCS_ROLE_PHY 1 /* local end is PHY side */ + +/* USXGMII lane mode, MXL862XX_XPCS_*_USX_LANE_MODE field values */ +#define MXL862XX_XPCS_USX_SINGLE 0 /* single USXGMII lane */ +#define MXL862XX_XPCS_USX_QUAD 1 /* quad USXGMII, 4 ports/lane */ + +/** + * union mxl862xx_xpcs_an_word - XPCS AN code word, tagged by interface mode + * @cl37: 16-bit base-page word exchanged over the CL37 hardware AN path + * (SR_MII_AN_ADV on write, SR_MII_LP_BABL on read). Carries the + * 802.3 CL37 base page for 1000BASE-X/2500BASE-X and the Cisco + * SGMII config word for SGMII/QSGMII. + * @usx: USXGMII 16-bit AN code word, MDIO_USXGMII_* layout + * @cl73: CL73 48-bit base page (10GBASE-KR), three 16-bit registers per + * 802.3 Annex 28C + * @cl73.adv1: CL73 SR_AN_ADV1 / SR_AN_LP_ABL1 + * @cl73.adv2: CL73 SR_AN_ADV2 / SR_AN_LP_ABL2 + * @cl73.adv3: CL73 SR_AN_ADV3 / SR_AN_LP_ABL3 + * + * The host picks the right member based on the interface field of the + * surrounding struct (and, for the asymmetric protocols, on the role). + */ +union mxl862xx_xpcs_an_word { + __le16 cl37; + __le16 usx; + struct { + __le16 adv1; + __le16 adv2; + __le16 adv3; + } cl73; +} __packed; + +/* PCS duplex mode, MXL862XX_XPCS_*_DUPLEX field values */ +#define MXL862XX_XPCS_DUPLEX_HALF 0 +#define MXL862XX_XPCS_DUPLEX_FULL 1 + +/** + * enum mxl862xx_xpcs_loopback_mode - XPCS loopback mode + * @MXL862XX_XPCS_LB_DISABLE: disable all loopback + * @MXL862XX_XPCS_LB_PCS_SERIAL: PCS TX-to-RX serial loopback + * @MXL862XX_XPCS_LB_PCS_PARALLEL: PCS RX-to-TX parallel loopback + * @MXL862XX_XPCS_LB_PMA_SERIAL: PMA TX-to-RX serial loopback + * @MXL862XX_XPCS_LB_PMA_PARALLEL: PMA RX-to-TX parallel loopback + */ +enum mxl862xx_xpcs_loopback_mode { + MXL862XX_XPCS_LB_DISABLE = 0, + MXL862XX_XPCS_LB_PCS_SERIAL = 1, + MXL862XX_XPCS_LB_PCS_PARALLEL = 2, + MXL862XX_XPCS_LB_PMA_SERIAL = 3, + MXL862XX_XPCS_LB_PMA_PARALLEL = 4, +}; + +/* Fields of mxl862xx_xpcs_pcs_cfg.mode */ +#define MXL862XX_XPCS_CFG_PORT_ID GENMASK(1, 0) +#define MXL862XX_XPCS_CFG_INTERFACE GENMASK(7, 2) +#define MXL862XX_XPCS_CFG_NEG_MODE GENMASK(9, 8) +#define MXL862XX_XPCS_CFG_PERMIT_PAUSE BIT(10) +#define MXL862XX_XPCS_CFG_USX_LANE_MODE GENMASK(12, 11) +#define MXL862XX_XPCS_CFG_ROLE BIT(13) +#define MXL862XX_XPCS_CFG_USX_SUBPORT GENMASK(15, 14) + +/** + * struct mxl862xx_xpcs_pcs_cfg - PCS configuration parameters + * @mode: Packed interface and negotiation parameters, see + * MXL862XX_XPCS_CFG_*. port_id is the XPCS port index (0-3); + * interface is the PCS interface mode (MXL862XX_XPCS_IF_*); + * neg_mode is the negotiation mode (MXL862XX_XPCS_NEG_*); + * permit_pause allows pause to MAC; usx_lane_mode is the USXGMII + * lane mode (MXL862XX_XPCS_USX_*); role is the protocol role + * (MXL862XX_XPCS_ROLE_*); usx_subport is the sub-port (0-3) within + * the XPCS -- despite the name it also identifies the QSGMII + * sub-port -- used by the firmware to set MAC pause per sub-port + * and ignored for the XPCS-wide bringup, which is idempotent across + * slots. + * @advertising: AN code word the local end transmits. The active union + * member is selected by the interface field (and, for the + * asymmetric protocols, by role). Ignored when the local end + * does not transmit an AN word (role=MAC for SGMII/QSGMII/ + * USXGMII, 10GBASE-R, 5GBASE-R) or when neg_mode is not + * INBAND_AN_ON. Pass all-zero to keep the firmware default + * advertisement. + * @result: Firmware result. >0 means the host must follow with an AN + * restart, 0 means no host follow-up is needed, <0 is an errno. + */ +struct mxl862xx_xpcs_pcs_cfg { + __le16 mode; + union mxl862xx_xpcs_an_word advertising; + __le16 result; +} __packed; + +/* Fields of mxl862xx_xpcs_pcs_state.mode */ +#define MXL862XX_XPCS_ST_PORT_ID GENMASK(1, 0) +#define MXL862XX_XPCS_ST_INTERFACE GENMASK(7, 2) +#define MXL862XX_XPCS_ST_USX_LANE_MODE GENMASK(9, 8) +#define MXL862XX_XPCS_ST_USX_SUBPORT GENMASK(11, 10) +#define MXL862XX_XPCS_ST_LINK BIT(12) +#define MXL862XX_XPCS_ST_AN_COMPLETE BIT(13) +#define MXL862XX_XPCS_ST_DUPLEX BIT(14) +#define MXL862XX_XPCS_ST_PCS_FAULT BIT(15) +#define MXL862XX_XPCS_ST_PAUSE GENMASK(17, 16) +#define MXL862XX_XPCS_ST_LP_EEE_CAP BIT(18) +#define MXL862XX_XPCS_ST_LP_EEE_CS_CAP BIT(19) + +/** + * struct mxl862xx_xpcs_pcs_state - PCS link state + * @mode: Packed input parameters and firmware status, see + * MXL862XX_XPCS_ST_*. The host writes port_id (XPCS port index 0-3), + * interface (MXL862XX_XPCS_IF_*), usx_lane_mode + * (MXL862XX_XPCS_USX_*) and usx_subport (0-3); the firmware fills in + * link, an_complete, duplex (MXL862XX_XPCS_DUPLEX_*), pcs_fault, + * pause (bit 0 symmetric, bit 1 asymmetric), lp_eee_cap and + * lp_eee_cs_cap. + * @speed: Resolved speed in Mbit/s (output) + * @lpa: Link partner ability word (output). Same union as + * &union mxl862xx_xpcs_an_word; the host picks the member based on + * the interface field. + */ +struct mxl862xx_xpcs_pcs_state { + __le32 mode; + __le16 speed; /* Mbit/s */ + union mxl862xx_xpcs_an_word lpa; +} __packed; + +/** + * struct mxl862xx_xpcs_pcs_disable - PCS disable parameters + * @port_id: XPCS port index + * @__pad: padding + * @result: Firmware result. 0 on success, <0 on error. + * + * Asserts IDDQ + PHY + XPCS resets to power down the SERDES when the + * port is admin-down or no module is plugged in. The next PCS config + * implicitly powers it back up and reprograms the desired interface. + */ +struct mxl862xx_xpcs_pcs_disable { + u8 port_id; + u8 __pad; + __le16 result; +} __packed; + +/* Fields of mxl862xx_xpcs_an_restart.mode */ +#define MXL862XX_XPCS_ANR_PORT_ID GENMASK(1, 0) +#define MXL862XX_XPCS_ANR_INTERFACE GENMASK(7, 2) +#define MXL862XX_XPCS_ANR_USX_LANE_MODE GENMASK(9, 8) +#define MXL862XX_XPCS_ANR_USX_SUBPORT GENMASK(11, 10) + +/** + * struct mxl862xx_xpcs_an_restart - AN restart parameters + * @mode: Packed input parameters, see MXL862XX_XPCS_ANR_*. port_id is the + * XPCS port index (0-3); interface is the PCS interface mode + * (MXL862XX_XPCS_IF_*); usx_lane_mode is the USX lane mode + * (MXL862XX_XPCS_USX_*); usx_subport (0-3) selects the lane whose + * AN is restarted for QSGMII and QUSXGMII and is ignored by + * single-lane modes. + * @result: Firmware result. 0 on success, <0 on error. + * + * Restarts auto-negotiation on a single sub-port of the XPCS. The + * SERDES must already be configured. + */ +struct mxl862xx_xpcs_an_restart { + __le16 mode; + __le16 result; +} __packed; + +/* Fields of mxl862xx_xpcs_pcs_link_up.mode */ +#define MXL862XX_XPCS_LU_PORT_ID GENMASK(1, 0) +#define MXL862XX_XPCS_LU_INTERFACE GENMASK(7, 2) +#define MXL862XX_XPCS_LU_DUPLEX BIT(8) +#define MXL862XX_XPCS_LU_USX_LANE_MODE GENMASK(10, 9) +#define MXL862XX_XPCS_LU_USX_SUBPORT GENMASK(12, 11) + +/** + * struct mxl862xx_xpcs_pcs_link_up - PCS link-up parameters + * @mode: Packed input parameters, see MXL862XX_XPCS_LU_*. port_id is the + * XPCS port index (0-3); interface is the PCS interface mode + * (MXL862XX_XPCS_IF_*); duplex is the duplex mode + * (MXL862XX_XPCS_DUPLEX_*); usx_lane_mode is the USX lane mode + * (USXGMII only, ignored otherwise, MXL862XX_XPCS_USX_*); + * usx_subport (0-3) selects the sub-port for QUSXGMII and QSGMII + * (despite the name) and is ignored otherwise. + * @speed: Resolved speed in Mbit/s + * @result: Firmware result. 0 on success, <0 is errno. + * + * Called once per link-up event after the host has resolved the + * line-side speed/duplex (from the PHY's read_status, from a preceding + * PCS get-state, or from a fixed-link description). + */ +struct mxl862xx_xpcs_pcs_link_up { + __le16 mode; + __le16 speed; /* Mbit/s */ + __le16 result; +} __packed; + #endif /* __MXL862XX_API_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h index f1ea40aa7ea0..c87a955c13c4 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h @@ -24,6 +24,7 @@ #define MXL862XX_SS_MAGIC 0x1600 #define GPY_GPY2XX_MAGIC 0x1800 #define SYS_MISC_MAGIC 0x1900 +#define MXL862XX_XPCS_MAGIC 0x1a00 #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) #define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xa) @@ -71,6 +72,14 @@ #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2) +#define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1) +#define MXL862XX_XPCS_PCS_GET_STATE (MXL862XX_XPCS_MAGIC + 0x2) +#define MXL862XX_XPCS_PCS_DISABLE (MXL862XX_XPCS_MAGIC + 0x4) +#define MXL862XX_XPCS_AN_RESTART (MXL862XX_XPCS_MAGIC + 0x5) +#define MXL862XX_XPCS_PCS_LINK_UP (MXL862XX_XPCS_MAGIC + 0x7) +#define MXL862XX_XPCS_LOOPBACK (MXL862XX_XPCS_MAGIC + 0x8) +#define MXL862XX_XPCS_RESET (MXL862XX_XPCS_MAGIC + 0x9) + #define MMD_API_MAXIMUM_ID 0x7fff #endif /* __MXL862XX_CMD_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.h b/drivers/net/dsa/mxl862xx/mxl862xx-host.h index 84512a30bc18..66d6ae198aff 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx-host.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h @@ -9,6 +9,14 @@ void mxl862xx_host_init(struct mxl862xx_priv *priv); void mxl862xx_host_shutdown(struct mxl862xx_priv *priv); int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, bool read, bool quiet); + +#define MXL862XX_API_WRITE(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) +#define MXL862XX_API_READ(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) +#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) + int mxl862xx_reset(struct mxl862xx_priv *priv); #endif /* __MXL862XX_HOST_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c new file mode 100644 index 000000000000..b689652aa9b9 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phylink and PCS support for MaxLinear MxL862xx switch family + * + * Copyright (C) 2024 MaxLinear Inc. + * Copyright (C) 2025 John Crispin + * Copyright (C) 2025 Daniel Golle + */ + +#include +#include +#include +#include + +#include "mxl862xx.h" +#include "mxl862xx-api.h" +#include "mxl862xx-cmd.h" +#include "mxl862xx-host.h" +#include "mxl862xx-phylink.h" + +void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct mxl862xx_priv *priv = ds->priv; + + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000 | MAC_2500FD; + + switch (port) { + case 1 ... 8: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + break; + case 9: + case 13: + /* Advertised also on old firmware lacking the XPCS API: + * there the SerDes runs in its flash-configured mode + * without host control (mac_select_pcs returns NULL), + * keeping the CPU port working. + */ + __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GKR, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces); + fallthrough; + case 10 ... 12: + case 14 ... 16: + if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) + break; + __set_bit(PHY_INTERFACE_MODE_QSGMII, config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, config->supported_interfaces); + + break; + default: + break; + } + + if (port == 9 || port == 13) + config->mac_capabilities |= MAC_10000FD | MAC_5000FD; +} + +static struct mxl862xx_pcs *pcs_to_mxl862xx_pcs(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mxl862xx_pcs, pcs); +} + +static int mxl862xx_xpcs_if_mode(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + return MXL862XX_XPCS_IF_SGMII; + case PHY_INTERFACE_MODE_QSGMII: + return MXL862XX_XPCS_IF_QSGMII; + case PHY_INTERFACE_MODE_1000BASEX: + return MXL862XX_XPCS_IF_1000BASEX; + case PHY_INTERFACE_MODE_2500BASEX: + return MXL862XX_XPCS_IF_2500BASEX; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + return MXL862XX_XPCS_IF_USXGMII; + case PHY_INTERFACE_MODE_10GBASER: + return MXL862XX_XPCS_IF_10GBASER; + case PHY_INTERFACE_MODE_10GKR: + return MXL862XX_XPCS_IF_10GKR; + default: + return -EINVAL; + } +} + +static int mxl862xx_xpcs_neg_mode(unsigned int neg_mode) +{ + if (!(neg_mode & PHYLINK_PCS_NEG_INBAND)) + return MXL862XX_XPCS_NEG_NONE; + if (neg_mode & PHYLINK_PCS_NEG_ENABLED) + return MXL862XX_XPCS_NEG_INBAND_AN_ON; + return MXL862XX_XPCS_NEG_INBAND_AN_OFF; +} + +static int mxl862xx_pcs_enable(struct phylink_pcs *pcs) +{ + struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); + + /* Bringup is done idempotently by pcs_config; just account this + * sub-port so pcs_disable powers the shared XPCS down only after + * the last sub-port has been released. + */ + mutex_lock(&mpcs->priv->serdes_lock); + mpcs->priv->serdes_refcount[mpcs->serdes_id]++; + mutex_unlock(&mpcs->priv->serdes_lock); + + return 0; +} + +static void mxl862xx_pcs_disable(struct phylink_pcs *pcs) +{ + struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_xpcs_pcs_disable dis = {}; + struct mxl862xx_priv *priv = mpcs->priv; + + dis.port_id = mpcs->serdes_id; + + /* The SerDes is shared across QSGMII/QUSXGMII sub-ports; only + * power it down once the last active sub-port goes away. Hold + * serdes_lock across the count and the power-down so a sibling + * sub-port enable cannot race the transition to zero. + */ + mutex_lock(&priv->serdes_lock); + if (--priv->serdes_refcount[mpcs->serdes_id] == 0) + MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, dis); + mutex_unlock(&priv->serdes_lock); +} + +/* The XPCS firmware reports failures in the result field using its own + * libc errno values; ENOTSUP (134) in particular has no kernel errno. + * Translate the codes the firmware can actually return. + */ +static int mxl862xx_xpcs_errno(int result) +{ + switch (result) { + case -5: /* firmware -EIO */ + return -EIO; + case -134: /* firmware -ENOTSUP */ + return -EOPNOTSUPP; + default: /* firmware -EINVAL and anything unexpected */ + return -EINVAL; + } +} + +static int mxl862xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv = mpcs->priv; + struct mxl862xx_xpcs_pcs_cfg cfg = {}; + int if_mode, lane, ret, adv; + + if_mode = mxl862xx_xpcs_if_mode(interface); + if (if_mode < 0) { + dev_err(priv->ds->dev, "unsupported interface: %s\n", + phy_modes(interface)); + return if_mode; + } + + /* The XPCS bringup is per-instance and idempotent in the + * firmware: every QSGMII/QUSXGMII sub-port may call pcs_config + * and the firmware will skip the bringup if the requested mode + * matches the cached one, then update MAC pause for the + * sub-port indicated by @usx_subport. No serdes_lock is needed + * here: the refcount held since pcs_enable keeps a sibling + * pcs_disable from powering the XPCS down, and pcs_disable + * invalidates the firmware's cached mode so the next pcs_config + * redoes the bringup. + */ + lane = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ? + MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; + + cfg.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_CFG_PORT_ID, + mpcs->serdes_id) | + FIELD_PREP(MXL862XX_XPCS_CFG_USX_SUBPORT, + mpcs->slot) | + FIELD_PREP(MXL862XX_XPCS_CFG_USX_LANE_MODE, lane) | + FIELD_PREP(MXL862XX_XPCS_CFG_INTERFACE, if_mode) | + FIELD_PREP(MXL862XX_XPCS_CFG_NEG_MODE, + mxl862xx_xpcs_neg_mode(neg_mode)) | + FIELD_PREP(MXL862XX_XPCS_CFG_ROLE, + MXL862XX_XPCS_ROLE_MAC) | + FIELD_PREP(MXL862XX_XPCS_CFG_PERMIT_PAUSE, + permit_pause_to_mac)); + + if (neg_mode & PHYLINK_PCS_NEG_INBAND) { + adv = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); + if (adv >= 0) + cfg.advertising.cl37 = cpu_to_le16(adv); + } + + ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_CONFIG, cfg); + if (ret) + return ret; + + ret = (s16)le16_to_cpu(cfg.result); + if (ret < 0) + return mxl862xx_xpcs_errno(ret); + + mpcs->interface = interface; + return ret > 0 ? 1 : 0; +} + +static void mxl862xx_pcs_get_state(struct phylink_pcs *pcs, + unsigned int neg_mode, + struct phylink_link_state *state) +{ + struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv = mpcs->priv; + struct mxl862xx_xpcs_pcs_state st = {}; + int if_mode, lane, ret; + u32 mode; + u16 bmsr; + + if_mode = mxl862xx_xpcs_if_mode(state->interface); + if (if_mode < 0) { + state->link = false; + return; + } + + lane = (state->interface == PHY_INTERFACE_MODE_10G_QXGMII) ? + MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; + + st.mode = cpu_to_le32(FIELD_PREP(MXL862XX_XPCS_ST_PORT_ID, + mpcs->serdes_id) | + FIELD_PREP(MXL862XX_XPCS_ST_INTERFACE, if_mode) | + FIELD_PREP(MXL862XX_XPCS_ST_USX_SUBPORT, + mpcs->slot) | + FIELD_PREP(MXL862XX_XPCS_ST_USX_LANE_MODE, lane)); + + ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_GET_STATE, st); + if (ret) { + state->link = false; + return; + } + + mode = le32_to_cpu(st.mode); + state->link = FIELD_GET(MXL862XX_XPCS_ST_LINK, mode) && + !FIELD_GET(MXL862XX_XPCS_ST_PCS_FAULT, mode); + state->an_complete = FIELD_GET(MXL862XX_XPCS_ST_AN_COMPLETE, mode); + + switch (state->interface) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + bmsr = (state->link ? BMSR_LSTATUS : 0) | + (state->an_complete ? BMSR_ANEGCOMPLETE : 0); + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, + le16_to_cpu(st.lpa.cl37)); + break; + + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + if (state->link) + phylink_decode_usxgmii_word(state, + le16_to_cpu(st.lpa.usx)); + break; + + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: + if (state->link) { + state->speed = SPEED_10000; + state->duplex = DUPLEX_FULL; + } + break; + + default: + state->link = false; + break; + } +} + +static void mxl862xx_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_priv *priv = mpcs->priv; + struct mxl862xx_xpcs_an_restart an = {}; + int if_mode, lane; + + if_mode = mxl862xx_xpcs_if_mode(mpcs->interface); + if (if_mode < 0) + return; + + lane = (mpcs->interface == PHY_INTERFACE_MODE_10G_QXGMII) ? + MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; + + an.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_ANR_PORT_ID, + mpcs->serdes_id) | + FIELD_PREP(MXL862XX_XPCS_ANR_INTERFACE, if_mode) | + FIELD_PREP(MXL862XX_XPCS_ANR_USX_SUBPORT, + mpcs->slot) | + FIELD_PREP(MXL862XX_XPCS_ANR_USX_LANE_MODE, lane)); + + MXL862XX_API_WRITE(priv, MXL862XX_XPCS_AN_RESTART, an); +} + +static void mxl862xx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, int speed, + int duplex) +{ + struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs); + struct mxl862xx_xpcs_pcs_link_up lu = {}; + struct mxl862xx_priv *priv = mpcs->priv; + int if_mode, lane, dup; + + /* With inband-AN enabled (role=MAC), the XPCS auto-resolves + * speed/duplex from the partner's AN word and the firmware + * short-circuits link_up. Skip the firmware round-trip, same + * as pcs-mtk-lynxi. + */ + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + return; + + if_mode = mxl862xx_xpcs_if_mode(interface); + if (if_mode < 0) + return; + + lane = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ? + MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE; + dup = (duplex == DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL : + MXL862XX_XPCS_DUPLEX_HALF; + + lu.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_LU_PORT_ID, + mpcs->serdes_id) | + FIELD_PREP(MXL862XX_XPCS_LU_INTERFACE, if_mode) | + FIELD_PREP(MXL862XX_XPCS_LU_USX_SUBPORT, + mpcs->slot) | + FIELD_PREP(MXL862XX_XPCS_LU_USX_LANE_MODE, lane) | + FIELD_PREP(MXL862XX_XPCS_LU_DUPLEX, dup)); + lu.speed = cpu_to_le16(speed); + + MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_LINK_UP, lu); +} + +static unsigned int mxl862xx_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + case PHY_INTERFACE_MODE_10GKR: + return LINK_INBAND_ENABLE; + case PHY_INTERFACE_MODE_10GBASER: + return LINK_INBAND_DISABLE; + default: + return 0; + } +} + +static const struct phylink_pcs_ops mxl862xx_pcs_ops = { + .pcs_enable = mxl862xx_pcs_enable, + .pcs_disable = mxl862xx_pcs_disable, + .pcs_config = mxl862xx_pcs_config, + .pcs_get_state = mxl862xx_pcs_get_state, + .pcs_an_restart = mxl862xx_pcs_an_restart, + .pcs_link_up = mxl862xx_pcs_link_up, + .pcs_inband_caps = mxl862xx_pcs_inband_caps, +}; + +void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs, + int port) +{ + pcs->priv = priv; + pcs->serdes_id = MXL862XX_SERDES_PORT_ID(port); + pcs->slot = MXL862XX_SERDES_SLOT(port); + pcs->interface = PHY_INTERFACE_MODE_NA; + + pcs->pcs.ops = &mxl862xx_pcs_ops; + pcs->pcs.poll = true; + + __set_bit(PHY_INTERFACE_MODE_QSGMII, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10G_QXGMII, pcs->pcs.supported_interfaces); + if (pcs->slot != 0) + return; + + __set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GKR, pcs->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_USXGMII, pcs->pcs.supported_interfaces); +} + +static struct phylink_pcs * +mxl862xx_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct mxl862xx_priv *priv = dp->ds->priv; + int port = dp->index; + + switch (port) { + case 9 ... 16: + if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) { + dev_warn_once(dp->ds->dev, + "SerDes PCS unsupported on old firmware.\n"); + return NULL; + } + return &priv->serdes_ports[port - 9].pcs; + default: + return NULL; + } +} + +static void mxl862xx_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ +} + +static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ +} + +const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { + .mac_config = mxl862xx_phylink_mac_config, + .mac_link_down = mxl862xx_phylink_mac_link_down, + .mac_link_up = mxl862xx_phylink_mac_link_up, + .mac_select_pcs = mxl862xx_phylink_mac_select_pcs, +}; diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h new file mode 100644 index 000000000000..03bb9caad9aa --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-phylink.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MXL862XX_PHYLINK_H +#define __MXL862XX_PHYLINK_H + +#include + +#include "mxl862xx.h" + +#define MXL862XX_SERDES_SLOT(port) \ + (((port) - MXL862XX_FIRST_SERDES_PORT) % MXL862XX_SERDES_SLOTS) +#define MXL862XX_SERDES_PORT_ID(port) \ + (((port) - MXL862XX_FIRST_SERDES_PORT) / MXL862XX_SERDES_SLOTS) + +extern const struct phylink_mac_ops mxl862xx_phylink_mac_ops; +void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config); +void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs, + int port); + +#endif /* __MXL862XX_PHYLINK_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c index b60482d93a85..45d237b3a40f 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.c +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -22,13 +22,7 @@ #include "mxl862xx-api.h" #include "mxl862xx-cmd.h" #include "mxl862xx-host.h" - -#define MXL862XX_API_WRITE(dev, cmd, data) \ - mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) -#define MXL862XX_API_READ(dev, cmd, data) \ - mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) -#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ - mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) +#include "mxl862xx-phylink.h" /* Polling interval for RMON counter accumulation. At 2.5 Gbps with * minimum-size (64-byte) frames, a 32-bit packet counter wraps in ~880s. @@ -257,6 +251,9 @@ static int mxl862xx_wait_ready(struct dsa_switch *ds) ver.iv_major, ver.iv_minor, le16_to_cpu(ver.iv_revision), le32_to_cpu(ver.iv_build_num)); + priv->fw_version.major = ver.iv_major; + priv->fw_version.minor = ver.iv_minor; + priv->fw_version.revision = le16_to_cpu(ver.iv_revision); return 0; not_ready_yet: @@ -625,7 +622,7 @@ static int mxl862xx_setup(struct dsa_switch *ds) int n_user_ports = 0, max_vlans; int ingress_finals, vid_rules; struct dsa_port *dp; - int ret; + int ret, i; ret = mxl862xx_reset(priv); if (ret) @@ -635,6 +632,11 @@ static int mxl862xx_setup(struct dsa_switch *ds) if (ret) return ret; + mutex_init(&priv->serdes_lock); + for (i = 0; i < ARRAY_SIZE(priv->serdes_ports); i++) + mxl862xx_setup_pcs(priv, &priv->serdes_ports[i], + i + MXL862XX_FIRST_SERDES_PORT); + /* Calculate Extended VLAN block sizes. * With VLAN Filter handling VID membership checks: * Ingress: only final catchall rules (PVID insertion, 802.1Q @@ -1421,16 +1423,6 @@ static void mxl862xx_port_teardown(struct dsa_switch *ds, int port) priv->ports[port].setup_done = false; } -static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, - struct phylink_config *config) -{ - config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | - MAC_100 | MAC_1000 | MAC_2500FD; - - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - config->supported_interfaces); -} - static int mxl862xx_get_fid(struct dsa_switch *ds, struct dsa_db db) { struct mxl862xx_priv *priv = ds->priv; @@ -2096,33 +2088,6 @@ static const struct dsa_switch_ops mxl862xx_switch_ops = { .get_stats64 = mxl862xx_get_stats64, }; -static void mxl862xx_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state) -{ -} - -static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface) -{ -} - -static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, - unsigned int mode, - phy_interface_t interface, - int speed, int duplex, - bool tx_pause, bool rx_pause) -{ -} - -static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { - .mac_config = mxl862xx_phylink_mac_config, - .mac_link_down = mxl862xx_phylink_mac_link_down, - .mac_link_up = mxl862xx_phylink_mac_link_up, -}; - static int mxl862xx_probe(struct mdio_device *mdiodev) { struct device *dev = &mdiodev->dev; diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx/mxl862xx.h index 80053ab40e4c..432a5f3f2e08 100644 --- a/drivers/net/dsa/mxl862xx/mxl862xx.h +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h @@ -3,6 +3,7 @@ #ifndef __MXL862XX_H #define __MXL862XX_H +#include #include #include #include @@ -10,6 +11,9 @@ struct mxl862xx_priv; #define MXL862XX_MAX_PORTS 17 +#define MXL862XX_FIRST_SERDES_PORT 9 +#define MXL862XX_SERDES_SLOTS 4 + #define MXL862XX_DEFAULT_BRIDGE 0 #define MXL862XX_MAX_BRIDGES 48 #define MXL862XX_MAX_BRIDGE_PORTS 128 @@ -241,6 +245,45 @@ struct mxl862xx_port { spinlock_t stats_lock; /* protects stats accumulators */ }; +/** + * struct mxl862xx_pcs - link SerDes interfaces to bridge ports + * @pcs: &struct phylink_pcs instance + * @priv: pointer to &struct mxl862xx_priv + * @serdes_id: SerDes instance index (0 or 1) + * @slot: slot within the SerDes (0-3 for QSGMII/QUSXGMII, 0 otherwise) + * @interface: cached PHY interface, last value passed to pcs_config(). + * %PHY_INTERFACE_MODE_NA before the first successful + * pcs_config(). Used by pcs_an_restart() to populate the + * firmware command and by pcs_disable() to skip the + * firmware power-down for shared (QSGMII/QUSXGMII) modes. + */ +struct mxl862xx_pcs { + struct phylink_pcs pcs; + struct mxl862xx_priv *priv; + int serdes_id; + int slot; + phy_interface_t interface; +}; + +/** + * struct mxl862xx_fw_version - firmware version for comparison and display + * @major: firmware major version + * @minor: firmware minor version + * @revision: firmware revision number + */ +struct mxl862xx_fw_version { + u8 major; + u8 minor; + u16 revision; +}; + +#define MXL862XX_FW_VER(maj, min, rev) \ + (((u32)(maj) << 24) | ((u32)(min) << 16) | (rev)) +#define MXL862XX_FW_VER_MIN(priv, maj, min, rev) \ + (MXL862XX_FW_VER((priv)->fw_version.major, (priv)->fw_version.minor, \ + (priv)->fw_version.revision) >= \ + MXL862XX_FW_VER(maj, min, rev)) + /* Bit indices for struct mxl862xx_priv::flags */ #define MXL862XX_FLAG_CRC_ERR 0 #define MXL862XX_FLAG_WORK_STOPPED 1 @@ -258,6 +301,16 @@ struct mxl862xx_port { * @drop_meter: index of the single shared zero-rate firmware meter * used to unconditionally drop traffic (used to block * flooding) + * @fw_version: cached firmware version, populated at probe and + * compared with MXL862XX_FW_VER_MIN() + * @serdes_ports: SerDes interfaces incl. sub-interfaces in case of + * 10G_QXGMII or QSGMII + * @serdes_refcount: per-XPCS count of sub-ports enabled by phylink; + * pcs_disable powers an XPCS down when the count + * reaches zero. Protected by @serdes_lock. + * @serdes_lock: serializes the @serdes_refcount transitions with + * the XPCS power-down so a sibling sub-port enable + * cannot race a power-down to zero * @ports: per-port state, indexed by switch port number * @bridges: maps DSA bridge number to firmware bridge ID; * zero means no firmware bridge allocated for that @@ -275,6 +328,10 @@ struct mxl862xx_priv { struct work_struct crc_err_work; unsigned long flags; u16 drop_meter; + struct mxl862xx_fw_version fw_version; + struct mxl862xx_pcs serdes_ports[8]; + int serdes_refcount[2]; + struct mutex serdes_lock; struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; u16 bridges[MXL862XX_MAX_BRIDGES + 1]; u16 evlan_ingress_size; diff --git a/drivers/net/dsa/netc/Kconfig b/drivers/net/dsa/netc/Kconfig new file mode 100644 index 000000000000..793f7691a24f --- /dev/null +++ b/drivers/net/dsa/netc/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_NETC_SWITCH + tristate "NXP NETC Ethernet switch support" + depends on ARM64 || COMPILE_TEST + depends on NET_DSA && PCI + depends on NET_VENDOR_FREESCALE + select NET_DSA_TAG_NETC + select FSL_ENETC_MDIO + select NXP_NTMP + select NXP_NETC_LIB + help + This driver supports the NXP NETC Ethernet switch, which is embedded + as a PCIe function of the NXP NETC IP. But note that this driver is + only available for NETC v4.3 and later versions. + + If compiled as module (M), the module name is nxp-netc-switch. diff --git a/drivers/net/dsa/netc/Makefile b/drivers/net/dsa/netc/Makefile new file mode 100644 index 000000000000..f40b13c702e0 --- /dev/null +++ b/drivers/net/dsa/netc/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_NET_DSA_NETC_SWITCH) += nxp-netc-switch.o +nxp-netc-switch-objs := netc_main.o netc_platform.o netc_ethtool.o diff --git a/drivers/net/dsa/netc/netc_ethtool.c b/drivers/net/dsa/netc/netc_ethtool.c new file mode 100644 index 000000000000..e6028851a6d0 --- /dev/null +++ b/drivers/net/dsa/netc/netc_ethtool.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * NXP NETC switch driver + * Copyright 2025-2026 NXP + */ + +#include + +#include "netc_switch.h" + +static const struct ethtool_rmon_hist_range netc_rmon_ranges[] = { + { 64, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1522 }, + { 1523, NETC_MAX_FRAME_LEN }, + { } +}; + +static const struct netc_port_stat netc_port_counters[] = { + { NETC_PTGSLACR, "port gate late arrival frames" }, + { NETC_PSDFTCR, "port SDF transmit frames" }, + { NETC_PSDFDDCR, "port SDF drop duplicate frames" }, + { NETC_PRXDCR, "port rx discard frames" }, + { NETC_PRXDCRRR, "port rx discard read-reset" }, + { NETC_PRXDCRR0, "port rx discard reason 0" }, + { NETC_PRXDCRR1, "port rx discard reason 1" }, + { NETC_PTXDCR, "port tx discard frames" }, + { NETC_BPDCR, "bridge port discard frames" }, +}; + +static const struct netc_port_stat netc_emac_counters[] = { + { NETC_PM_ROCT(0), "eMAC rx octets" }, + { NETC_PM_RVLAN(0), "eMAC rx VLAN frames" }, + { NETC_PM_RERR(0), "eMAC rx frame errors" }, + { NETC_PM_RUCA(0), "eMAC rx unicast frames" }, + { NETC_PM_RDRP(0), "eMAC rx dropped packets" }, + { NETC_PM_RPKT(0), "eMAC rx packets" }, + { NETC_PM_TOCT(0), "eMAC tx octets" }, + { NETC_PM_TVLAN(0), "eMAC tx VLAN frames" }, + { NETC_PM_TFCS(0), "eMAC tx FCS errors" }, + { NETC_PM_TUCA(0), "eMAC tx unicast frames" }, + { NETC_PM_TPKT(0), "eMAC tx packets" }, + { NETC_PM_TUND(0), "eMAC tx undersized packets" }, + { NETC_PM_TIOCT(0), "eMAC tx invalid octets" }, +}; + +static const struct netc_port_stat netc_pmac_counters[] = { + { NETC_PM_ROCT(1), "pMAC rx octets" }, + { NETC_PM_RVLAN(1), "pMAC rx VLAN frames" }, + { NETC_PM_RERR(1), "pMAC rx frame errors" }, + { NETC_PM_RUCA(1), "pMAC rx unicast frames" }, + { NETC_PM_RDRP(1), "pMAC rx dropped packets" }, + { NETC_PM_RPKT(1), "pMAC rx packets" }, + { NETC_PM_TOCT(1), "pMAC tx octets" }, + { NETC_PM_TVLAN(1), "pMAC tx VLAN frames" }, + { NETC_PM_TFCS(1), "pMAC tx FCS errors" }, + { NETC_PM_TUCA(1), "pMAC tx unicast frames" }, + { NETC_PM_TPKT(1), "pMAC tx packets" }, + { NETC_PM_TUND(1), "pMAC tx undersized packets" }, + { NETC_PM_TIOCT(1), "pMAC tx invalid octets" }, +}; + +static void netc_port_pause_stats(struct netc_port *np, int mac, + struct ethtool_pause_stats *stats) +{ + if (mac && !np->caps.pmac) + return; + + stats->tx_pause_frames = netc_port_rd64(np, NETC_PM_TXPF(mac)); + stats->rx_pause_frames = netc_port_rd64(np, NETC_PM_RXPF(mac)); +} + +void netc_port_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct net_device *ndev; + + switch (pause_stats->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + netc_port_pause_stats(np, 0, pause_stats); + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + netc_port_pause_stats(np, 1, pause_stats); + break; + case ETHTOOL_MAC_STATS_SRC_AGGREGATE: + ndev = dsa_to_port(ds, port)->user; + ethtool_aggregate_pause_stats(ndev, pause_stats); + break; + } +} + +static void netc_port_rmon_stats(struct netc_port *np, int mac, + struct ethtool_rmon_stats *stats) +{ + if (mac && !np->caps.pmac) + return; + + stats->undersize_pkts = netc_port_rd64(np, NETC_PM_RUND(mac)); + stats->oversize_pkts = netc_port_rd64(np, NETC_PM_ROVR(mac)); + stats->fragments = netc_port_rd64(np, NETC_PM_RFRG(mac)); + stats->jabbers = netc_port_rd64(np, NETC_PM_RJBR(mac)); + + stats->hist[0] = netc_port_rd64(np, NETC_PM_R64(mac)); + stats->hist[1] = netc_port_rd64(np, NETC_PM_R127(mac)); + stats->hist[2] = netc_port_rd64(np, NETC_PM_R255(mac)); + stats->hist[3] = netc_port_rd64(np, NETC_PM_R511(mac)); + stats->hist[4] = netc_port_rd64(np, NETC_PM_R1023(mac)); + stats->hist[5] = netc_port_rd64(np, NETC_PM_R1522(mac)); + stats->hist[6] = netc_port_rd64(np, NETC_PM_R1523X(mac)); + + stats->hist_tx[0] = netc_port_rd64(np, NETC_PM_T64(mac)); + stats->hist_tx[1] = netc_port_rd64(np, NETC_PM_T127(mac)); + stats->hist_tx[2] = netc_port_rd64(np, NETC_PM_T255(mac)); + stats->hist_tx[3] = netc_port_rd64(np, NETC_PM_T511(mac)); + stats->hist_tx[4] = netc_port_rd64(np, NETC_PM_T1023(mac)); + stats->hist_tx[5] = netc_port_rd64(np, NETC_PM_T1522(mac)); + stats->hist_tx[6] = netc_port_rd64(np, NETC_PM_T1523X(mac)); +} + +void netc_port_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct net_device *ndev; + + *ranges = netc_rmon_ranges; + + switch (rmon_stats->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + netc_port_rmon_stats(np, 0, rmon_stats); + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + netc_port_rmon_stats(np, 1, rmon_stats); + break; + case ETHTOOL_MAC_STATS_SRC_AGGREGATE: + ndev = dsa_to_port(ds, port)->user; + ethtool_aggregate_rmon_stats(ndev, rmon_stats); + break; + } +} + +static void netc_port_ctrl_stats(struct netc_port *np, int mac, + struct ethtool_eth_ctrl_stats *stats) +{ + if (mac && !np->caps.pmac) + return; + + stats->MACControlFramesTransmitted = + netc_port_rd64(np, NETC_PM_TCNP(mac)); + stats->MACControlFramesReceived = + netc_port_rd64(np, NETC_PM_RCNP(mac)); +} + +void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct net_device *ndev; + + switch (ctrl_stats->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + netc_port_ctrl_stats(np, 0, ctrl_stats); + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + netc_port_ctrl_stats(np, 1, ctrl_stats); + break; + case ETHTOOL_MAC_STATS_SRC_AGGREGATE: + ndev = dsa_to_port(ds, port)->user; + ethtool_aggregate_ctrl_stats(ndev, ctrl_stats); + break; + } +} + +static void netc_port_mac_stats(struct netc_port *np, int mac, + struct ethtool_eth_mac_stats *stats) +{ + if (mac && !np->caps.pmac) + return; + + stats->FramesTransmittedOK = netc_port_rd64(np, NETC_PM_TFRM(mac)); + stats->SingleCollisionFrames = netc_port_rd64(np, NETC_PM_TSCOL(mac)); + stats->MultipleCollisionFrames = + netc_port_rd64(np, NETC_PM_TMCOL(mac)); + stats->FramesReceivedOK = netc_port_rd64(np, NETC_PM_RFRM(mac)); + stats->FrameCheckSequenceErrors = + netc_port_rd64(np, NETC_PM_RFCS(mac)); + stats->AlignmentErrors = netc_port_rd64(np, NETC_PM_RALN(mac)); + stats->OctetsTransmittedOK = netc_port_rd64(np, NETC_PM_TEOCT(mac)); + stats->FramesWithDeferredXmissions = + netc_port_rd64(np, NETC_PM_TDFR(mac)); + stats->LateCollisions = netc_port_rd64(np, NETC_PM_TLCOL(mac)); + stats->FramesAbortedDueToXSColls = + netc_port_rd64(np, NETC_PM_TECOL(mac)); + stats->FramesLostDueToIntMACXmitError = + netc_port_rd64(np, NETC_PM_TERR(mac)); + stats->OctetsReceivedOK = netc_port_rd64(np, NETC_PM_REOCT(mac)); + stats->FramesLostDueToIntMACRcvError = + netc_port_rd64(np, NETC_PM_RDRNTP(mac)); + stats->MulticastFramesXmittedOK = + netc_port_rd64(np, NETC_PM_TMCA(mac)); + stats->BroadcastFramesXmittedOK = + netc_port_rd64(np, NETC_PM_TBCA(mac)); + stats->MulticastFramesReceivedOK = + netc_port_rd64(np, NETC_PM_RMCA(mac)); + stats->BroadcastFramesReceivedOK = + netc_port_rd64(np, NETC_PM_RBCA(mac)); + stats->FramesWithExcessiveDeferral = + netc_port_rd64(np, NETC_PM_TEDFR(mac)); +} + +void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct net_device *ndev; + + switch (mac_stats->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + netc_port_mac_stats(np, 0, mac_stats); + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + netc_port_mac_stats(np, 1, mac_stats); + break; + case ETHTOOL_MAC_STATS_SRC_AGGREGATE: + ndev = dsa_to_port(ds, port)->user; + ethtool_aggregate_mac_stats(ndev, mac_stats); + break; + } +} + +int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + struct netc_port *np = NETC_PORT(ds, port); + int size; + + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; + + size = ARRAY_SIZE(netc_port_counters) + + ARRAY_SIZE(netc_emac_counters); + + if (np->caps.pmac) + size += ARRAY_SIZE(netc_pmac_counters); + + return size; +} + +void netc_port_get_strings(struct dsa_switch *ds, int port, + u32 sset, u8 *data) +{ + struct netc_port *np = NETC_PORT(ds, port); + int i; + + if (sset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++) + ethtool_cpy(&data, netc_port_counters[i].name); + + for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++) + ethtool_cpy(&data, netc_emac_counters[i].name); + + if (!np->caps.pmac) + return; + + for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++) + ethtool_cpy(&data, netc_pmac_counters[i].name); +} + +void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) +{ + struct netc_port *np = NETC_PORT(ds, port); + int i; + + for (i = 0; i < ARRAY_SIZE(netc_port_counters); i++) + *data++ = netc_port_rd(np, netc_port_counters[i].reg); + + for (i = 0; i < ARRAY_SIZE(netc_emac_counters); i++) + *data++ = netc_port_rd64(np, netc_emac_counters[i].reg); + + if (!np->caps.pmac) + return; + + for (i = 0; i < ARRAY_SIZE(netc_pmac_counters); i++) + *data++ = netc_port_rd64(np, netc_pmac_counters[i].reg); +} diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c new file mode 100644 index 000000000000..77077352c1a5 --- /dev/null +++ b/drivers/net/dsa/netc/netc_main.c @@ -0,0 +1,2524 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * NXP NETC switch driver + * Copyright 2025-2026 NXP + */ + +#include +#include +#include +#include +#include +#include + +#include "netc_switch.h" + +static struct netc_fdb_entry * +netc_lookup_fdb_entry(struct netc_switch *priv, + const unsigned char *addr, + u16 vid) +{ + struct netc_fdb_entry *entry; + + hlist_for_each_entry(entry, &priv->fdb_list, node) + if (ether_addr_equal(entry->keye.mac_addr, addr) && + le16_to_cpu(entry->keye.fid) == vid) + return entry; + + return NULL; +} + +static void netc_destroy_fdb_list(struct netc_switch *priv) +{ + struct netc_fdb_entry *entry; + struct hlist_node *tmp; + + hlist_for_each_entry_safe(entry, tmp, &priv->fdb_list, node) + netc_del_fdb_entry(entry); +} + +static struct netc_vlan_entry * +netc_lookup_vlan_entry(struct netc_switch *priv, u16 vid) +{ + struct netc_vlan_entry *entry; + + hlist_for_each_entry(entry, &priv->vlan_list, node) + if (entry->vid == vid) + return entry; + + return NULL; +} + +static void netc_destroy_vlan_list(struct netc_switch *priv) +{ + struct netc_vlan_entry *entry; + struct hlist_node *tmp; + + hlist_for_each_entry_safe(entry, tmp, &priv->vlan_list, node) + netc_del_vlan_entry(entry); +} + +static enum dsa_tag_protocol +netc_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprot) +{ + return DSA_TAG_PROTO_NETC; +} + +static void netc_port_rmw(struct netc_port *np, u32 reg, + u32 mask, u32 val) +{ + u32 old, new; + + WARN_ON((mask | val) != mask); + + old = netc_port_rd(np, reg); + new = (old & ~mask) | val; + if (new == old) + return; + + netc_port_wr(np, reg, new); +} + +static void netc_mac_port_wr(struct netc_port *np, u32 reg, u32 val) +{ + if (is_netc_pseudo_port(np)) + return; + + netc_port_wr(np, reg, val); + if (np->caps.pmac) + netc_port_wr(np, reg + NETC_PMAC_OFFSET, val); +} + +/* netc_mac_port_rmw() is used to synchronize the configurations of eMAC + * and pMAC to maintain consistency. This function should not be used if + * differentiated settings are required. + */ +static void netc_mac_port_rmw(struct netc_port *np, u32 reg, + u32 mask, u32 val) +{ + u32 old, new; + + if (is_netc_pseudo_port(np)) + return; + + WARN_ON((mask | val) != mask); + + old = netc_port_rd(np, reg); + new = (old & ~mask) | val; + if (new == old) + return; + + netc_port_wr(np, reg, new); + if (np->caps.pmac) + netc_port_wr(np, reg + NETC_PMAC_OFFSET, new); +} + +static void netc_port_get_capability(struct netc_port *np) +{ + u32 val; + + val = netc_port_rd(np, NETC_PMCAPR); + if (val & PMCAPR_HD) + np->caps.half_duplex = true; + + if (FIELD_GET(PMCAPR_FP, val) == FP_SUPPORT) + np->caps.pmac = true; + + val = netc_port_rd(np, NETC_PCAPR); + if (val & PCAPR_LINK_TYPE) + np->caps.pseudo_link = true; +} + +static int netc_port_get_info_from_dt(struct netc_port *np, + struct device_node *node, + struct device *dev) +{ + if (of_find_property(node, "clock-names", NULL)) { + np->ref_clk = devm_get_clk_from_child(dev, node, "ref"); + if (IS_ERR(np->ref_clk)) { + dev_err(dev, "Port %d cannot get reference clock\n", + np->dp->index); + return PTR_ERR(np->ref_clk); + } + } + + return 0; +} + +static int netc_port_create_emdio_bus(struct netc_port *np, + struct device_node *node) +{ + struct netc_switch *priv = np->switch_priv; + struct enetc_mdio_priv *mdio_priv; + struct device *dev = priv->dev; + struct enetc_hw *hw; + struct mii_bus *bus; + int err; + + hw = enetc_hw_alloc(dev, np->iobase); + if (IS_ERR(hw)) + return dev_err_probe(dev, PTR_ERR(hw), + "Failed to allocate enetc_hw\n"); + + bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); + if (!bus) + return -ENOMEM; + + bus->name = "NXP NETC switch external MDIO Bus"; + bus->read = enetc_mdio_read_c22; + bus->write = enetc_mdio_write_c22; + bus->read_c45 = enetc_mdio_read_c45; + bus->write_c45 = enetc_mdio_write_c45; + bus->parent = dev; + mdio_priv = bus->priv; + mdio_priv->hw = hw; + mdio_priv->mdio_base = NETC_EMDIO_BASE; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-p%d-emdio", + dev_name(dev), np->dp->index); + + err = devm_of_mdiobus_register(dev, bus, node); + if (err) + return dev_err_probe(dev, err, + "Cannot register EMDIO bus\n"); + + np->emdio = bus; + + return 0; +} + +static int netc_port_create_mdio_bus(struct netc_port *np, + struct device_node *node) +{ + struct device_node *mdio_node; + int err; + + mdio_node = of_get_child_by_name(node, "mdio"); + if (mdio_node) { + err = netc_port_create_emdio_bus(np, mdio_node); + of_node_put(mdio_node); + if (err) + return err; + } + + return 0; +} + +static int netc_init_switch_id(struct netc_switch *priv) +{ + struct netc_switch_regs *regs = &priv->regs; + struct dsa_switch *ds = priv->ds; + + /* The value of 0 is reserved for the VEPA switch and cannot + * be used. So 'dsa,member' is a required property for NETC + * switch, the member is used to specify the switch ID, which + * cannot be zero. This way, the hardware switch ID and the + * software switch ID are consistent. + */ + if (ds->index > FIELD_MAX(SWCR_SWID) || !ds->index) { + dev_err(priv->dev, "Switch index %d out of range\n", + ds->index); + return -ERANGE; + } + + netc_base_wr(regs, NETC_SWCR, ds->index); + + return 0; +} + +static void netc_get_switch_capabilities(struct netc_switch *priv) +{ + struct netc_switch_regs *regs = &priv->regs; + u32 val; + + val = netc_base_rd(regs, NETC_HTMCAPR); + priv->htmcapr_num_words = FIELD_GET(HTMCAPR_NUM_WORDS, val); + + val = netc_base_rd(regs, NETC_BPCAPR); + priv->num_bp = FIELD_GET(BPCAPR_NUM_BP, val); +} + +static int netc_init_all_ports(struct netc_switch *priv) +{ + struct device *dev = priv->dev; + struct netc_port *np; + struct dsa_port *dp; + int ett_offset = 0; + int err; + + priv->ports = devm_kcalloc(dev, priv->info->num_ports, + sizeof(struct netc_port *), + GFP_KERNEL); + if (!priv->ports) + return -ENOMEM; + + /* Some DSA interfaces may set the port even it is disabled, such + * as .port_disable(), .port_stp_state_set() and so on. To avoid + * crash caused by accessing NULL port pointer, each port is + * allocated its own memory. Otherwise, we need to check whether + * the port pointer is NULL in these interfaces. The latter is + * difficult for us to cover. + */ + for (int i = 0; i < priv->info->num_ports; i++) { + np = devm_kzalloc(dev, sizeof(*np), GFP_KERNEL); + if (!np) + return -ENOMEM; + + np->switch_priv = priv; + np->iobase = priv->regs.port + PORT_IOBASE(i); + netc_port_get_capability(np); + priv->ports[i] = np; + } + + dsa_switch_for_each_available_port(dp, priv->ds) { + np = priv->ports[dp->index]; + np->dp = dp; + np->ett_offset = ett_offset++; + priv->port_bitmap |= BIT(dp->index); + + err = netc_port_get_info_from_dt(np, dp->dn, dev); + if (err) + return err; + + if (dsa_port_is_user(dp)) { + err = netc_port_create_mdio_bus(np, dp->dn); + if (err) { + dev_err(dev, "Failed to create MDIO bus\n"); + return err; + } + } + } + + return 0; +} + +static void netc_init_ntmp_tbl_versions(struct netc_switch *priv) +{ + struct ntmp_user *ntmp = &priv->ntmp; + + /* All tables default to version 0 */ + memset(&ntmp->tbl, 0, sizeof(ntmp->tbl)); +} + +static int netc_init_all_cbdrs(struct netc_switch *priv) +{ + struct netc_switch_regs *regs = &priv->regs; + struct ntmp_user *ntmp = &priv->ntmp; + int i, err; + + ntmp->cbdr_num = NETC_CBDR_NUM; + ntmp->dev = priv->dev; + ntmp->ring = devm_kcalloc(ntmp->dev, ntmp->cbdr_num, + sizeof(struct netc_cbdr), + GFP_KERNEL); + if (!ntmp->ring) + return -ENOMEM; + + for (i = 0; i < ntmp->cbdr_num; i++) { + struct netc_cbdr *cbdr = &ntmp->ring[i]; + struct netc_cbdr_regs cbdr_regs; + + cbdr_regs.pir = regs->base + NETC_CBDRPIR(i); + cbdr_regs.cir = regs->base + NETC_CBDRCIR(i); + cbdr_regs.mr = regs->base + NETC_CBDRMR(i); + cbdr_regs.bar0 = regs->base + NETC_CBDRBAR0(i); + cbdr_regs.bar1 = regs->base + NETC_CBDRBAR1(i); + cbdr_regs.lenr = regs->base + NETC_CBDRLENR(i); + + err = ntmp_init_cbdr(cbdr, ntmp->dev, &cbdr_regs); + if (err) + goto free_cbdrs; + } + + return 0; + +free_cbdrs: + for (i--; i >= 0; i--) + ntmp_free_cbdr(&ntmp->ring[i]); + + return err; +} + +static void netc_remove_all_cbdrs(struct netc_switch *priv) +{ + struct ntmp_user *ntmp = &priv->ntmp; + + for (int i = 0; i < NETC_CBDR_NUM; i++) + ntmp_free_cbdr(&ntmp->ring[i]); +} + +static u32 netc_num_available_ports(struct netc_switch *priv) +{ + struct dsa_port *dp; + u32 num_ports = 0; + + dsa_switch_for_each_available_port(dp, priv->ds) + num_ports++; + + return num_ports; +} + +static int netc_init_ntmp_bitmap_sizes(struct netc_switch *priv) +{ + u32 num_ports = netc_num_available_ports(priv); + struct netc_switch_regs *regs = &priv->regs; + struct ntmp_user *ntmp = &priv->ntmp; + u32 val; + + if (!num_ports) + return -EINVAL; + + val = netc_base_rd(regs, NETC_ETTCAPR); + ntmp->ett_bitmap_size = NETC_GET_NUM_ENTRIES(val) / num_ports; + if (!ntmp->ett_bitmap_size) + return -EINVAL; + + val = netc_base_rd(regs, NETC_ECTCAPR); + ntmp->ect_bitmap_size = NETC_GET_NUM_ENTRIES(val) / num_ports; + if (!ntmp->ect_bitmap_size) + return -EINVAL; + + return 0; +} + +static int netc_init_ntmp_bitmaps(struct netc_switch *priv) +{ + struct ntmp_user *ntmp = &priv->ntmp; + + ntmp->ett_gid_bitmap = bitmap_zalloc(ntmp->ett_bitmap_size, + GFP_KERNEL); + if (!ntmp->ett_gid_bitmap) + return -ENOMEM; + + ntmp->ect_gid_bitmap = bitmap_zalloc(ntmp->ect_bitmap_size, + GFP_KERNEL); + if (!ntmp->ect_gid_bitmap) + goto free_ett_gid_bitmap; + + return 0; + +free_ett_gid_bitmap: + bitmap_free(ntmp->ett_gid_bitmap); + ntmp->ett_gid_bitmap = NULL; + + return -ENOMEM; +} + +static void netc_free_ntmp_bitmaps(struct netc_switch *priv) +{ + struct ntmp_user *ntmp = &priv->ntmp; + + bitmap_free(ntmp->ect_gid_bitmap); + ntmp->ect_gid_bitmap = NULL; + + bitmap_free(ntmp->ett_gid_bitmap); + ntmp->ett_gid_bitmap = NULL; +} + +static int netc_init_ntmp_user(struct netc_switch *priv) +{ + int err; + + netc_init_ntmp_tbl_versions(priv); + + err = netc_init_ntmp_bitmap_sizes(priv); + if (err) + return err; + + err = netc_init_ntmp_bitmaps(priv); + if (err) + return err; + + err = netc_init_all_cbdrs(priv); + if (err) + goto free_ntmp_bitmaps; + + return 0; + +free_ntmp_bitmaps: + netc_free_ntmp_bitmaps(priv); + + return err; +} + +static void netc_free_ntmp_user(struct netc_switch *priv) +{ + netc_remove_all_cbdrs(priv); + netc_free_ntmp_bitmaps(priv); +} + +static void netc_clean_fdbt_ageing_entries(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct netc_switch *priv; + + priv = container_of(dwork, struct netc_switch, fdbt_ageing_work); + + /* Update the activity element in FDB table */ + mutex_lock(&priv->fdbt_lock); + ntmp_fdbt_update_activity_element(&priv->ntmp); + /* Delete the ageing entries after the activity element is updated */ + ntmp_fdbt_delete_ageing_entries(&priv->ntmp, NETC_FDBT_AGEING_THRESH); + mutex_unlock(&priv->fdbt_lock); + + if (atomic_read(&priv->br_cnt)) + schedule_delayed_work(&priv->fdbt_ageing_work, + READ_ONCE(priv->fdbt_ageing_delay)); +} + +static void netc_switch_dos_default_config(struct netc_switch *priv) +{ + struct netc_switch_regs *regs = &priv->regs; + u32 val; + + val = DOSL2CR_SAMEADDR | DOSL2CR_MSAMCC; + netc_base_wr(regs, NETC_DOSL2CR, val); + + val = DOSL3CR_SAMEADDR | DOSL3CR_IPSAMCC; + netc_base_wr(regs, NETC_DOSL3CR, val); +} + +static void netc_switch_vfht_default_config(struct netc_switch *priv) +{ + struct netc_switch_regs *regs = &priv->regs; + u32 val; + + val = netc_base_rd(regs, NETC_VFHTDECR2); + + /* If no match is found in the VLAN Filter table, then VFHTDECR2[MLO] + * will take effect. VFHTDECR2[MLO] is set to "Software MAC learning + * secure" by default. Notice BPCR[MLO] will override VFHTDECR2[MLO] + * if its value is not zero. + */ + val = u32_replace_bits(val, MLO_SW_SEC, VFHTDECR2_MLO); + val = u32_replace_bits(val, MFO_NO_MATCH_DISCARD, VFHTDECR2_MFO); + netc_base_wr(regs, NETC_VFHTDECR2, val); +} + +static void netc_port_set_max_frame_size(struct netc_port *np, + u32 max_frame_size) +{ + netc_mac_port_wr(np, NETC_PM_MAXFRM(0), + max_frame_size & PM_MAXFRAM); +} + +static void netc_switch_fixed_config(struct netc_switch *priv) +{ + netc_switch_dos_default_config(priv); + netc_switch_vfht_default_config(priv); +} + +static void netc_port_set_tc_max_sdu(struct netc_port *np, + int tc, u32 max_sdu) +{ + u32 val = FIELD_PREP(PTCTMSDUR_MAXSDU, max_sdu) | + FIELD_PREP(PTCTMSDUR_SDU_TYPE, SDU_TYPE_MPDU); + + netc_port_wr(np, NETC_PTCTMSDUR(tc), val); +} + +static void netc_port_set_all_tc_msdu(struct netc_port *np) +{ + for (int tc = 0; tc < NETC_TC_NUM; tc++) + netc_port_set_tc_max_sdu(np, tc, NETC_MAX_FRAME_LEN); +} + +static void netc_port_set_mlo(struct netc_port *np, enum netc_mlo mlo) +{ + netc_port_rmw(np, NETC_BPCR, BPCR_MLO, FIELD_PREP(BPCR_MLO, mlo)); +} + +static void netc_port_set_pvid(struct netc_port *np, u16 pvid) +{ + netc_port_rmw(np, NETC_BPDVR, BPDVR_VID, pvid); +} + +static void netc_port_set_vlan_aware(struct netc_port *np, bool aware) +{ + netc_port_rmw(np, NETC_BPDVR, BPDVR_RXVAM, + aware ? 0 : BPDVR_RXVAM); +} + +static void netc_port_fixed_config(struct netc_port *np) +{ + /* Default IPV and DR setting */ + netc_port_rmw(np, NETC_PQOSMR, PQOSMR_VS | PQOSMR_VE, + PQOSMR_VS | PQOSMR_VE); + + /* Enable L2 and L3 DOS */ + netc_port_rmw(np, NETC_PCR, PCR_L2DOSE | PCR_L3DOSE, + PCR_L2DOSE | PCR_L3DOSE); + + /* Set the quanta value of TX PAUSE frame */ + netc_mac_port_wr(np, NETC_PM_PAUSE_QUANTA(0), NETC_PAUSE_QUANTA); + + /* When a quanta timer counts down and reaches this value, + * the MAC sends a refresh PAUSE frame with the programmed + * full quanta value if a pause condition still exists. + */ + netc_mac_port_wr(np, NETC_PM_PAUSE_THRESH(0), NETC_PAUSE_THRESH); +} + +static void netc_port_default_config(struct netc_port *np) +{ + netc_port_fixed_config(np); + + /* Default VLAN unaware */ + netc_port_set_vlan_aware(np, false); + + if (dsa_port_is_cpu(np->dp)) + /* For CPU port, source port pruning is disabled */ + netc_port_rmw(np, NETC_BPCR, BPCR_SRCPRND, BPCR_SRCPRND); + else + netc_port_set_mlo(np, MLO_DISABLE); + + netc_port_set_max_frame_size(np, NETC_MAX_FRAME_LEN); + netc_port_set_all_tc_msdu(np); +} + +static u32 netc_available_port_bitmap(struct netc_switch *priv) +{ + struct dsa_port *dp; + u32 bitmap = 0; + + dsa_switch_for_each_available_port(dp, priv->ds) + bitmap |= BIT(dp->index); + + return bitmap; +} + +static int netc_add_standalone_vlan_entry(struct netc_switch *priv) +{ + u32 bitmap_stg = VFT_STG_ID(0) | netc_available_port_bitmap(priv); + struct vft_cfge_data *cfge; + u16 cfg; + int err; + + cfge = kzalloc_obj(*cfge); + if (!cfge) + return -ENOMEM; + + cfge->bitmap_stg = cpu_to_le32(bitmap_stg); + cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID); + cfge->fid = cpu_to_le16(NETC_STANDALONE_PVID); + + /* For standalone ports, MAC learning needs to be disabled, so frames + * from other user ports will not be forwarded to the standalone ports, + * because there are no FDB entries on the standalone ports. Also, the + * frames received by the standalone ports cannot be flooded to other + * ports, so MAC forwarding option needs to be set to + * MFO_NO_MATCH_DISCARD, so the frames will be discarded rather than + * flooding to other ports. + */ + cfg = FIELD_PREP(VFT_MLO, MLO_DISABLE) | + FIELD_PREP(VFT_MFO, MFO_NO_MATCH_DISCARD); + cfge->cfg = cpu_to_le16(cfg); + + err = ntmp_vft_add_entry(&priv->ntmp, NETC_STANDALONE_PVID, cfge); + if (err) + dev_err(priv->dev, + "Failed to add standalone VLAN entry\n"); + + kfree(cfge); + + return err; +} + +static int netc_port_add_fdb_entry(struct netc_port *np, + const unsigned char *addr, u16 vid) +{ + struct netc_switch *priv = np->switch_priv; + struct netc_fdb_entry *entry; + struct fdbt_keye_data *keye; + struct fdbt_cfge_data *cfge; + int port = np->dp->index; + u32 cfg = 0; + int err; + + entry = kzalloc_obj(*entry); + if (!entry) + return -ENOMEM; + + keye = &entry->keye; + cfge = &entry->cfge; + ether_addr_copy(keye->mac_addr, addr); + keye->fid = cpu_to_le16(vid); + + cfge->port_bitmap = cpu_to_le32(BIT(port)); + cfge->cfg = cpu_to_le32(cfg); + cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID); + + err = ntmp_fdbt_add_entry(&priv->ntmp, &entry->entry_id, keye, cfge); + if (err) { + kfree(entry); + + return err; + } + + netc_add_fdb_entry(priv, entry); + + return 0; +} + +static int netc_port_set_fdb_entry(struct netc_port *np, + const unsigned char *addr, u16 vid) +{ + struct netc_switch *priv = np->switch_priv; + struct netc_fdb_entry *entry; + struct fdbt_cfge_data *cfge; + int port = np->dp->index; + __le32 old_port_bitmap; + int err = 0; + + mutex_lock(&priv->fdbt_lock); + + entry = netc_lookup_fdb_entry(priv, addr, vid); + if (!entry) { + err = netc_port_add_fdb_entry(np, addr, vid); + if (err) + dev_err(priv->dev, + "Failed to add FDB entry on port %d\n", + port); + + goto unlock_fdbt; + } + + cfge = &entry->cfge; + /* If the entry already exists on the port, return 0 directly */ + if (unlikely(cfge->port_bitmap & cpu_to_le32(BIT(port)))) + goto unlock_fdbt; + + /* If the entry already exists, but not on this port, we need to + * update the port bitmap. In general, it should only be valid + * for multicast or broadcast address. + */ + old_port_bitmap = cfge->port_bitmap; + if (is_multicast_ether_addr(addr)) + cfge->port_bitmap |= cpu_to_le32(BIT(port)); + else + cfge->port_bitmap = cpu_to_le32(BIT(port)); + + err = ntmp_fdbt_update_entry(&priv->ntmp, entry->entry_id, cfge); + if (err) { + cfge->port_bitmap = old_port_bitmap; + dev_err(priv->dev, "Failed to set FDB entry on port %d\n", + port); + } + +unlock_fdbt: + mutex_unlock(&priv->fdbt_lock); + + return err; +} + +static int netc_port_del_fdb_entry(struct netc_port *np, + const unsigned char *addr, u16 vid) +{ + struct netc_switch *priv = np->switch_priv; + struct ntmp_user *ntmp = &priv->ntmp; + struct netc_fdb_entry *entry; + struct fdbt_cfge_data *cfge; + int port = np->dp->index; + int err = 0; + + mutex_lock(&priv->fdbt_lock); + + entry = netc_lookup_fdb_entry(priv, addr, vid); + if (unlikely(!entry)) + /* The hardware-learned dynamic FDB entries cannot be deleted + * through .port_fdb_del() interface. + * For NTF_MASTER path: Since hardware-learned dynamic FDB + * entries are never synchronized back to the bridge software + * database. br_fdb_delete() -> br_fdb_find() cannot find the + * FDB entry, so .port_fdb_del() will not be called. + * For NTF_SELF path: dsa_user_netdev_ops does not implement + * ndo_fdb_del(), so rtnl_fdb_del() falls back to + * ndo_dflt_fdb_del(), which only supports NUD_PERMANENT static + * entries and rejects all others with -EINVAL. + */ + goto unlock_fdbt; + + cfge = &entry->cfge; + if (unlikely(!(cfge->port_bitmap & cpu_to_le32(BIT(port))))) + goto unlock_fdbt; + + if (cfge->port_bitmap != cpu_to_le32(BIT(port))) { + /* If the entry also exists on other ports, we need to + * update the entry in the FDB table. + */ + cfge->port_bitmap &= cpu_to_le32(~BIT(port)); + err = ntmp_fdbt_update_entry(ntmp, entry->entry_id, cfge); + if (err) { + cfge->port_bitmap |= cpu_to_le32(BIT(port)); + goto unlock_fdbt; + } + } else { + /* If the entry only exists on this port, just delete + * it from the FDB table. + */ + err = ntmp_fdbt_delete_entry(ntmp, entry->entry_id); + if (err) + goto unlock_fdbt; + + netc_del_fdb_entry(entry); + } + +unlock_fdbt: + mutex_unlock(&priv->fdbt_lock); + + return err; +} + +static int netc_add_standalone_fdb_bcast_entry(struct netc_switch *priv) +{ + const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct dsa_port *dp, *cpu_dp = NULL; + + dsa_switch_for_each_cpu_port(dp, priv->ds) { + /* The switch has only one CPU port, so only need to find + * the first CPU port to break out of the loop. + */ + cpu_dp = dp; + break; + } + + if (!cpu_dp) + return -ENODEV; + + /* If the user port acts as a standalone port, then its PVID is 0, + * MLO is set to "disable MAC learning" and MFO is set to "discard + * frames if no matching entry found in FDB table". Therefore, we + * need to add a broadcast FDB entry on the CPU port so that the + * broadcast frames received on the user port can be forwarded to + * the CPU port. + */ + return netc_port_set_fdb_entry(NETC_PORT(priv->ds, cpu_dp->index), + bcast, NETC_STANDALONE_PVID); +} + +static void netc_port_set_pbpmcr(struct netc_port *np, u64 mapping) +{ + u32 pbpmcr0 = lower_32_bits(mapping); + u32 pbpmcr1 = upper_32_bits(mapping); + + netc_port_wr(np, NETC_PBPMCR0, pbpmcr0); + netc_port_wr(np, NETC_PBPMCR1, pbpmcr1); +} + +static void netc_ipv_to_buffer_pool_mapping(struct netc_switch *priv) +{ + int bp_per_port = priv->num_bp / priv->info->num_ports; + int q = NETC_IPV_NUM / bp_per_port; + int r = NETC_IPV_NUM % bp_per_port; + int num = q + r; + + /* IPV-to-buffer-pool mapping per port: + * Each port is allocated 'bp_per_port' buffer pools and supports 8 + * IPVs, where a higher IPV indicates a higher frame priority. Each + * IPV can be mapped to only one buffer pool, from hardware design + * perspective, bp_per_port will not be greater than 8. So 'q' will + * not be 0. + * + * The mapping rule is as follows: + * - The first 'num' IPVs share the port's first buffer pool (index + * 'base_id'). + * - After that, every 'q' IPVs share one buffer pool, with pool + * indices increasing sequentially. + */ + for (int i = 0; i < priv->info->num_ports; i++) { + u32 base_id = i * bp_per_port; + u32 bp_id = base_id; + u64 mapping = 0; + + for (int ipv = 0; ipv < NETC_IPV_NUM; ipv++) { + /* Update the buffer pool index */ + if (ipv >= num) + bp_id = base_id + ((ipv - num) / q) + 1; + + mapping |= (u64)bp_id << (ipv * 8); + } + + netc_port_set_pbpmcr(priv->ports[i], mapping); + } +} + +static int netc_switch_bpt_default_config(struct netc_switch *priv) +{ + if (priv->num_bp < priv->info->num_ports) + return -EINVAL; + + priv->bpt_list = devm_kcalloc(priv->dev, priv->num_bp, + sizeof(struct bpt_cfge_data), + GFP_KERNEL); + if (!priv->bpt_list) + return -ENOMEM; + + /* Initialize the maximum threshold of each buffer pool entry */ + for (int i = 0; i < priv->num_bp; i++) { + struct bpt_cfge_data *cfge = &priv->bpt_list[i]; + int err; + + cfge->max_thresh = cpu_to_le16(NETC_BP_THRESH); + err = ntmp_bpt_update_entry(&priv->ntmp, i, cfge); + if (err) + return err; + } + + netc_ipv_to_buffer_pool_mapping(priv); + + return 0; +} + +static int netc_setup(struct dsa_switch *ds) +{ + struct netc_switch *priv = ds->priv; + struct dsa_port *dp; + int err; + + err = netc_init_switch_id(priv); + if (err) + return err; + + netc_get_switch_capabilities(priv); + + err = netc_init_all_ports(priv); + if (err) + return err; + + err = netc_init_ntmp_user(priv); + if (err) + return err; + + INIT_HLIST_HEAD(&priv->fdb_list); + mutex_init(&priv->fdbt_lock); + priv->fdbt_ageing_delay = NETC_FDBT_AGEING_DELAY; + atomic_set(&priv->br_cnt, 0); + INIT_DELAYED_WORK(&priv->fdbt_ageing_work, + netc_clean_fdbt_ageing_entries); + INIT_HLIST_HEAD(&priv->vlan_list); + mutex_init(&priv->vft_lock); + + netc_switch_fixed_config(priv); + + /* default setting for ports */ + dsa_switch_for_each_available_port(dp, ds) + netc_port_default_config(priv->ports[dp->index]); + + err = netc_switch_bpt_default_config(priv); + if (err) + goto free_lock_and_ntmp_user; + + err = netc_add_standalone_vlan_entry(priv); + if (err) + goto free_lock_and_ntmp_user; + + err = netc_add_standalone_fdb_bcast_entry(priv); + if (err) + goto free_lock_and_ntmp_user; + + return 0; + +free_lock_and_ntmp_user: + /* No need to clear the hardware state, netc_setup() is only called + * when the driver is bound, and FLR will be performed to reset the + * hardware state. + */ + mutex_destroy(&priv->fdbt_lock); + mutex_destroy(&priv->vft_lock); + netc_free_ntmp_user(priv); + + return err; +} + +static void netc_destroy_all_lists(struct netc_switch *priv) +{ + netc_destroy_fdb_list(priv); + mutex_destroy(&priv->fdbt_lock); + netc_destroy_vlan_list(priv); + mutex_destroy(&priv->vft_lock); +} + +static void netc_free_host_flood_rules(struct netc_switch *priv) +{ + struct dsa_port *dp; + + dsa_switch_for_each_user_port(dp, priv->ds) { + struct netc_port *np = priv->ports[dp->index]; + + /* No need to clear the hardware IPFT entry. Because PCIe + * FLR will be performed when the switch is re-registered, + * it will reset hardware state. So only need to free the + * memory to avoid memory leak. + */ + kfree(np->host_flood); + np->host_flood = NULL; + } +} + +static void netc_teardown(struct dsa_switch *ds) +{ + struct netc_switch *priv = ds->priv; + + disable_delayed_work_sync(&priv->fdbt_ageing_work); + netc_destroy_all_lists(priv); + netc_free_host_flood_rules(priv); + netc_free_ntmp_user(priv); +} + +static bool netc_port_is_emdio_consumer(struct device_node *node) +{ + struct device_node *mdio_node; + + /* If the port node has phy-handle property and it does + * not contain a mdio child node, then the port is the + * EMDIO consumer. + */ + mdio_node = of_get_child_by_name(node, "mdio"); + if (!mdio_node) + return true; + + of_node_put(mdio_node); + + return false; +} + +/* Currently, phylink_of_phy_connect() is called by dsa_user_create(), + * so if the switch uses the external MDIO controller (like the EMDIO + * function) to manage the external PHYs. The MDIO bus may not be + * created when phylink_of_phy_connect() is called, so it will return + * an error and cause the switch driver to fail to probe. + * This workaround can be removed when DSA phylink_of_phy_connect() + * calls are moved from probe() to ndo_open(). + */ +static int netc_switch_check_emdio_is_ready(struct device *dev) +{ + struct device_node *ports, *phy_node; + struct phy_device *phydev; + int err = 0; + + ports = of_get_child_by_name(dev->of_node, "ethernet-ports"); + if (!ports) { + dev_err(dev, "Cannot find the ethernet-ports node\n"); + return -EINVAL; + } + + for_each_available_child_of_node_scoped(ports, child) { + /* If the node does not have phy-handle property, then the + * port does not connect to a PHY, so the port is not the + * EMDIO consumer. + */ + phy_node = of_parse_phandle(child, "phy-handle", 0); + if (!phy_node) + continue; + + /* Note that from the hardware perspective, the switch ports + * do not support sharing the MDIO bus defined under one port. + * Each port can only access its own external PHY through its + * port MDIO bus. + */ + if (!netc_port_is_emdio_consumer(child)) { + of_node_put(phy_node); + continue; + } + + phydev = of_phy_find_device(phy_node); + of_node_put(phy_node); + if (!phydev) { + err = -EPROBE_DEFER; + goto out; + } + + put_device(&phydev->mdio.dev); + } + +out: + of_node_put(ports); + + return err; +} + +static int netc_switch_pci_init(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct netc_switch_regs *regs; + struct netc_switch *priv; + void __iomem *base; + int err; + + pcie_flr(pdev); + err = pcim_enable_device(pdev); + if (err) + return dev_err_probe(dev, err, "Failed to enable device\n"); + + err = pcim_request_all_regions(pdev, KBUILD_MODNAME); + if (err) + return dev_err_probe(dev, err, "Failed to request regions\n"); + + /* The command BD rings and NTMP tables need DMA. No need to check + * the return value, because it never returns fail when the mask is + * DMA_BIT_MASK(64), see dma-api-howto.rst. + */ + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + + if (pci_resource_len(pdev, NETC_REGS_BAR) < NETC_REGS_SIZE) { + return dev_err_probe(dev, -EINVAL, + "Invalid register space size\n"); + } + + base = pcim_iomap(pdev, NETC_REGS_BAR, 0); + if (!base) + return dev_err_probe(dev, -ENXIO, "pcim_iomap() failed\n"); + + pci_set_master(pdev); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdev = pdev; + priv->dev = dev; + + regs = &priv->regs; + regs->base = base; + regs->port = regs->base + NETC_REGS_PORT_BASE; + regs->global = regs->base + NETC_REGS_GLOBAL_BASE; + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void netc_switch_get_ip_revision(struct netc_switch *priv) +{ + struct netc_switch_regs *regs = &priv->regs; + u32 val = netc_glb_rd(regs, NETC_IPBRR0); + + priv->revision = FIELD_GET(IPBRR0_IP_REV, val); +} + +static void netc_init_ett_cfge(struct ett_cfge_data *cfge, + bool untagged, u32 ect_eid) +{ + u32 vuda_sqta = FMTEID_VUDA_SQTA; + u16 efm_cfg = 0; + + if (ect_eid != NTMP_NULL_ENTRY_ID) { + /* Increase egress frame counter */ + efm_cfg |= FIELD_PREP(ETT_ECA, ETT_ECA_INC); + cfge->ec_eid = cpu_to_le32(ect_eid); + } + + /* If egress rule is VLAN untagged */ + if (untagged) { + /* delete outer VLAN tag */ + vuda_sqta |= FIELD_PREP(FMTEID_VUDA, FMTEID_VUDA_DEL_OTAG); + /* length change: twos-complement notation */ + efm_cfg |= FIELD_PREP(ETT_EFM_LEN_CHANGE, + ETT_FRM_LEN_DEL_VLAN); + } + + cfge->efm_eid = cpu_to_le32(vuda_sqta); + cfge->efm_cfg = cpu_to_le16(efm_cfg); +} + +static int netc_add_ett_entry(struct netc_switch *priv, bool untagged, + u32 ett_eid, u32 ect_eid) +{ + struct ntmp_user *ntmp = &priv->ntmp; + struct ett_cfge_data cfge = {}; + + netc_init_ett_cfge(&cfge, untagged, ect_eid); + + return ntmp_ett_add_entry(ntmp, ett_eid, &cfge); +} + +static int netc_update_ett_entry(struct netc_switch *priv, bool untagged, + u32 ett_eid, u32 ect_eid) +{ + struct ntmp_user *ntmp = &priv->ntmp; + struct ett_cfge_data cfge = {}; + + netc_init_ett_cfge(&cfge, untagged, ect_eid); + + return ntmp_ett_update_entry(ntmp, ett_eid, &cfge); +} + +static int netc_add_ett_group_entries(struct netc_switch *priv, + u32 untagged_port_bitmap, + u32 ett_base_eid, + u32 ect_base_eid) +{ + struct netc_port **ports = priv->ports; + u32 ett_eid, ect_eid; + bool untagged; + int i, err; + + for (i = 0; i < priv->info->num_ports; i++) { + if (!ports[i]->dp) + continue; + + untagged = !!(untagged_port_bitmap & BIT(i)); + ett_eid = ett_base_eid + ports[i]->ett_offset; + ect_eid = NTMP_NULL_ENTRY_ID; + if (ect_base_eid != NTMP_NULL_ENTRY_ID) + ect_eid = ect_base_eid + ports[i]->ett_offset; + + err = netc_add_ett_entry(priv, untagged, ett_eid, ect_eid); + if (err) + goto clear_ett_entries; + } + + return 0; + +clear_ett_entries: + while (--i >= 0) { + if (!ports[i]->dp) + continue; + + ett_eid = ett_base_eid + ports[i]->ett_offset; + ntmp_ett_delete_entry(&priv->ntmp, ett_eid); + } + + return err; +} + +static int netc_add_vlan_egress_rule(struct netc_switch *priv, + struct netc_vlan_entry *entry) +{ + u32 num_ports = netc_num_available_ports(priv); + struct ntmp_user *ntmp = &priv->ntmp; + u32 ect_eid = NTMP_NULL_ENTRY_ID; + u32 ett_eid, ett_gid, ect_gid; + int err; + + /* Step 1: Find available egress counter table entries and update + * these entries. + */ + ect_gid = ntmp_lookup_free_eid(ntmp->ect_gid_bitmap, + ntmp->ect_bitmap_size); + if (ect_gid == NTMP_NULL_ENTRY_ID) { + dev_info(priv->dev, + "No egress counter table entries available\n"); + } else { + ect_eid = ect_gid * num_ports; + for (int i = 0; i < num_ports; i++) + /* There is no need to check the return value, the only + * issue is that the entry's counter might be inaccurate, + * but it will not affect the functionality, it is only + * for future debugging. + */ + ntmp_ect_update_entry(ntmp, ect_eid + i); + } + + /* Step 2: Find available egress treatment table entries and add + * these entries. + */ + ett_gid = ntmp_lookup_free_eid(ntmp->ett_gid_bitmap, + ntmp->ett_bitmap_size); + if (ett_gid == NTMP_NULL_ENTRY_ID) { + dev_err(priv->dev, + "No egress treatment table entries available\n"); + err = -ENOSPC; + goto clear_ect_gid; + } + + ett_eid = ett_gid * num_ports; + err = netc_add_ett_group_entries(priv, entry->untagged_port_bitmap, + ett_eid, ect_eid); + if (err) + goto clear_ett_gid; + + entry->cfge.et_eid = cpu_to_le32(ett_eid); + entry->ect_gid = ect_gid; + + return 0; + +clear_ett_gid: + ntmp_clear_eid_bitmap(ntmp->ett_gid_bitmap, ett_gid); + +clear_ect_gid: + if (ect_gid != NTMP_NULL_ENTRY_ID) + ntmp_clear_eid_bitmap(ntmp->ect_gid_bitmap, ect_gid); + + return err; +} + +static void netc_delete_vlan_egress_rule(struct netc_switch *priv, + struct netc_vlan_entry *entry) +{ + u32 num_ports = netc_num_available_ports(priv); + struct ntmp_user *ntmp = &priv->ntmp; + u32 ett_eid, ett_gid; + + ett_eid = le32_to_cpu(entry->cfge.et_eid); + if (ett_eid == NTMP_NULL_ENTRY_ID) + return; + + ett_gid = ett_eid / num_ports; + ntmp_clear_eid_bitmap(ntmp->ett_gid_bitmap, ett_gid); + for (int i = 0; i < num_ports; i++) + ntmp_ett_delete_entry(ntmp, ett_eid + i); + + if (entry->ect_gid == NTMP_NULL_ENTRY_ID) + return; + + ntmp_clear_eid_bitmap(ntmp->ect_gid_bitmap, entry->ect_gid); +} + +static int netc_port_update_vlan_egress_rule(struct netc_port *np, + struct netc_vlan_entry *entry) +{ + bool untagged = !!(entry->untagged_port_bitmap & BIT(np->dp->index)); + u32 num_ports = netc_num_available_ports(np->switch_priv); + u32 ett_eid = le32_to_cpu(entry->cfge.et_eid); + struct netc_switch *priv = np->switch_priv; + u32 ect_eid = NTMP_NULL_ENTRY_ID; + int err; + + if (ett_eid == NTMP_NULL_ENTRY_ID) + return 0; + + if (entry->ect_gid != NTMP_NULL_ENTRY_ID) + /* Each ETT entry maps to an ECT entry if ect_gid is not NULL + * entry ID. The offset of the ECT entry corresponding to the + * port in the group is equal to ett_offset. + */ + ect_eid = entry->ect_gid * num_ports + np->ett_offset; + + ett_eid += np->ett_offset; + err = netc_update_ett_entry(priv, untagged, ett_eid, ect_eid); + if (err) { + dev_err(priv->dev, + "Failed to update VLAN %u egress rule on port %d\n", + entry->vid, np->dp->index); + return err; + } + + if (ect_eid != NTMP_NULL_ENTRY_ID) + ntmp_ect_update_entry(&priv->ntmp, ect_eid); + + return 0; +} + +static int netc_port_add_vlan_entry(struct netc_port *np, u16 vid, + bool untagged) +{ + struct netc_switch *priv = np->switch_priv; + struct netc_vlan_entry *entry; + struct vft_cfge_data *cfge; + u32 index = np->dp->index; + u32 bitmap_stg; + int err; + u16 cfg; + + entry = kzalloc_obj(*entry); + if (!entry) + return -ENOMEM; + + entry->vid = vid; + entry->ect_gid = NTMP_NULL_ENTRY_ID; + + bitmap_stg = BIT(index) | VFT_STG_ID(0); + /* If the VID is a VLAN-unaware PVID, the CPU port needs to be + * a member of this VLAN. + */ + if (dsa_port_is_user(np->dp) && + vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) { + struct dsa_port *cpu_dp = np->dp->cpu_dp; + + bitmap_stg |= BIT(cpu_dp->index); + } + + cfg = FIELD_PREP(VFT_MLO, MLO_HW) | + FIELD_PREP(VFT_MFO, MFO_NO_MATCH_FLOOD); + + cfge = &entry->cfge; + cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID); + cfge->bitmap_stg = cpu_to_le32(bitmap_stg); + cfge->fid = cpu_to_le16(vid); + cfge->cfg = cpu_to_le16(cfg); + cfge->eta_port_bitmap = cpu_to_le32(priv->port_bitmap); + + if (untagged) + entry->untagged_port_bitmap = BIT(index); + + err = netc_add_vlan_egress_rule(priv, entry); + if (err) + goto free_vlan_entry; + + err = ntmp_vft_add_entry(&priv->ntmp, vid, cfge); + if (err) { + dev_err(priv->dev, + "Failed to add VLAN %u entry on port %d\n", + vid, index); + goto delete_vlan_egress_rule; + } + + netc_add_vlan_entry(priv, entry); + + return 0; + +delete_vlan_egress_rule: + netc_delete_vlan_egress_rule(priv, entry); +free_vlan_entry: + kfree(entry); + + return err; +} + +static bool netc_port_vlan_egress_rule_changed(struct netc_switch *priv, + struct netc_vlan_entry *entry, + int port, bool untagged) +{ + bool old_untagged = !!(entry->untagged_port_bitmap & BIT(port)); + + /* VLAN-unaware VIDs have no egress rules, so return 'false' */ + if (entry->vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) + return false; + + return old_untagged != untagged; +} + +static int netc_port_set_vlan_entry(struct netc_port *np, u16 vid, + bool untagged) +{ + struct netc_switch *priv = np->switch_priv; + struct netc_vlan_entry *entry; + struct vft_cfge_data *cfge; + int port = np->dp->index; + bool changed; + int err = 0; + + mutex_lock(&priv->vft_lock); + + entry = netc_lookup_vlan_entry(priv, vid); + if (!entry) { + err = netc_port_add_vlan_entry(np, vid, untagged); + goto unlock_vft; + } + + /* Check whether the egress VLAN rule is changed */ + changed = netc_port_vlan_egress_rule_changed(priv, entry, port, + untagged); + if (changed) { + entry->untagged_port_bitmap ^= BIT(port); + err = netc_port_update_vlan_egress_rule(np, entry); + if (err) { + entry->untagged_port_bitmap ^= BIT(port); + goto unlock_vft; + } + } + + cfge = &entry->cfge; + if (cfge->bitmap_stg & cpu_to_le32(BIT(port))) + goto unlock_vft; + + cfge->bitmap_stg |= cpu_to_le32(BIT(port)); + err = ntmp_vft_update_entry(&priv->ntmp, vid, cfge); + if (err) { + dev_err(priv->dev, + "Failed to update VLAN %u entry on port %d\n", + vid, port); + + goto restore_bitmap_stg; + } + + mutex_unlock(&priv->vft_lock); + + return 0; + +restore_bitmap_stg: + cfge->bitmap_stg &= cpu_to_le32(~BIT(port)); + if (changed) { + entry->untagged_port_bitmap ^= BIT(port); + /* Recover the corresponding ETT entry. It doesn't matter + * if it fails because the bit corresponding to the port + * in the port bitmap of the VFT entry is not set. so the + * frame will not match that ETT entry. + */ + if (netc_port_update_vlan_egress_rule(np, entry)) + entry->untagged_port_bitmap ^= BIT(port); + } +unlock_vft: + mutex_unlock(&priv->vft_lock); + + return err; +} + +static int netc_port_del_vlan_entry(struct netc_port *np, u16 vid) +{ + struct netc_switch *priv = np->switch_priv; + struct netc_vlan_entry *entry; + struct vft_cfge_data *cfge; + int port = np->dp->index; + u32 vlan_port_bitmap; + int err = 0; + + mutex_lock(&priv->vft_lock); + + entry = netc_lookup_vlan_entry(priv, vid); + if (!entry) + goto unlock_vft; + + cfge = &entry->cfge; + vlan_port_bitmap = FIELD_GET(VFT_PORT_MEMBERSHIP, + le32_to_cpu(cfge->bitmap_stg)); + /* If the VID is a VLAN-unaware PVID, we need to clear the CPU + * port bit of vlan_port_bitmap, so that the VLAN entry can be + * deleted if no user ports use this VLAN. + */ + if (dsa_port_is_user(np->dp) && + vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) { + struct dsa_port *cpu_dp = np->dp->cpu_dp; + + vlan_port_bitmap &= ~BIT(cpu_dp->index); + } + + /* If the VLAN only belongs to the current port */ + if (vlan_port_bitmap == BIT(port)) { + err = ntmp_vft_delete_entry(&priv->ntmp, vid); + if (err) + goto unlock_vft; + + netc_delete_vlan_egress_rule(priv, entry); + netc_del_vlan_entry(entry); + + goto unlock_vft; + } + + if (!(vlan_port_bitmap & BIT(port))) + goto unlock_vft; + + cfge->bitmap_stg &= cpu_to_le32(~BIT(port)); + err = ntmp_vft_update_entry(&priv->ntmp, vid, cfge); + if (err) { + cfge->bitmap_stg |= cpu_to_le32(BIT(port)); + goto unlock_vft; + } + +unlock_vft: + mutex_unlock(&priv->vft_lock); + + return err; +} + +static int netc_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct netc_port *np = NETC_PORT(ds, port); + int err; + + if (np->enable) + return 0; + + err = clk_prepare_enable(np->ref_clk); + if (err) { + dev_err(ds->dev, + "Failed to enable enet_ref_clk of port %d\n", port); + return err; + } + + np->enable = true; + + return 0; +} + +static void netc_port_disable(struct dsa_switch *ds, int port) +{ + struct netc_port *np = NETC_PORT(ds, port); + + /* When .port_disable() is called, .port_enable() may not have been + * called. In this case, both the prepare_count and enable_count of + * clock are 0. Calling clk_disable_unprepare() at this time will + * cause warnings. + */ + if (!np->enable) + return; + + clk_disable_unprepare(np->ref_clk); + np->enable = false; +} + +static void netc_port_stp_state_set(struct dsa_switch *ds, + int port, u8 state) +{ + struct netc_port *np = NETC_PORT(ds, port); + u32 val; + + switch (state) { + case BR_STATE_DISABLED: + case BR_STATE_LISTENING: + case BR_STATE_BLOCKING: + val = NETC_STG_STATE_DISABLED; + break; + case BR_STATE_LEARNING: + val = NETC_STG_STATE_LEARNING; + break; + case BR_STATE_FORWARDING: + val = NETC_STG_STATE_FORWARDING; + break; + default: + return; + } + + netc_port_wr(np, NETC_BPSTGSR, val); +} + +static int netc_port_change_mtu(struct dsa_switch *ds, + int port, int mtu) +{ + u32 max_frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + + netc_port_set_max_frame_size(NETC_PORT(ds, port), max_frame_size); + + return 0; +} + +static int netc_port_max_mtu(struct dsa_switch *ds, int port) +{ + return NETC_MAX_FRAME_LEN - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + +static struct net_device *netc_classify_db(struct dsa_db db) +{ + switch (db.type) { + case DSA_DB_PORT: + return NULL; + case DSA_DB_BRIDGE: + return db.bridge.dev; + default: + return ERR_PTR(-EOPNOTSUPP); + } +} + +static u16 netc_vlan_unaware_pvid(struct dsa_bridge *bridge) +{ + u32 br_num; + + if (!bridge) + return NETC_STANDALONE_PVID; + + br_num = bridge->num; + + /* The br_num is supposed to be 1 ~ ds->max_num_bridges, see + * dsa_bridge_num_get(). Since max_num_bridges is non-zero, + * so dsa_port_bridge_create() will return an error if + * dsa_bridge_num_get() returns 0. + */ + if (WARN_ON(!br_num)) + return NETC_STANDALONE_PVID; + + return NETC_VLAN_UNAWARE_PVID(br_num); +} + +static int netc_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *br_ndev = netc_classify_db(db); + struct netc_port *np = NETC_PORT(ds, port); + + if (IS_ERR(br_ndev)) + return PTR_ERR(br_ndev); + + if (!vid) + vid = netc_vlan_unaware_pvid(br_ndev ? &db.bridge : NULL); + + return netc_port_set_fdb_entry(np, addr, vid); +} + +static int netc_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct net_device *br_ndev = netc_classify_db(db); + struct netc_port *np = NETC_PORT(ds, port); + + if (IS_ERR(br_ndev)) + return PTR_ERR(br_ndev); + + if (!vid) + vid = netc_vlan_unaware_pvid(br_ndev ? &db.bridge : NULL); + + return netc_port_del_fdb_entry(np, addr, vid); +} + +static int netc_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct netc_switch *priv = ds->priv; + u32 resume_eid = NTMP_NULL_ENTRY_ID; + struct fdbt_entry_data *entry; + struct fdbt_keye_data *keye; + struct fdbt_cfge_data *cfge; + u32 cfg, cnt = 0; + bool is_static; + int err; + u16 vid; + + entry = kmalloc_obj(*entry); + if (!entry) + return -ENOMEM; + + keye = &entry->keye; + cfge = &entry->cfge; + mutex_lock(&priv->fdbt_lock); + + do { + memset(entry, 0, sizeof(*entry)); + err = ntmp_fdbt_search_port_entry(&priv->ntmp, port, + &resume_eid, entry); + if (err || entry->entry_id == NTMP_NULL_ENTRY_ID) + break; + + cfg = le32_to_cpu(cfge->cfg); + is_static = (cfg & FDBT_DYNAMIC) ? false : true; + vid = le16_to_cpu(keye->fid); + if (vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges)) + vid = 0; + + err = cb(keye->mac_addr, vid, is_static, data); + if (err) + break; + + /* To prevent hardware malfunctions from causing an + * infinite loop. + */ + if (++cnt >= priv->htmcapr_num_words) + break; + } while (resume_eid != NTMP_NULL_ENTRY_ID); + + mutex_unlock(&priv->fdbt_lock); + kfree(entry); + + return err; +} + +static int netc_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + return netc_port_fdb_add(ds, port, mdb->addr, mdb->vid, db); +} + +static int netc_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + return netc_port_fdb_del(ds, port, mdb->addr, mdb->vid, db); +} + +static int netc_port_add_host_flood_rule(struct netc_port *np, + bool uc, bool mc) +{ + const u8 dmac_mask[ETH_ALEN] = {0x1, 0, 0, 0, 0, 0}; + struct netc_switch *priv = np->switch_priv; + struct ipft_entry_data *host_flood; + struct ipft_keye_data *keye; + struct ipft_cfge_data *cfge; + u16 src_port; + u32 cfg; + int err; + + if (!uc && !mc) { + /* Disable ingress port filter table lookup */ + netc_port_wr(np, NETC_PIPFCR, 0); + np->uc = false; + np->mc = false; + + return 0; + } + + host_flood = kzalloc_obj(*host_flood); + if (!host_flood) + return -ENOMEM; + + keye = &host_flood->keye; + cfge = &host_flood->cfge; + + src_port = FIELD_PREP(IPFT_SRC_PORT, np->dp->index); + src_port |= IPFT_SRC_PORT_MASK; + keye->src_port = cpu_to_le16(src_port); + + /* If either only unicast or only multicast need to be flooded + * to the host, we always set the mask that tests the first MAC + * DA octet. The value should be 0 for the first bit (if unicast + * has to be flooded) or 1 (if multicast). If both unicast and + * multicast have to be flooded, we leave the key mask empty, so + * it matches everything. + */ + if (uc && !mc) + ether_addr_copy(keye->dmac_mask, dmac_mask); + + if (!uc && mc) { + ether_addr_copy(keye->dmac, dmac_mask); + ether_addr_copy(keye->dmac_mask, dmac_mask); + } + + cfg = FIELD_PREP(IPFT_FLTFA, IPFT_FLTFA_REDIRECT); + cfg |= FIELD_PREP(IPFT_HR, NETC_HR_HOST_FLOOD); + cfge->cfg = cpu_to_le32(cfg); + + err = ntmp_ipft_add_entry(&priv->ntmp, host_flood); + if (err) { + kfree(host_flood); + return err; + } + + np->uc = uc; + np->mc = mc; + np->host_flood = host_flood; + /* Enable ingress port filter table lookup */ + netc_port_wr(np, NETC_PIPFCR, PIPFCR_EN); + + return 0; +} + +static void netc_port_remove_host_flood(struct netc_port *np, + struct ipft_entry_data *host_flood) +{ + struct netc_switch *priv = np->switch_priv; + bool disable_host_flood = false; + + if (!host_flood) + return; + + if (np->host_flood == host_flood) + disable_host_flood = true; + + ntmp_ipft_delete_entry(&priv->ntmp, host_flood->entry_id); + kfree(host_flood); + + if (disable_host_flood) { + np->host_flood = NULL; + np->uc = false; + np->mc = false; + netc_port_wr(np, NETC_PIPFCR, 0); + } +} + +static void netc_port_set_host_flood(struct dsa_switch *ds, int port, + bool uc, bool mc) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct ipft_entry_data *old_host_flood; + + /* Do not add host flood rule to ingress port filter table when + * the port has joined a bridge. Otherwise, the ingress frames + * will bypass FDB table lookup and MAC learning, so the frames + * will be redirected directly to the CPU port. + */ + if (dsa_port_bridge_dev_get(np->dp)) { + netc_port_remove_host_flood(np, np->host_flood); + + return; + } + + if (np->uc == uc && np->mc == mc) + return; + + /* IPFT does not support in-place updates to the KEYE element, + * we need to add a new entry and then delete the old one. So + * save the old entry first. + */ + old_host_flood = np->host_flood; + np->host_flood = NULL; + + if (netc_port_add_host_flood_rule(np, uc, mc)) { + np->host_flood = old_host_flood; + dev_err(ds->dev, "Failed to add host flood rule on port %d\n", + port); + return; + } + + /* Remove the old host flood entry */ + netc_port_remove_host_flood(np, old_host_flood); +} + +static int netc_single_vlan_aware_bridge(struct dsa_switch *ds, + struct netlink_ext_ack *extack) +{ + struct net_device *br_ndev = NULL; + struct dsa_port *dp; + + dsa_switch_for_each_available_port(dp, ds) { + struct net_device *port_br = dsa_port_bridge_dev_get(dp); + + if (!port_br || !br_vlan_enabled(port_br)) + continue; + + if (!br_ndev) { + br_ndev = port_br; + continue; + } + + if (br_ndev == port_br) + continue; + + NL_SET_ERR_MSG_MOD(extack, + "Only one VLAN-aware bridge is supported"); + + return -EBUSY; + } + + return 0; +} + +static int netc_port_vlan_filtering(struct dsa_switch *ds, + int port, bool vlan_aware, + struct netlink_ext_ack *extack) +{ + struct netc_port *np = NETC_PORT(ds, port); + u16 pvid; + int err; + + /* Before calling port_vlan_filtering(), br_vlan_filter_toggle() has + * already updated the BROPT_VLAN_ENABLED bit of br->options. So the + * VLAN filtering status of the switch ports can be checked by the + * br_vlan_enabled() function. + */ + err = netc_single_vlan_aware_bridge(ds, extack); + if (err) + return err; + + pvid = netc_vlan_unaware_pvid(np->dp->bridge); + if (pvid == NETC_STANDALONE_PVID) { + vlan_aware = false; + goto bpdvr_config; + } + + if (vlan_aware) { + /* The FDB entries associated with unaware_pvid do not need + * to be deleted, so that when switching from VLAN-aware to + * VLAN-unaware mode, these FDB entries do not need to be + * re-added. + */ + err = netc_port_del_vlan_entry(np, pvid); + if (err) + return err; + + pvid = np->pvid; + } else { + err = netc_port_set_vlan_entry(np, pvid, false); + if (err) + return err; + } + +bpdvr_config: + netc_port_set_vlan_aware(np, vlan_aware); + netc_port_set_pvid(np, pvid); + + return 0; +} + +static int netc_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct dsa_port *dp = np->dp; + bool untagged; + int err; + + /* The 8021q layer may attempt to change NETC_STANDALONE_PVID + * (VID 0), so we need to ignore it. + */ + if (vlan->vid == NETC_STANDALONE_PVID) + return 0; + + if (vlan->vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges)) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "VID %d~4095 reserved for VLAN-unaware bridge", + NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges)); + return -EINVAL; + } + + untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + err = netc_port_set_vlan_entry(np, vlan->vid, untagged); + if (err) + return err; + + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { + np->pvid = vlan->vid; + if (dsa_port_is_vlan_filtering(dp)) + netc_port_set_pvid(np, vlan->vid); + + return 0; + } + + if (np->pvid != vlan->vid) + return 0; + + /* Delete PVID */ + np->pvid = NETC_STANDALONE_PVID; + if (dsa_port_is_vlan_filtering(dp)) + netc_port_set_pvid(np, NETC_STANDALONE_PVID); + + return 0; +} + +static int netc_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct netc_port *np = NETC_PORT(ds, port); + int err; + + if (vlan->vid == NETC_STANDALONE_PVID) + return 0; + + if (vlan->vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges)) + return -EINVAL; + + err = netc_port_del_vlan_entry(np, vlan->vid); + if (err) + return err; + + if (np->pvid == vlan->vid) { + np->pvid = NETC_STANDALONE_PVID; + + /* Set the port PVID to NETC_STANDALONE_PVID if the VLAN-aware + * bridge port has no PVID. The untagged frames will not be + * forwarded to other user ports, as NETC_STANDALONE_PVID VLAN + * entry has disabled MAC learning and flooding, and other user + * ports do not have FDB entries with NETC_STANDALONE_PVID. + */ + if (dsa_port_is_vlan_filtering(np->dp)) + netc_port_set_pvid(np, NETC_STANDALONE_PVID); + } + + return 0; +} + +static int netc_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct netc_switch *priv = ds->priv; + u16 vlan_unaware_pvid; + int err; + + if (!bridge.num) { + NL_SET_ERR_MSG_MOD(extack, "Bridge number 0 is unsupported"); + return -EINVAL; + } + + err = netc_single_vlan_aware_bridge(ds, extack); + if (err) + return err; + + netc_port_set_mlo(np, MLO_NOT_OVERRIDE); + + if (br_vlan_enabled(bridge.dev)) + goto out; + + vlan_unaware_pvid = NETC_VLAN_UNAWARE_PVID(bridge.num); + err = netc_port_set_vlan_entry(np, vlan_unaware_pvid, false); + if (err) + goto disable_mlo; + + netc_port_set_pvid(np, vlan_unaware_pvid); + +out: + netc_port_remove_host_flood(np, np->host_flood); + + if (atomic_inc_return(&priv->br_cnt) == 1) + schedule_delayed_work(&priv->fdbt_ageing_work, + READ_ONCE(priv->fdbt_ageing_delay)); + + return 0; + +disable_mlo: + netc_port_set_mlo(np, MLO_DISABLE); + + return err; +} + +static void netc_port_remove_dynamic_entries(struct netc_port *np) +{ + struct netc_switch *priv = np->switch_priv; + + /* Return if the port is not available */ + if (!np->dp) + return; + + mutex_lock(&priv->fdbt_lock); + ntmp_fdbt_delete_port_dynamic_entries(&priv->ntmp, np->dp->index); + mutex_unlock(&priv->fdbt_lock); +} + +static void netc_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct netc_port *np = NETC_PORT(ds, port); + struct net_device *ndev = np->dp->user; + struct netc_switch *priv = ds->priv; + u16 vlan_unaware_pvid; + bool mc, uc; + + netc_port_set_mlo(np, MLO_DISABLE); + netc_port_set_pvid(np, NETC_STANDALONE_PVID); + np->pvid = NETC_STANDALONE_PVID; + + if (atomic_dec_and_test(&priv->br_cnt)) + cancel_delayed_work_sync(&priv->fdbt_ageing_work); + + netc_port_remove_dynamic_entries(np); + uc = ndev->flags & IFF_PROMISC; + mc = ndev->flags & (IFF_PROMISC | IFF_ALLMULTI); + + if (netc_port_add_host_flood_rule(np, uc, mc)) + dev_warn(ds->dev, + "Failed to restore host flood rule on port %d\n", + port); + + /* When a port leaves a VLAN-aware bridge, dsa_port_bridge_leave() + * follows the sequence below: + * + * 1. dsa_port_bridge_destroy() is called to set dp->bridge to NULL. + * 2. dsa_broadcast() is called, which eventually invokes + * ds->ops->port_bridge_leave() + * 3. dsa_port_switchdev_unsync_attrs() is called, which triggers + * dsa_port_reset_vlan_filtering() and ultimately calls + * ds->ops->port_vlan_filtering() to transition the port from + * VLAN-aware mode to VLAN-unaware mode. + * + * At step 3, since dp->bridge has already been set to NULL in step 1, + * netc_port_vlan_filtering() will detect this and skip the creation + * of an unaware PVID entry in the VLAN filter table. Therefore, it is + * safe to return directly here. + */ + if (br_vlan_enabled(bridge.dev)) + return; + + vlan_unaware_pvid = NETC_VLAN_UNAWARE_PVID(bridge.num); + /* There is no need to check the return value even if it fails. + * Because the PVID has been set to NETC_STANDALONE_PVID, the + * frames will not match this VLAN entry. + */ + netc_port_del_vlan_entry(np, vlan_unaware_pvid); +} + +static int netc_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ + struct netc_switch *priv = ds->priv; + unsigned long delay_jiffies; + + /* The dynamic FDB entry is deleted when its activity counter reaches + * NETC_FDBT_AGEING_THRESH (100). Each delayed_work tick increments + * the counter by 1 if the entry is inactive. + * + * Therefore: + * msecs (ms) = NETC_FDBT_AGEING_THRESH * delay_ms (ms) + * delay_ms = msecs / NETC_FDBT_AGEING_THRESH + * delay_jiffies = (delay_ms / 1000) * HZ + * = (msecs * HZ) / (1000 * NETC_FDBT_AGEING_THRESH) + * + * Use DIV_ROUND_CLOSEST_ULL to perform a single nearest-jiffy + * rounding, avoiding the two-step rounding error of the intermediate + * delay_ms approach. + * Maximum error = +/-0.5 jiffy * 100 = +/-50000/HZ ms. + */ + delay_jiffies = DIV_ROUND_CLOSEST_ULL((u64)msecs * HZ, + 1000 * NETC_FDBT_AGEING_THRESH); + WRITE_ONCE(priv->fdbt_ageing_delay, delay_jiffies); + + if (atomic_read(&priv->br_cnt)) + mod_delayed_work(system_percpu_wq, &priv->fdbt_ageing_work, + READ_ONCE(priv->fdbt_ageing_delay)); + + return 0; +} + +static void netc_port_fast_age(struct dsa_switch *ds, int port) +{ + struct netc_port *np = NETC_PORT(ds, port); + + netc_port_remove_dynamic_entries(np); +} + +static void netc_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct netc_switch *priv = ds->priv; + + priv->info->phylink_get_caps(port, config); +} + +static void netc_port_set_mac_mode(struct netc_port *np, + unsigned int mode, + phy_interface_t phy_mode) +{ + u32 mask = PM_IF_MODE_IFMODE | PM_IF_MODE_REVMII; + u32 val = 0; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + val |= IFMODE_RGMII; + break; + case PHY_INTERFACE_MODE_RMII: + val |= IFMODE_RMII; + break; + case PHY_INTERFACE_MODE_REVMII: + val |= PM_IF_MODE_REVMII; + fallthrough; + case PHY_INTERFACE_MODE_MII: + val |= IFMODE_MII; + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + val |= IFMODE_SGMII; + break; + default: + break; + } + + netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val); +} + +static void netc_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + + netc_port_set_mac_mode(NETC_PORT(dp->ds, dp->index), mode, + state->interface); +} + +static void netc_port_set_speed(struct netc_port *np, int speed) +{ + netc_port_rmw(np, NETC_PCR, PCR_PSPEED, PSPEED_SET_VAL(speed)); +} + +static void netc_port_set_rgmii_mac(struct netc_port *np, + int speed, int duplex) +{ + u32 mask, val; + + mask = PM_IF_MODE_SSP | PM_IF_MODE_HD | PM_IF_MODE_M10; + + switch (speed) { + default: + case SPEED_1000: + val = FIELD_PREP(PM_IF_MODE_SSP, SSP_1G); + break; + case SPEED_100: + val = FIELD_PREP(PM_IF_MODE_SSP, SSP_100M); + break; + case SPEED_10: + val = FIELD_PREP(PM_IF_MODE_SSP, SSP_10M); + break; + } + + if (duplex != DUPLEX_FULL) + val |= PM_IF_MODE_HD; + + netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val); +} + +static void netc_port_set_rmii_mii_mac(struct netc_port *np, + int speed, int duplex) +{ + u32 mask, val = 0; + + mask = PM_IF_MODE_SSP | PM_IF_MODE_HD | PM_IF_MODE_M10; + + if (speed == SPEED_10) + val |= PM_IF_MODE_M10; + + if (duplex != DUPLEX_FULL) + val |= PM_IF_MODE_HD; + + netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val); +} + +static void netc_port_set_tx_pause(struct netc_port *np, bool tx_pause) +{ + struct netc_switch *priv = np->switch_priv; + int port = np->dp->index; + int i, j, num_bp; + + num_bp = priv->num_bp / priv->info->num_ports; + for (i = 0, j = port * num_bp; i < num_bp; i++, j++) { + struct bpt_cfge_data *cfge = &priv->bpt_list[j]; + struct bpt_cfge_data old_cfge = *cfge; + + if (tx_pause) { + cfge->fc_on_thresh = cpu_to_le16(NETC_FC_THRESH_ON); + cfge->fc_off_thresh = cpu_to_le16(NETC_FC_THRESH_OFF); + cfge->fccfg_sbpen = FIELD_PREP(BPT_FC_CFG, + BPT_FC_CFG_EN_BPFC); + cfge->fc_ports = cpu_to_le32(BIT(port)); + } else { + cfge->fc_on_thresh = cpu_to_le16(0); + cfge->fc_off_thresh = cpu_to_le16(0); + cfge->fccfg_sbpen = 0; + cfge->fc_ports = cpu_to_le32(0); + } + + if (ntmp_bpt_update_entry(&priv->ntmp, j, cfge)) { + *cfge = old_cfge; + dev_warn(priv->dev, + "Failed to %s TX pause of buffer pool %d (swp%d)\n", + tx_pause ? "enable" : "disable", j, port); + } + } +} + +static void netc_port_set_rx_pause(struct netc_port *np, bool rx_pause) +{ + netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_PAUSE_IGN, + rx_pause ? 0 : PM_CMD_CFG_PAUSE_IGN); +} + +static void netc_port_mac_rx_enable(struct netc_port *np) +{ + netc_port_rmw(np, NETC_POR, POR_RXDIS, 0); + netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_RX_EN, + PM_CMD_CFG_RX_EN); +} + +static void netc_port_wait_rx_empty(struct netc_port *np, int mac) +{ + u32 val; + + /* PM_IEVENT_RX_EMPTY is a read-only bit, it is automatically set by + * hardware if RX FIFO is empty and no RX packet receive in process. + * And it is automatically cleared if RX FIFO is not empty or RX + * packet receive in process. + */ + if (read_poll_timeout(netc_port_rd, val, val & PM_IEVENT_RX_EMPTY, + 100, 10000, false, np, NETC_PM_IEVENT(mac))) + dev_warn(np->switch_priv->dev, + "swp%d MAC%d: RX is not idle\n", np->dp->index, mac); +} + +static void netc_port_mac_rx_graceful_stop(struct netc_port *np) +{ + u32 val; + + if (is_netc_pseudo_port(np)) + goto rx_disable; + + if (np->caps.pmac) { + netc_port_rmw(np, NETC_PM_CMD_CFG(1), PM_CMD_CFG_RX_EN, 0); + netc_port_wait_rx_empty(np, 1); + } + + netc_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_RX_EN, 0); + netc_port_wait_rx_empty(np, 0); + + if (read_poll_timeout(netc_port_rd, val, !(val & PSR_RX_BUSY), + 100, 10000, false, np, NETC_PSR)) + dev_warn(np->switch_priv->dev, "swp%d RX is busy\n", + np->dp->index); + +rx_disable: + netc_port_rmw(np, NETC_POR, POR_RXDIS, POR_RXDIS); +} + +static void netc_port_mac_tx_enable(struct netc_port *np) +{ + netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_TX_EN, + PM_CMD_CFG_TX_EN); + netc_port_rmw(np, NETC_POR, POR_TXDIS, 0); +} + +static void netc_port_wait_tx_empty(struct netc_port *np, int mac) +{ + u32 val; + + /* PM_IEVENT_TX_EMPTY is a read-only bit, it is automatically set by + * hardware if TX FIFO is empty. And it is automatically cleared if + * TX FIFO is not empty. + */ + if (read_poll_timeout(netc_port_rd, val, val & PM_IEVENT_TX_EMPTY, + 100, 10000, false, np, NETC_PM_IEVENT(mac))) + dev_warn(np->switch_priv->dev, + "swp%d MAC%d: TX FIFO is not empty\n", + np->dp->index, mac); +} + +static void netc_port_mac_tx_graceful_stop(struct netc_port *np) +{ + netc_port_rmw(np, NETC_POR, POR_TXDIS, POR_TXDIS); + + if (is_netc_pseudo_port(np)) + return; + + netc_port_wait_tx_empty(np, 0); + if (np->caps.pmac) + netc_port_wait_tx_empty(np, 1); + + netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_TX_EN, 0); +} + +static void netc_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 dsa_port *dp = dsa_phylink_to_port(config); + struct netc_port *np; + + np = NETC_PORT(dp->ds, dp->index); + netc_port_set_speed(np, speed); + + if (phy_interface_mode_is_rgmii(interface)) + netc_port_set_rgmii_mac(np, speed, duplex); + + if (interface == PHY_INTERFACE_MODE_RMII || + interface == PHY_INTERFACE_MODE_REVMII || + interface == PHY_INTERFACE_MODE_MII) + netc_port_set_rmii_mii_mac(np, speed, duplex); + + netc_port_set_tx_pause(np, tx_pause); + netc_port_set_rx_pause(np, rx_pause); + netc_port_mac_tx_enable(np); + netc_port_mac_rx_enable(np); +} + +static void netc_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct netc_port *np; + + np = NETC_PORT(dp->ds, dp->index); + netc_port_mac_rx_graceful_stop(np); + netc_port_mac_tx_graceful_stop(np); + netc_port_remove_dynamic_entries(np); +} + +static const struct phylink_mac_ops netc_phylink_mac_ops = { + .mac_config = netc_mac_config, + .mac_link_up = netc_mac_link_up, + .mac_link_down = netc_mac_link_down, +}; + +static const struct dsa_switch_ops netc_switch_ops = { + .get_tag_protocol = netc_get_tag_protocol, + .setup = netc_setup, + .teardown = netc_teardown, + .phylink_get_caps = netc_phylink_get_caps, + .port_enable = netc_port_enable, + .port_disable = netc_port_disable, + .port_stp_state_set = netc_port_stp_state_set, + .port_change_mtu = netc_port_change_mtu, + .port_max_mtu = netc_port_max_mtu, + .port_fdb_add = netc_port_fdb_add, + .port_fdb_del = netc_port_fdb_del, + .port_fdb_dump = netc_port_fdb_dump, + .port_mdb_add = netc_port_mdb_add, + .port_mdb_del = netc_port_mdb_del, + .port_set_host_flood = netc_port_set_host_flood, + .port_vlan_filtering = netc_port_vlan_filtering, + .port_vlan_add = netc_port_vlan_add, + .port_vlan_del = netc_port_vlan_del, + .port_bridge_join = netc_port_bridge_join, + .port_bridge_leave = netc_port_bridge_leave, + .set_ageing_time = netc_set_ageing_time, + .port_fast_age = netc_port_fast_age, + .get_pause_stats = netc_port_get_pause_stats, + .get_rmon_stats = netc_port_get_rmon_stats, + .get_eth_ctrl_stats = netc_port_get_eth_ctrl_stats, + .get_eth_mac_stats = netc_port_get_eth_mac_stats, + .get_sset_count = netc_port_get_sset_count, + .get_strings = netc_port_get_strings, + .get_ethtool_stats = netc_port_get_ethtool_stats, +}; + +static int netc_switch_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device_node *node = dev_of_node(&pdev->dev); + struct device *dev = &pdev->dev; + struct netc_switch *priv; + struct dsa_switch *ds; + int err; + + if (!node) + return dev_err_probe(dev, -ENODEV, + "No DT bindings, skipping\n"); + + err = netc_switch_check_emdio_is_ready(dev); + if (err) + return err; + + err = netc_switch_pci_init(pdev); + if (err) + return err; + + priv = pci_get_drvdata(pdev); + netc_switch_get_ip_revision(priv); + + err = netc_switch_platform_probe(priv); + if (err) + return err; + + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); + if (!ds) + return -ENOMEM; + + ds->dev = dev; + ds->num_ports = priv->info->num_ports; + ds->num_tx_queues = NETC_TC_NUM; + ds->ops = &netc_switch_ops; + ds->phylink_mac_ops = &netc_phylink_mac_ops; + ds->fdb_isolation = true; + ds->max_num_bridges = priv->info->num_ports - 1; + ds->ageing_time_min = 1000; + ds->ageing_time_max = U32_MAX; + ds->priv = priv; + priv->ds = ds; + + err = dsa_register_switch(ds); + if (err) + return dev_err_probe(dev, err, + "Failed to register DSA switch\n"); + + return 0; +} + +static void netc_switch_remove(struct pci_dev *pdev) +{ + struct netc_switch *priv = pci_get_drvdata(pdev); + + if (!priv) + return; + + dsa_unregister_switch(priv->ds); +} + +static void netc_switch_shutdown(struct pci_dev *pdev) +{ + struct netc_switch *priv = pci_get_drvdata(pdev); + + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + pci_set_drvdata(pdev, NULL); +} + +static const struct pci_device_id netc_switch_ids[] = { + { PCI_DEVICE(NETC_SWITCH_VENDOR_ID, NETC_SWITCH_DEVICE_ID) }, + { } +}; +MODULE_DEVICE_TABLE(pci, netc_switch_ids); + +static struct pci_driver netc_switch_driver = { + .name = KBUILD_MODNAME, + .id_table = netc_switch_ids, + .probe = netc_switch_probe, + .remove = netc_switch_remove, + .shutdown = netc_switch_shutdown, +}; +module_pci_driver(netc_switch_driver); + +MODULE_DESCRIPTION("NXP NETC Switch driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/dsa/netc/netc_platform.c b/drivers/net/dsa/netc/netc_platform.c new file mode 100644 index 000000000000..34aeb6fceb3c --- /dev/null +++ b/drivers/net/dsa/netc/netc_platform.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * NXP NETC switch driver + * Copyright 2025-2026 NXP + */ + +#include "netc_switch.h" + +struct netc_switch_platform { + u16 revision; + const struct netc_switch_info *info; +}; + +static void imx94_switch_phylink_get_caps(int port, + struct phylink_config *config) +{ + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_1000FD; + + switch (port) { + case 0 ... 1: + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + config->mac_capabilities |= MAC_2500FD; + fallthrough; + case 2: + config->mac_capabilities |= MAC_10 | MAC_100; + __set_bit(PHY_INTERFACE_MODE_MII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + /* Port 0 and 1 do not support REVMII */ + if (port == 2) + __set_bit(PHY_INTERFACE_MODE_REVMII, + config->supported_interfaces); + + phy_interface_set_rgmii(config->supported_interfaces); + break; + case 3: /* CPU port */ + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + config->mac_capabilities |= MAC_10FD | MAC_100FD | + MAC_2500FD; + break; + default: + break; + } +} + +static const struct netc_switch_info imx94_info = { + .num_ports = 4, + .phylink_get_caps = imx94_switch_phylink_get_caps, +}; + +static const struct netc_switch_platform netc_platforms[] = { + { .revision = NETC_SWITCH_REV_4_3, .info = &imx94_info, }, + { } +}; + +static const struct netc_switch_info * +netc_switch_get_info(struct netc_switch *priv) +{ + int i; + + /* Matching based on IP revision */ + for (i = 0; i < ARRAY_SIZE(netc_platforms); i++) { + if (priv->revision == netc_platforms[i].revision) + return netc_platforms[i].info; + } + + return NULL; +} + +int netc_switch_platform_probe(struct netc_switch *priv) +{ + const struct netc_switch_info *info = netc_switch_get_info(priv); + + if (!info) { + dev_err(priv->dev, "Cannot find switch platform info\n"); + return -EINVAL; + } + + priv->info = info; + + return 0; +} diff --git a/drivers/net/dsa/netc/netc_switch.h b/drivers/net/dsa/netc/netc_switch.h new file mode 100644 index 000000000000..305f2a92e2f9 --- /dev/null +++ b/drivers/net/dsa/netc/netc_switch.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright 2025-2026 NXP + */ + +#ifndef _NETC_SWITCH_H +#define _NETC_SWITCH_H + +#include +#include +#include +#include +#include +#include + +#include "netc_switch_hw.h" + +#define NETC_REGS_BAR 0 +#define NETC_REGS_SIZE 0x80000 +#define NETC_MSIX_TBL_BAR 2 +#define NETC_REGS_PORT_BASE 0x4000 +/* register block size per port */ +#define NETC_REGS_PORT_SIZE 0x4000 +#define PORT_IOBASE(p) (NETC_REGS_PORT_SIZE * (p)) +#define NETC_REGS_GLOBAL_BASE 0x70000 + +#define NETC_SWITCH_REV_4_3 0x0403 + +#define NETC_TC_NUM 8 +#define NETC_CBDR_NUM 2 +#define NETC_IPV_NUM 8 + +#define NETC_MAX_FRAME_LEN 9600 + +#define NETC_STANDALONE_PVID 0 +#define NETC_VLAN_UNAWARE_PVID(br_id) (4096 - (br_id)) + +/* Threshold format: MANT (bits 11:4) * 2^EXP (bits 3:0) + * Unit: Memory words (average of 20 bytes each) + * NETC_BP_THRESH = 0x8c3, MANT = 0x8c, EXP = 3. Threshold: 1120 words + * NETC_FC_THRESH_ON = 0x733, MANT = 0x73, EXP = 3. Threshold: 920 words + * NETC_FC_THRESH_OFF = 0x263, MANT = 0x26, EXP = 3. Threshold: 304 words + */ +#define NETC_BP_THRESH 0x8c3 +#define NETC_FC_THRESH_ON 0x733 +#define NETC_FC_THRESH_OFF 0x263 + +/* PAUSE quanta: 0xFFFF = 65535 quanta (each quanta = 512 bit times) */ +#define NETC_PAUSE_QUANTA 0xFFFF +/* PAUSE refresh threshold: send refresh when timer reaches this value */ +#define NETC_PAUSE_THRESH 0x7FFF + +#define NETC_FDBT_AGEING_DELAY (3 * HZ) +#define NETC_FDBT_AGEING_THRESH 100 + +struct netc_switch; + +struct netc_switch_info { + u32 num_ports; + void (*phylink_get_caps)(int port, struct phylink_config *config); +}; + +struct netc_port_caps { + u32 half_duplex:1; /* indicates whether the port support half-duplex */ + u32 pmac:1; /* indicates whether the port has preemption MAC */ + u32 pseudo_link:1; +}; + +enum netc_host_reason { + /* Software defined host reasons */ + NETC_HR_HOST_FLOOD = 8, +}; + +struct netc_port { + void __iomem *iobase; + struct netc_switch *switch_priv; + struct netc_port_caps caps; + struct dsa_port *dp; + struct clk *ref_clk; /* RGMII/RMII reference clock */ + struct mii_bus *emdio; + int ett_offset; + + u16 enable:1; + u16 uc:1; + u16 mc:1; + u16 pvid; + struct ipft_entry_data *host_flood; +}; + +struct netc_switch_regs { + void __iomem *base; + void __iomem *port; + void __iomem *global; +}; + +struct netc_fdb_entry { + u32 entry_id; + struct fdbt_cfge_data cfge; + struct fdbt_keye_data keye; + struct hlist_node node; +}; + +struct netc_vlan_entry { + u16 vid; + u32 ect_gid; + u32 untagged_port_bitmap; + struct vft_cfge_data cfge; + struct hlist_node node; +}; + +struct netc_port_stat { + int reg; + char name[ETH_GSTRING_LEN] __nonstring; +}; + +struct netc_switch { + struct pci_dev *pdev; + struct device *dev; + struct dsa_switch *ds; + u16 revision; + + const struct netc_switch_info *info; + struct netc_switch_regs regs; + struct netc_port **ports; + u32 port_bitmap; /* bitmap of available ports */ + + struct ntmp_user ntmp; + struct hlist_head fdb_list; + struct mutex fdbt_lock; /* FDB table lock */ + struct delayed_work fdbt_ageing_work; + /* (fdbt_ageing_delay * NETC_FDBT_AGEING_THRESH) is ageing time */ + unsigned long fdbt_ageing_delay; + atomic_t br_cnt; + struct hlist_head vlan_list; + struct mutex vft_lock; /* VLAN filter table lock */ + + /* Switch hardware capabilities */ + u32 htmcapr_num_words; + u32 num_bp; + + struct bpt_cfge_data *bpt_list; +}; + +#define NETC_PRIV(ds) ((struct netc_switch *)((ds)->priv)) +#define NETC_PORT(ds, port_id) (NETC_PRIV(ds)->ports[(port_id)]) + +/* Write/Read Switch base registers */ +#define netc_base_rd(r, o) netc_read((r)->base + (o)) +#define netc_base_wr(r, o, v) netc_write((r)->base + (o), v) + +/* Write/Read registers of Switch Port (including pseudo MAC port) */ +#define netc_port_rd(p, o) netc_read((p)->iobase + (o)) +#define netc_port_rd64(p, o) netc_read64((p)->iobase + (o)) +#define netc_port_wr(p, o, v) netc_write((p)->iobase + (o), v) + +/* Write/Read Switch global registers */ +#define netc_glb_rd(r, o) netc_read((r)->global + (o)) +#define netc_glb_wr(r, o, v) netc_write((r)->global + (o), v) + +static inline bool is_netc_pseudo_port(struct netc_port *np) +{ + return np->caps.pseudo_link; +} + +static inline void netc_add_fdb_entry(struct netc_switch *priv, + struct netc_fdb_entry *entry) +{ + hlist_add_head(&entry->node, &priv->fdb_list); +} + +static inline void netc_del_fdb_entry(struct netc_fdb_entry *entry) +{ + hlist_del(&entry->node); + kfree(entry); +} + +static inline void netc_add_vlan_entry(struct netc_switch *priv, + struct netc_vlan_entry *entry) +{ + hlist_add_head(&entry->node, &priv->vlan_list); +} + +static inline void netc_del_vlan_entry(struct netc_vlan_entry *entry) +{ + hlist_del(&entry->node); + kfree(entry); +} + +int netc_switch_platform_probe(struct netc_switch *priv); + +/* ethtool APIs */ +void netc_port_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats); +void netc_port_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges); +void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats); +void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats); +int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset); +void netc_port_get_strings(struct dsa_switch *ds, int port, + u32 sset, u8 *data); +void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data); + +#endif diff --git a/drivers/net/dsa/netc/netc_switch_hw.h b/drivers/net/dsa/netc/netc_switch_hw.h new file mode 100644 index 000000000000..1404ae41c7bc --- /dev/null +++ b/drivers/net/dsa/netc/netc_switch_hw.h @@ -0,0 +1,372 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright 2025-2026 NXP + */ + +#ifndef _NETC_SWITCH_HW_H +#define _NETC_SWITCH_HW_H + +#include + +#define NETC_SWITCH_VENDOR_ID 0x1131 +#define NETC_SWITCH_DEVICE_ID 0xeef2 + +/* Definition of Switch base registers */ +#define NETC_BPCAPR 0x0008 +#define BPCAPR_NUM_BP GENMASK(7, 0) + +#define NETC_PBPMCR0 0x0400 +#define NETC_PBPMCR1 0x0404 + +#define NETC_CBDRMR(a) (0x0800 + (a) * 0x30) +#define NETC_CBDRBAR0(a) (0x0810 + (a) * 0x30) +#define NETC_CBDRBAR1(a) (0x0814 + (a) * 0x30) +#define NETC_CBDRPIR(a) (0x0818 + (a) * 0x30) +#define NETC_CBDRCIR(a) (0x081c + (a) * 0x30) +#define NETC_CBDRLENR(a) (0x0820 + (a) * 0x30) + +#define NETC_SWCR 0x1018 +#define SWCR_SWID GENMASK(2, 0) + +#define NETC_DOSL2CR 0x1220 +#define DOSL2CR_SAMEADDR BIT(0) +#define DOSL2CR_MSAMCC BIT(1) + +#define NETC_DOSL3CR 0x1224 +#define DOSL3CR_SAMEADDR BIT(0) +#define DOSL3CR_IPSAMCC BIT(1) + +#define NETC_ETTCAPR 0x18c4 +#define NETC_ECTCAPR 0x18ec +/* Index table NUM_ENTRIES mask */ +#define NETC_NUM_ENTRIES GENMASK(15, 0) +#define NETC_GET_NUM_ENTRIES(v) FIELD_GET(NETC_NUM_ENTRIES, (v)) + +/* Hash table memory capability register, the memory is shared by + * the following tables: + * + * - Ingress Stream Identification table + * - Ingress Stream Filter table + * - VLAN Filter table + * - FDB table + * - L2 IPv4 Multicast Filter table + * + * Each hash table entry is one word in size. + */ +#define NETC_HTMCAPR 0x1900 +#define HTMCAPR_NUM_WORDS GENMASK(15, 0) + +#define NETC_VFHTDECR1 0x2014 +#define NETC_VFHTDECR2 0x2018 +#define VFHTDECR2_ET_PORT(a) BIT((a)) +#define VFHTDECR2_MLO GENMASK(26, 24) +#define VFHTDECR2_MFO GENMASK(28, 27) + +/* Definition of Switch port registers */ +#define NETC_PCAPR 0x0000 +#define PCAPR_LINK_TYPE BIT(4) +#define PCAPR_NUM_TC GENMASK(15, 12) +#define PCAPR_NUM_Q GENMASK(19, 16) +#define PCAPR_NUM_CG GENMASK(27, 24) +#define PCAPR_TGS BIT(28) +#define PCAPR_CBS BIT(29) + +#define NETC_PMCAPR 0x0004 +#define PMCAPR_HD BIT(8) +#define PMCAPR_FP GENMASK(10, 9) +#define FP_SUPPORT 2 + +#define NETC_PCR 0x0010 +#define PCR_HDR_FMT BIT(0) +#define PCR_NS_TAG_PORT BIT(3) +#define PCR_L2DOSE BIT(4) +#define PCR_L3DOSE BIT(5) +#define PCR_TIMER_CS BIT(8) +#define PCR_PSPEED GENMASK(29, 16) +#define PSPEED_SET_VAL(s) FIELD_PREP(PCR_PSPEED, ((s) / 10 - 1)) + +#define NETC_PQOSMR 0x0054 +#define PQOSMR_VS BIT(0) +#define PQOSMR_VE BIT(1) +#define PQOSMR_DDR GENMASK(3, 2) +#define PQOSMR_DIPV GENMASK(6, 4) +#define PQOSMR_VQMP GENMASK(19, 16) +#define PQOSMR_QVMP GENMASK(23, 20) + +#define NETC_PIPFCR 0x0084 +#define PIPFCR_EN BIT(0) + +#define NETC_POR 0x100 +#define POR_TXDIS BIT(0) +#define POR_RXDIS BIT(1) + +#define NETC_PSR 0x104 +#define PSR_TX_BUSY BIT(0) +#define PSR_RX_BUSY BIT(1) + +#define NETC_PTGSLACR 0x130 + +#define NETC_PRXDCR 0x1c0 +#define NETC_PRXDCRRR 0x1c4 +#define NETC_PRXDCRR0 0x1c8 +#define NETC_PRXDCRR1 0x1cc +#define NETC_PTXDCR 0x1e0 + +#define NETC_PTCTMSDUR(a) (0x208 + (a) * 0x20) +#define PTCTMSDUR_MAXSDU GENMASK(15, 0) +#define PTCTMSDUR_SDU_TYPE GENMASK(17, 16) +#define SDU_TYPE_PPDU 0 +#define SDU_TYPE_MPDU 1 +#define SDU_TYPE_MSDU 2 + +#define NETC_PSDFTCR 0x4c4 +#define NETC_PSDFDDCR 0x4c8 + +#define NETC_BPCR 0x500 +#define BPCR_DYN_LIMIT GENMASK(15, 0) +#define BPCR_MLO GENMASK(22, 20) +#define BPCR_UUCASTE BIT(24) +#define BPCR_UMCASTE BIT(25) +#define BPCR_MCASTE BIT(26) +#define BPCR_BCASTE BIT(27) +#define BPCR_STAMVD BIT(28) +#define BPCR_SRCPRND BIT(29) + +/* MAC learning options, see BPCR[MLO], VFHTDECR2[MLO] and + * VLAN Filter Table CFGE_DATA[MLO] + */ +enum netc_mlo { + MLO_NOT_OVERRIDE = 0, + MLO_DISABLE, + MLO_HW, + MLO_SW_SEC, + MLO_SW_UNSEC, + MLO_DISABLE_SMAC, +}; + +/* MAC forwarding options, see VFHTDECR2[MFO] and VLAN + * Filter Table CFGE_DATA[MFO] + */ +enum netc_mfo { + MFO_NO_FDB_LOOKUP = 1, + MFO_NO_MATCH_FLOOD, + MFO_NO_MATCH_DISCARD, +}; + +#define NETC_BPDVR 0x510 +#define BPDVR_VID GENMASK(11, 0) +#define BPDVR_DEI BIT(12) +#define BPDVR_PCP GENMASK(15, 13) +#define BPDVR_TPID BIT(16) +#define BPDVR_RXTAGA GENMASK(23, 20) +#define BPDVR_RXVAM BIT(24) +#define BPDVR_TXTAGA GENMASK(26, 25) + +#define NETC_BPSTGSR 0x520 + +enum netc_stg_stage { + NETC_STG_STATE_DISABLED = 0, + NETC_STG_STATE_LEARNING, + NETC_STG_STATE_FORWARDING, +}; + +#define NETC_BPDCR 0x580 + +/* Definition of Switch ethernet MAC port registers */ +#define NETC_PMAC_OFFSET 0x400 +#define NETC_PM_CMD_CFG(a) (0x1008 + (a) * 0x400) +#define PM_CMD_CFG_TX_EN BIT(0) +#define PM_CMD_CFG_RX_EN BIT(1) +#define PM_CMD_CFG_PAUSE_IGN BIT(8) + +#define NETC_PM_MAXFRM(a) (0x1014 + (a) * 0x400) +#define PM_MAXFRAM GENMASK(15, 0) + +#define NETC_PM_IEVENT(a) (0x1040 + (a) * 0x400) +#define PM_IEVENT_TX_EMPTY BIT(5) +#define PM_IEVENT_RX_EMPTY BIT(6) + +#define NETC_PM_PAUSE_QUANTA(a) (0x1054 + (a) * 0x400) +#define NETC_PM_PAUSE_THRESH(a) (0x1064 + (a) * 0x400) + +#define NETC_PM_IF_MODE(a) (0x1300 + (a) * 0x400) +#define PM_IF_MODE_IFMODE GENMASK(2, 0) +#define IFMODE_MII 1 +#define IFMODE_RMII 3 +#define IFMODE_RGMII 4 +#define IFMODE_SGMII 5 +#define PM_IF_MODE_REVMII BIT(3) +#define PM_IF_MODE_M10 BIT(4) +#define PM_IF_MODE_HD BIT(6) +#define PM_IF_MODE_SSP GENMASK(14, 13) +#define SSP_100M 0 +#define SSP_10M 1 +#define SSP_1G 2 + +/* Port MAC 0/1 Receive Ethernet Octets Counter */ +#define NETC_PM_REOCT(a) (0x1100 + (a) * 0x400) + +/* Port MAC 0/1 Receive Octets Counter */ +#define NETC_PM_ROCT(a) (0x1108 + (a) * 0x400) + +/* Port MAC 0/1 Receive Alignment Error Counter Register */ +#define NETC_PM_RALN(a) (0x1110 + (a) * 0x400) + +/* Port MAC 0/1 Receive Valid Pause Frame Counter */ +#define NETC_PM_RXPF(a) (0x1118 + (a) * 0x400) + +/* Port MAC 0/1 Receive Frame Counter */ +#define NETC_PM_RFRM(a) (0x1120 + (a) * 0x400) + +/* Port MAC 0/1 Receive Frame Check Sequence Error Counter */ +#define NETC_PM_RFCS(a) (0x1128 + (a) * 0x400) + +/* Port MAC 0/1 Receive VLAN Frame Counter */ +#define NETC_PM_RVLAN(a) (0x1130 + (a) * 0x400) + +/* Port MAC 0/1 Receive Frame Error Counter */ +#define NETC_PM_RERR(a) (0x1138 + (a) * 0x400) + +/* Port MAC 0/1 Receive Unicast Frame Counter */ +#define NETC_PM_RUCA(a) (0x1140 + (a) * 0x400) + +/* Port MAC 0/1 Receive Multicast Frame Counter */ +#define NETC_PM_RMCA(a) (0x1148 + (a) * 0x400) + +/* Port MAC 0/1 Receive Broadcast Frame Counter */ +#define NETC_PM_RBCA(a) (0x1150 + (a) * 0x400) + +/* Port MAC 0/1 Receive Dropped Packets Counter */ +#define NETC_PM_RDRP(a) (0x1158 + (a) * 0x400) + +/* Port MAC 0/1 Receive Packets Counter */ +#define NETC_PM_RPKT(a) (0x1160 + (a) * 0x400) + +/* Port MAC 0/1 Receive Undersized Packet Counter */ +#define NETC_PM_RUND(a) (0x1168 + (a) * 0x400) + +/* Port MAC 0/1 Receive 64-Octet Packet Counter */ +#define NETC_PM_R64(a) (0x1170 + (a) * 0x400) + +/* Port MAC 0/1 Receive 65 to 127-Octet Packet Counter */ +#define NETC_PM_R127(a) (0x1178 + (a) * 0x400) + +/* Port MAC 0/1 Receive 128 to 255-Octet Packet Counter */ +#define NETC_PM_R255(a) (0x1180 + (a) * 0x400) + +/* Port MAC 0/1 Receive 256 to 511-Octet Packet Counter */ +#define NETC_PM_R511(a) (0x1188 + (a) * 0x400) + +/* Port MAC 0/1 Receive 512 to 1023-Octet Packet Counter */ +#define NETC_PM_R1023(a) (0x1190 + (a) * 0x400) + +/* Port MAC 0/1 Receive 1024 to 1522-Octet Packet Counter */ +#define NETC_PM_R1522(a) (0x1198 + (a) * 0x400) + +/* Port MAC 0/1 Receive 1523 to Max-Octet Packet Counter */ +#define NETC_PM_R1523X(a) (0x11a0 + (a) * 0x400) + +/* Port MAC 0/1 Receive Oversized Packet Counter */ +#define NETC_PM_ROVR(a) (0x11a8 + (a) * 0x400) + +/* Port MAC 0/1 Receive Jabber Packet Counter */ +#define NETC_PM_RJBR(a) (0x11b0 + (a) * 0x400) + +/* Port MAC 0/1 Receive Fragment Packet Counter */ +#define NETC_PM_RFRG(a) (0x11b8 + (a) * 0x400) + +/* Port MAC 0/1 Receive Control Packet Counter */ +#define NETC_PM_RCNP(a) (0x11c0 + (a) * 0x400) + +/* Port MAC 0/1 Receive Dropped Not Truncated Packets Counter */ +#define NETC_PM_RDRNTP(a) (0x11c8 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Ethernet Octets Counter */ +#define NETC_PM_TEOCT(a) (0x1200 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Octets Counter */ +#define NETC_PM_TOCT(a) (0x1208 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Excessive Deferral Packet Counter */ +#define NETC_PM_TEDFR(a) (0x1210 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Valid Pause Frame Counter */ +#define NETC_PM_TXPF(a) (0x1218 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Frame Counter */ +#define NETC_PM_TFRM(a) (0x1220 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Frame Check Sequence Error Counter */ +#define NETC_PM_TFCS(a) (0x1228 + (a) * 0x400) + +/* Port MAC 0/1 Transmit VLAN Frame Counter */ +#define NETC_PM_TVLAN(a) (0x1230 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Frame Error Counter */ +#define NETC_PM_TERR(a) (0x1238 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Unicast Frame Counter */ +#define NETC_PM_TUCA(a) (0x1240 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Multicast Frame Counter */ +#define NETC_PM_TMCA(a) (0x1248 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Broadcast Frame Counter */ +#define NETC_PM_TBCA(a) (0x1250 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Packets Counter */ +#define NETC_PM_TPKT(a) (0x1260 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Undersized Packet Counter */ +#define NETC_PM_TUND(a) (0x1268 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 64-Octet Packet Counter */ +#define NETC_PM_T64(a) (0x1270 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 65 to 127-Octet Packet Counter */ +#define NETC_PM_T127(a) (0x1278 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 128 to 255-Octet Packet Counter */ +#define NETC_PM_T255(a) (0x1280 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 256 to 511-Octet Packet Counter */ +#define NETC_PM_T511(a) (0x1288 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 512 to 1023-Octet Packet Counter */ +#define NETC_PM_T1023(a) (0x1290 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 1024 to 1522-Octet Packet Counter */ +#define NETC_PM_T1522(a) (0x1298 + (a) * 0x400) + +/* Port MAC 0/1 Transmit 1523 to TX_MTU-Octet Packet Counter */ +#define NETC_PM_T1523X(a) (0x12a0 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Control Packet Counter */ +#define NETC_PM_TCNP(a) (0x12c0 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Deferred Packet Counter */ +#define NETC_PM_TDFR(a) (0x12d0 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Multiple Collisions Counter */ +#define NETC_PM_TMCOL(a) (0x12d8 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Single Collision */ +#define NETC_PM_TSCOL(a) (0x12e0 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Late Collision Counter */ +#define NETC_PM_TLCOL(a) (0x12e8 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Excessive Collisions Counter */ +#define NETC_PM_TECOL(a) (0x12f0 + (a) * 0x400) + +/* Port MAC 0/1 Transmit Invalid Octets Counter */ +#define NETC_PM_TIOCT(a) (0x12f8 + (a) * 0x400) + +#define NETC_PEMDIOCR 0x1c00 +#define NETC_EMDIO_BASE NETC_PEMDIOCR + +/* Definition of global registers (read only) */ +#define NETC_IPBRR0 0x0bf8 +#define IPBRR0_IP_REV GENMASK(15, 0) + +#endif diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 84cf8e7fb17a..84a95007825f 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -2001,7 +2001,8 @@ static int felix_cls_flower_stats(struct dsa_switch *ds, int port, } static int felix_port_policer_add(struct dsa_switch *ds, int port, - const struct flow_action_police *policer) + const struct flow_action_police *policer, + struct netlink_ext_ack *extack) { struct ocelot *ocelot = ds->priv; struct ocelot_policer pol = { @@ -2445,12 +2446,14 @@ struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) } EXPORT_SYMBOL_GPL(felix_port_to_netdev); -int felix_netdev_to_port(struct net_device *dev) +int felix_netdev_to_port(struct ocelot *ocelot, struct net_device *dev) { + struct felix *felix = ocelot_to_felix(ocelot); + struct dsa_switch *ds = felix->ds; struct dsa_port *dp; dp = dsa_port_from_netdev(dev); - if (IS_ERR(dp)) + if (IS_ERR(dp) || dp->ds != ds) return -EINVAL; return dp->index; diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index a657b190c5d7..19addcfd62be 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -104,6 +104,6 @@ int felix_register_switch(struct device *dev, resource_size_t switch_base, enum dsa_tag_protocol init_tag_proto, const struct felix_info *info); struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port); -int felix_netdev_to_port(struct net_device *dev); +int felix_netdev_to_port(struct ocelot *ocelot, struct net_device *dev); #endif diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index a36b8b07030e..4c928983b862 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, { struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv; int cpu_port_index, ret, port; - u32 reg, val; + u32 mask, reg, val; port = pcs_to_qca8k_pcs(pcs)->port; switch (port) { @@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 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); + mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | + QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0; + + /* + * (Un)set force mode on QCA8337 only, don't include it in the mask for + * others. It is written to the PORT0 PAD register for both port 0 and 6. + */ + if (priv->switch_id == QCA8K_ID_QCA8337) { + if (neg_mode == PHYLINK_PCS_NEG_OUTBAND) + val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE; + mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE; + } + + if (mask) + ret = qca8k_rmw(priv, reg, mask, val); return 0; } diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c index 43ac68052baf..ef496e345a4e 100644 --- a/drivers/net/dsa/qca/qca8k-leds.c +++ b/drivers/net/dsa/qca/qca8k-leds.c @@ -429,7 +429,8 @@ qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int p init_data.fwnode = led; init_data.devname_mandatory = true; init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", - priv->internal_mdio_bus->id, + priv->internal_mdio_bus ? + priv->internal_mdio_bus->id : priv->bus->id, port_num); if (!init_data.devicename) { fwnode_handle_put(led); diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index 1a00e2f62fef..956338893032 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -58,6 +58,7 @@ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25) #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) #define QCA8K_PORT_PAD_SGMII_EN BIT(7) +#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3) #define QCA8K_REG_PWS 0x010 #define QCA8K_PWS_POWER_ON_SEL BIT(31) /* This reg is only valid for QCA832x and toggle the package diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile index 17367bcba496..72bb42e80c95 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -16,3 +16,8 @@ ifdef CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS rtl8366-objs += rtl8366rb-leds.o endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o +rtl8365mb-objs := rtl8365mb_main.o \ + rtl8365mb_table.o \ + rtl8365mb_vlan.o \ + rtl8365mb_l2.o \ +# end of rtl8365mb-objs diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index c03485a80d93..6e0148cee8d8 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -45,6 +45,12 @@ struct rtl8366_vlan_4k { u8 fid; }; +struct realtek_fdb_entry { + u8 mac_addr[ETH_ALEN]; + u16 vid; + bool is_static; +}; + struct realtek_priv { struct device *dev; struct reset_control *reset_ctl; @@ -54,6 +60,20 @@ struct realtek_priv { struct regmap *map; struct regmap *map_nolock; struct mutex map_lock; + /* vlan_lock protects against concurrent Read-Modify-Write operations + * on the global VLAN 4K and VLANMC tables, such as when adding or + * deleting port VLAN memberships and PVID configurations. + */ + struct mutex vlan_lock; + /* l2_lock is used to prevent concurrent modifications of L2 table + * entries while another function is reading it. l2_(add,del)_mc + * is an example that first read current table entry and then + * create/update it. l2_(add|del)_uc uses a single table op and, + * internally, it might not need this lock. However, altering FDB + * may still collide, as well as l2_flush, with fdb_dump iterating + * over FDB. + */ + struct mutex l2_lock; struct mii_bus *user_mii_bus; struct mii_bus *bus; int mdio_addr; @@ -107,6 +127,32 @@ struct realtek_ops { 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 (*port_add_isolation)(struct realtek_priv *priv, int port, + u32 mask); + int (*port_remove_isolation)(struct realtek_priv *priv, int port, + u32 mask); + int (*port_set_efid)(struct realtek_priv *priv, int port, u32 efid); + int (*port_set_learning)(struct realtek_priv *priv, int port, + bool enable); + int (*port_set_ucast_flood)(struct realtek_priv *priv, int port, + bool enable); + int (*port_set_mcast_flood)(struct realtek_priv *priv, int port, + bool enable); + int (*port_set_bcast_flood)(struct realtek_priv *priv, int port, + bool enable); + int (*l2_add_uc)(struct realtek_priv *priv, int port, + const unsigned char addr[ETH_ALEN], + u16 efid, u16 vid); + int (*l2_del_uc)(struct realtek_priv *priv, int port, + const unsigned char addr[ETH_ALEN], + u16 efid, u16 vid); + int (*l2_get_next_uc)(struct realtek_priv *priv, u16 *addr, + int port, struct realtek_fdb_entry *entry); + int (*l2_add_mc)(struct realtek_priv *priv, int port, + const unsigned char addr[ETH_ALEN], u16 vid); + int (*l2_del_mc)(struct realtek_priv *priv, int port, + const unsigned char addr[ETH_ALEN], u16 vid); + int (*l2_flush)(struct realtek_priv *priv, int port, u16 vid); int (*phy_read)(struct realtek_priv *priv, int phy, int regnum); int (*phy_write)(struct realtek_priv *priv, int phy, int regnum, u16 val); diff --git a/drivers/net/dsa/realtek/rtl8365mb_l2.c b/drivers/net/dsa/realtek/rtl8365mb_l2.c new file mode 100644 index 000000000000..0494d8ab2578 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_l2.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Forwarding and multicast database interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin Šipraga + */ + +#include + +#include "rtl8365mb_l2.h" +#include "rtl8365mb_table.h" +#include + +#define RTL8365MB_L2_ENTRY_SIZE 6 + +#define RTL8365MB_L2_UC_D0_MAC5_MSK GENMASK(7, 0) +#define RTL8365MB_L2_UC_D0_MAC4_MSK GENMASK(15, 8) +#define RTL8365MB_L2_UC_D1_MAC3_MSK GENMASK(7, 0) +#define RTL8365MB_L2_UC_D1_MAC2_MSK GENMASK(15, 8) +#define RTL8365MB_L2_UC_D2_MAC1_MSK GENMASK(7, 0) +#define RTL8365MB_L2_UC_D2_MAC0_MSK GENMASK(15, 8) +#define RTL8365MB_L2_UC_D3_VID_MSK GENMASK(11, 0) +#define RTL8365MB_L2_UC_D3_IVL_MSK GENMASK(13, 13) +#define RTL8365MB_L2_UC_D3_PORT_EXT_MSK GENMASK(15, 15) +#define RTL8365MB_L2_UC_PORT_HI_MSK GENMASK(3, 3) +#define RTL8365MB_L2_UC_D4_EFID_MSK GENMASK(2, 0) +#define RTL8365MB_L2_UC_D4_FID_MSK GENMASK(6, 3) +#define RTL8365MB_L2_UC_D4_SA_PRI_MSK GENMASK(7, 7) +#define RTL8365MB_L2_UC_D4_PORT_MSK GENMASK(10, 8) +#define RTL8365MB_L2_UC_PORT_LO_MSK GENMASK(2, 0) +#define RTL8365MB_L2_UC_D4_AGE_MSK GENMASK(13, 11) +#define RTL8365MB_L2_UC_D4_AUTH_MSK GENMASK(14, 14) +#define RTL8365MB_L2_UC_D4_SA_BLOCK_MSK GENMASK(15, 15) + +#define RTL8365MB_L2_UC_D5_DA_BLOCK_MSK GENMASK(0, 0) +#define RTL8365MB_L2_UC_D5_PRIORITY_MSK GENMASK(3, 1) +#define RTL8365MB_L2_UC_D5_FWD_PRI_MSK GENMASK(4, 4) +#define RTL8365MB_L2_UC_D5_STATIC_MSK GENMASK(5, 5) + +#define RTL8365MB_L2_MC_D0_MAC5_MSK GENMASK(7, 0) +#define RTL8365MB_L2_MC_D0_MAC4_MSK GENMASK(15, 8) +#define RTL8365MB_L2_MC_D1_MAC3_MSK GENMASK(7, 0) +#define RTL8365MB_L2_MC_D1_MAC2_MSK GENMASK(15, 8) +#define RTL8365MB_L2_MC_D2_MAC1_MSK GENMASK(7, 0) +#define RTL8365MB_L2_MC_D2_MAC0_MSK GENMASK(15, 8) +#define RTL8365MB_L2_MC_D3_VID_MSK GENMASK(11, 0) +#define RTL8365MB_L2_MC_D3_IVL_MSK GENMASK(13, 13) +#define RTL8365MB_L2_MC_D3_MBR_HI1_MSK GENMASK(15, 14) +#define RTL8365MB_L2_MC_MBR_HI1_MSK GENMASK(9, 8) + +#define RTL8365MB_L2_MC_D4_MBR_MSK GENMASK(7, 0) +#define RTL8365MB_L2_MC_MBR_LO_MSK GENMASK(7, 0) +#define RTL8365MB_L2_MC_D4_IGMPIDX_MSK GENMASK(15, 8) + +#define RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK GENMASK(0, 0) +#define RTL8365MB_L2_MC_D5_PRIORITY_MSK GENMASK(3, 1) +#define RTL8365MB_L2_MC_D5_FWD_PRI_MSK GENMASK(4, 4) +#define RTL8365MB_L2_MC_D5_STATIC_MSK GENMASK(5, 5) +#define RTL8365MB_L2_MC_D5_MBR_HI2_MSK GENMASK(7, 7) +#define RTL8365MB_L2_MC_MBR_HI2_MSK GENMASK(10, 10) + +/* Port flush command registers - writing a 1 to the port's MASK bit will + * initiate the flush procedure. Completion is signalled when the corresponding + * BUSY bit is 0. + */ +#define RTL8365MB_L2_FLUSH_PORT_REG 0x0A36 +#define RTL8365MB_L2_FLUSH_PORT_MSK_MSK GENMASK(7, 0) +#define RTL8365MB_L2_FLUSH_PORT_BUSY_MSK GENMASK(15, 8) + +#define RTL8365MB_L2_FLUSH_PORT_EXT_REG 0x0A35 +#define RTL8365MB_L2_FLUSH_PORT_EXT_MSK_MSK GENMASK(2, 0) +#define RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MSK GENMASK(5, 3) + +#define RTL8365MB_L2_FLUSH_CTRL1_REG 0x0A37 +#define RTL8365MB_L2_FLUSH_CTRL1_VID_MSK GENMASK(11, 0) +#define RTL8365MB_L2_FLUSH_CTRL1_FID_MSK GENMASK(15, 12) + +#define RTL8365MB_L2_FLUSH_CTRL2_REG 0x0A38 +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_MSK GENMASK(1, 0) +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT 0 +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID 1 +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_FID 2 +#define RTL8365MB_L2_FLUSH_CTRL2_TYPE_MSK GENMASK(2, 2) +#define RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC 0 +#define RTL8365MB_L2_FLUSH_CTRL2_TYPE_BOTH 1 + +/* This flushes the entire LUT, reading it back it will turn 0 when the + * operation is complete + */ +#define RTL8365MB_L2_FLUSH_CTRL3_REG 0x0A39 +#define RTL8365MB_L2_FLUSH_CTRL3_MSK GENMASK(0, 0) + +struct rtl8365mb_l2_uc_key { + u8 mac_addr[ETH_ALEN]; + u16 vid; + u16 fid; + bool ivl; + u16 efid; +}; + +struct rtl8365mb_l2_uc { + struct rtl8365mb_l2_uc_key key; + u8 port; + u8 age; + u8 priority; + + bool sa_block; + bool da_block; + bool auth; + bool is_static; + bool sa_pri; + bool fwd_pri; +}; + +struct rtl8365mb_l2_mc_key { + u8 mac_addr[ETH_ALEN]; + union { + u16 vid; /* IVL */ + u16 fid; /* SVL */ + }; + bool ivl; +}; + +struct rtl8365mb_l2_mc { + struct rtl8365mb_l2_mc_key key; + u16 member; + u8 priority; + u8 igmpidx; + + bool is_static; + bool fwd_pri; + bool igmp_asic; +}; + +static void rtl8365mb_l2_data_to_uc(const u16 *data, struct rtl8365mb_l2_uc *uc) +{ + u32 val; + + uc->key.mac_addr[5] = FIELD_GET(RTL8365MB_L2_UC_D0_MAC5_MSK, data[0]); + uc->key.mac_addr[4] = FIELD_GET(RTL8365MB_L2_UC_D0_MAC4_MSK, data[0]); + uc->key.mac_addr[3] = FIELD_GET(RTL8365MB_L2_UC_D1_MAC3_MSK, data[1]); + uc->key.mac_addr[2] = FIELD_GET(RTL8365MB_L2_UC_D1_MAC2_MSK, data[1]); + uc->key.mac_addr[1] = FIELD_GET(RTL8365MB_L2_UC_D2_MAC1_MSK, data[2]); + uc->key.mac_addr[0] = FIELD_GET(RTL8365MB_L2_UC_D2_MAC0_MSK, data[2]); + uc->key.efid = FIELD_GET(RTL8365MB_L2_UC_D4_EFID_MSK, data[4]); + uc->key.vid = FIELD_GET(RTL8365MB_L2_UC_D3_VID_MSK, data[3]); + uc->key.ivl = FIELD_GET(RTL8365MB_L2_UC_D3_IVL_MSK, data[3]); + uc->key.fid = FIELD_GET(RTL8365MB_L2_UC_D4_FID_MSK, data[4]); + uc->age = FIELD_GET(RTL8365MB_L2_UC_D4_AGE_MSK, data[4]); + uc->auth = FIELD_GET(RTL8365MB_L2_UC_D4_AUTH_MSK, data[4]); + + val = FIELD_GET(RTL8365MB_L2_UC_D4_PORT_MSK, data[4]); + uc->port = FIELD_PREP(RTL8365MB_L2_UC_PORT_LO_MSK, val); + val = FIELD_GET(RTL8365MB_L2_UC_D3_PORT_EXT_MSK, data[3]); + uc->port |= FIELD_PREP(RTL8365MB_L2_UC_PORT_HI_MSK, val); + + uc->sa_pri = FIELD_GET(RTL8365MB_L2_UC_D4_SA_PRI_MSK, data[4]); + uc->fwd_pri = FIELD_GET(RTL8365MB_L2_UC_D5_FWD_PRI_MSK, data[5]); + uc->sa_block = FIELD_GET(RTL8365MB_L2_UC_D4_SA_BLOCK_MSK, data[4]); + uc->da_block = FIELD_GET(RTL8365MB_L2_UC_D5_DA_BLOCK_MSK, data[5]); + uc->priority = FIELD_GET(RTL8365MB_L2_UC_D5_PRIORITY_MSK, data[5]); + uc->is_static = FIELD_GET(RTL8365MB_L2_UC_D5_STATIC_MSK, data[5]); +} + +static void rtl8365mb_l2_uc_to_data(const struct rtl8365mb_l2_uc *uc, u16 *data) +{ + u32 val; + + memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2); + data[0] |= + FIELD_PREP(RTL8365MB_L2_UC_D0_MAC5_MSK, uc->key.mac_addr[5]); + data[0] |= + FIELD_PREP(RTL8365MB_L2_UC_D0_MAC4_MSK, uc->key.mac_addr[4]); + data[1] |= + FIELD_PREP(RTL8365MB_L2_UC_D1_MAC3_MSK, uc->key.mac_addr[3]); + data[1] |= + FIELD_PREP(RTL8365MB_L2_UC_D1_MAC2_MSK, uc->key.mac_addr[2]); + data[2] |= + FIELD_PREP(RTL8365MB_L2_UC_D2_MAC1_MSK, uc->key.mac_addr[1]); + data[2] |= + FIELD_PREP(RTL8365MB_L2_UC_D2_MAC0_MSK, uc->key.mac_addr[0]); + data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_VID_MSK, uc->key.vid); + data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_IVL_MSK, uc->key.ivl); + + val = FIELD_GET(RTL8365MB_L2_UC_PORT_HI_MSK, uc->port); + data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_PORT_EXT_MSK, val); + + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_FID_MSK, uc->key.fid); + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_EFID_MSK, uc->key.efid); + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_AGE_MSK, uc->age); + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_AUTH_MSK, uc->auth); + + val = FIELD_GET(RTL8365MB_L2_UC_PORT_LO_MSK, uc->port); + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_PORT_MSK, val); + + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_SA_PRI_MSK, uc->sa_pri); + data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_SA_BLOCK_MSK, uc->sa_block); + data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_FWD_PRI_MSK, uc->fwd_pri); + data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_DA_BLOCK_MSK, uc->da_block); + data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_PRIORITY_MSK, uc->priority); + data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_STATIC_MSK, uc->is_static); +} + +static void rtl8365mb_l2_data_to_mc(const u16 *data, struct rtl8365mb_l2_mc *mc) +{ + u32 val; + + mc->key.mac_addr[5] = FIELD_GET(RTL8365MB_L2_MC_D0_MAC5_MSK, data[0]); + mc->key.mac_addr[4] = FIELD_GET(RTL8365MB_L2_MC_D0_MAC4_MSK, data[0]); + mc->key.mac_addr[3] = FIELD_GET(RTL8365MB_L2_MC_D1_MAC3_MSK, data[1]); + mc->key.mac_addr[2] = FIELD_GET(RTL8365MB_L2_MC_D1_MAC2_MSK, data[1]); + mc->key.mac_addr[1] = FIELD_GET(RTL8365MB_L2_MC_D2_MAC1_MSK, data[2]); + mc->key.mac_addr[0] = FIELD_GET(RTL8365MB_L2_MC_D2_MAC0_MSK, data[2]); + /* key.vid,key.fid shares the same memory space */ + mc->key.vid = FIELD_GET(RTL8365MB_L2_MC_D3_VID_MSK, data[3]); + mc->key.ivl = FIELD_GET(RTL8365MB_L2_MC_D3_IVL_MSK, data[3]); + mc->priority = FIELD_GET(RTL8365MB_L2_MC_D5_PRIORITY_MSK, data[5]); + mc->fwd_pri = FIELD_GET(RTL8365MB_L2_MC_D5_FWD_PRI_MSK, data[5]); + mc->is_static = FIELD_GET(RTL8365MB_L2_MC_D5_STATIC_MSK, data[5]); + + val = FIELD_GET(RTL8365MB_L2_MC_D4_MBR_MSK, data[4]); + mc->member = FIELD_PREP(RTL8365MB_L2_MC_MBR_LO_MSK, val); + val = FIELD_GET(RTL8365MB_L2_MC_D3_MBR_HI1_MSK, data[3]); + mc->member |= FIELD_PREP(RTL8365MB_L2_MC_MBR_HI1_MSK, val); + val = FIELD_GET(RTL8365MB_L2_MC_D5_MBR_HI2_MSK, data[5]); + mc->member |= FIELD_PREP(RTL8365MB_L2_MC_MBR_HI2_MSK, val); + + mc->igmpidx = FIELD_GET(RTL8365MB_L2_MC_D4_IGMPIDX_MSK, data[4]); + mc->igmp_asic = FIELD_GET(RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK, data[5]); +} + +static void rtl8365mb_l2_mc_to_data(const struct rtl8365mb_l2_mc *mc, u16 *data) +{ + u32 val; + + memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2); + data[0] |= FIELD_PREP(RTL8365MB_L2_MC_D0_MAC5_MSK, mc->key.mac_addr[5]); + data[0] |= FIELD_PREP(RTL8365MB_L2_MC_D0_MAC4_MSK, mc->key.mac_addr[4]); + data[1] |= FIELD_PREP(RTL8365MB_L2_MC_D1_MAC3_MSK, mc->key.mac_addr[3]); + data[1] |= FIELD_PREP(RTL8365MB_L2_MC_D1_MAC2_MSK, mc->key.mac_addr[2]); + data[2] |= FIELD_PREP(RTL8365MB_L2_MC_D2_MAC1_MSK, mc->key.mac_addr[1]); + data[2] |= FIELD_PREP(RTL8365MB_L2_MC_D2_MAC0_MSK, mc->key.mac_addr[0]); + data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_VID_MSK, mc->key.vid); + data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_IVL_MSK, mc->key.ivl); + + val = FIELD_GET(RTL8365MB_L2_MC_MBR_HI1_MSK, mc->member); + data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_MBR_HI1_MSK, val); + + val = FIELD_GET(RTL8365MB_L2_MC_MBR_LO_MSK, mc->member); + data[4] |= FIELD_PREP(RTL8365MB_L2_MC_D4_MBR_MSK, val); + + data[4] |= FIELD_PREP(RTL8365MB_L2_MC_D4_IGMPIDX_MSK, mc->igmpidx); + data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK, mc->igmp_asic); + data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_PRIORITY_MSK, mc->priority); + data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_FWD_PRI_MSK, mc->fwd_pri); + data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_STATIC_MSK, mc->is_static); + + val = FIELD_GET(RTL8365MB_L2_MC_MBR_HI2_MSK, mc->member); + data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_MBR_HI2_MSK, val); +} + +/* + * rtl8365mb_l2_get_next_uc() - get the next Unicast L2 entry + * @priv: realtek_priv pointer + * @addr: as input, the table index to start the walk + * as output, the found table index + * @port: restrict the walk on entries related to port + * @entry: returned L2 Unicast table entry + * + * This function gets the next unicast L2 table entry starting from @addr + * and checking exclusively entries related to @port. + * + * On success, it returns 0, updates @addr to the index of the found entry, + * and populates @entry. If the search reaches the end of the table and + * wraps around and @addr will be strictly lower than the input @addr. + * Callers must detect this wrap-around condition to prevent infinite loops. + * + * If the table contains no matching entries at all, it returns -ENOENT + * and leaves @addr and @entry unmodified. + * + * Return: Returns 0 on success, a negative error on failure. + **/ +int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port, + struct realtek_fdb_entry *entry) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; + struct rtl8365mb_l2_uc uc; + int ret; + + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_READ, addr, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, + port, data, RTL8365MB_L2_ENTRY_SIZE); + if (ret) + return ret; + + rtl8365mb_l2_data_to_uc(data, &uc); + + ether_addr_copy(entry->mac_addr, uc.key.mac_addr); + entry->vid = uc.key.vid; + entry->is_static = uc.is_static; + + return 0; +} + +int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port, + const unsigned char mac_addr[static ETH_ALEN], + u16 efid, u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; + struct rtl8365mb_l2_uc uc = { 0 }; + u16 addr; + int ret; + + memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN); + uc.key.efid = efid; + uc.key.fid = 0; + uc.key.ivl = true; + uc.key.vid = vid; + + uc.port = port; + /* Entries programmed by DSA (including those dynamically learned by + * the software bridge and injected into the CPU port via assisted + * learning) must be static. We do not let HW decrease age behind the + * OS's back. As a trade-off, these will show up as permanent to users. + */ + uc.is_static = true; + /* age greater than 0 adds/updates entries */ + uc.age = 1; + rtl8365mb_l2_uc_to_data(&uc, data); + + /* add the new entry or update an existing one */ + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + + /* Assume the missing new entry as the table is full */ + if (ret == -ENOENT) + return -ENOSPC; + + /* addr will hold the table index, but it is not used here */ + return ret; +} + +int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port, + const unsigned char mac_addr[static ETH_ALEN], + u16 efid, u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; + struct rtl8365mb_l2_uc uc = { 0 }; + u16 addr; + int ret; + + memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN); + uc.key.efid = efid; + uc.key.fid = 0; + uc.key.ivl = true; + uc.key.vid = vid; + /* age 0 deletes the entry */ + uc.age = 0; + rtl8365mb_l2_uc_to_data(&uc, data); + + /* it looks like the switch will always add/update the entry, + * even when age is 0 or uc.key did not match an existing entry, + * just to immediately drop it because age is zero. You can still + * get the added/updated address from @addr + */ + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + + if (ret == -ENOENT) { + dev_dbg(priv->dev, "%s: %pM vid=%d efid=%d missing\n", + __func__, mac_addr, vid, efid); + /* Silently return success */ + return 0; + } + + /* addr will hold the table index, but it is not used here */ + return ret; +} + +int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid) +{ + int mode = vid ? RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID : + RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT; + u32 val, mask; + int ret; + + mutex_lock(&priv->map_lock); + + /* Configure flushing mode; only flush dynamic entries */ + ret = regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL2_REG, + FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_MODE_MSK, + mode) | + FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_TYPE_MSK, + RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC)); + if (ret) + goto out; + + ret = regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL1_REG, + FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL1_VID_MSK, vid)); + + if (ret) + goto out; + /* Now issue the flush command and wait for its completion. There are + * two registers for this purpose, and which one to use depends on the + * port number. The _EXT register is for ports 8 or higher. + */ + if (port < 8) { + val = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_MSK_MSK, + BIT(port) & 0xFF); + ret = regmap_write(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_REG, val); + if (ret) + goto out; + + mask = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_BUSY_MSK, + BIT(port) & 0xFF); + ret = regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_REG, + val, !(val & mask), 10, 10000); + if (ret) + goto out; + } else { + val = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_MSK_MSK, + BIT(port) >> 8); + ret = regmap_write(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_EXT_REG, val); + if (ret) + goto out; + + mask = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MSK, + BIT(port) >> 8); + ret = regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_EXT_REG, + val, !(val & mask), 10, 10000); + if (ret) + goto out; + } + +out: + mutex_unlock(&priv->map_lock); + + return ret; +} + +int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; + struct rtl8365mb_l2_mc mc = { 0 }; + u16 addr; + int ret; + + memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN); + mc.key.vid = vid; + mc.key.ivl = true; + /* Already set the port and is_static, although not used in OP_READ, + * data will be ready for OP_WRITE if it is a new entry. + */ + mc.member |= BIT(port); + mc.is_static = 1; + rtl8365mb_l2_mc_to_data(&mc, data); + + /* First look for an existing entry (to get existing port members) */ + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_READ, &addr, + RTL8365MB_TABLE_L2_METHOD_MAC, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + if (!ret) { + /* There is already an entry... */ + rtl8365mb_l2_data_to_mc(data, &mc); + dev_dbg(priv->dev, + "%s: found %pM addr=%d member=0x%x igmpidx=0x%x %s\n", + __func__, mac_addr, addr, mc.member, mc.igmpidx, + mc.is_static ? "static" : "dynamic"); + /* the port must be added as a member */ + mc.member |= BIT(port); + + if (!mc.is_static) { + dev_dbg(priv->dev, + "%s: promoting addr=%d group to static\n", + __func__, addr); + mc.is_static = 1; + } + + rtl8365mb_l2_mc_to_data(&mc, data); + } else if (ret == -ENOENT) { + /* New entry, no need to update data again as it already + * includes the member. + * + * Multicast hardware entries do not support EFID (bridge + * isolation). However, traffic isolation is still maintained + * because the hardware applies the port isolation masks + * (pmasks) configured in bridge_join after the L2 lookup. + * Entries from different bridges will collide on the same + * MAC+VID slot with an OR'ed member mask, but packets will + * only exit through ports allowed by the source port's pmask. + */ + } else { + return ret; + } + + /* add the new entry or update an existing one */ + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + + /* Assume the missing new entry as the table is full */ + if (ret == -ENOENT) + return -ENOSPC; + + return ret; +} + +int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 }; + struct rtl8365mb_l2_mc mc = { 0 }; + u16 addr; + int ret; + + memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN); + mc.key.vid = vid; + mc.key.ivl = true; + rtl8365mb_l2_mc_to_data(&mc, data); + + /* First look for an existing entry (to get existing port members) */ + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_READ, &addr, + RTL8365MB_TABLE_L2_METHOD_MAC, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + if (ret == -ENOENT) { + dev_dbg(priv->dev, "%s: %pM vid=%d missing\n", + __func__, mac_addr, vid); + /* Silently return success */ + return 0; + } + + if (ret) + /* Return on any other error */ + return ret; + + rtl8365mb_l2_data_to_mc(data, &mc); + dev_dbg(priv->dev, + "%s: found %pM addr=%d member=0x%x igmpidx=0x%x %s\n", + __func__, mac_addr, addr, mc.member, mc.igmpidx, + mc.is_static ? "static" : "dynamic"); + /* the port must be removed as a member */ + mc.member &= ~BIT(port); + if (!mc.member) { + /* Multicast entries do not have an age field. Clearing both + * the member portmask and is_static flags is the hardware + * signal to invalidate and reclaim the L2 table slot. + */ + mc.is_static = 0; + mc.igmpidx = 0; + mc.priority = 0; + mc.fwd_pri = 0; + mc.igmp_asic = 0; + } + rtl8365mb_l2_mc_to_data(&mc, data); + + /* update the existing entry. */ + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + return ret; +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_l2.h b/drivers/net/dsa/realtek/rtl8365mb_l2.h new file mode 100644 index 000000000000..9470cf059ce5 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_l2.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Forwarding and multicast database interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin Šipraga + */ + +#ifndef _REALTEK_RTL8365MB_L2_H +#define _REALTEK_RTL8365MB_L2_H + +#include +#include + +#include "realtek.h" + +int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port, + struct realtek_fdb_entry *entry); +int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port, + const unsigned char addr[static ETH_ALEN], + u16 efid, u16 vid); +int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port, + const unsigned char addr[static ETH_ALEN], + u16 efid, u16 vid); +int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid); + +int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid); +int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid); + +#endif /* _REALTEK_RTL8365MB_L2_H */ diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb_main.c similarity index 75% rename from drivers/net/dsa/realtek/rtl8365mb.c rename to drivers/net/dsa/realtek/rtl8365mb_main.c index c35cef01ec26..5ac091bf93c9 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -104,14 +104,20 @@ #include "realtek-smi.h" #include "realtek-mdio.h" #include "rtl83xx.h" +#include "rtl8365mb_l2.h" +#include "rtl8365mb_vlan.h" /* 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 11 -#define RTL8365MB_MAX_NUM_EXTINTS 3 +/* Valid for the whole family except RTL8370B, which has 4160 entries. + * RTL8370B is mentioned in vendor code but it might not even belong + * to the same RTL8367C family. + */ #define RTL8365MB_LEARN_LIMIT_MAX 2112 +#define RTL8365MB_MAX_NUM_EXTINTS 3 /* Chip identification registers */ #define RTL8365MB_CHIP_ID_REG 0x1300 @@ -284,6 +290,15 @@ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) #define RTL8365MB_PORT_ISOLATION_MASK 0x07FF +/* Extended filter ID registers - used to key forwarding database with IVL */ +#define RTL8365MB_EFID_MASK GENMASK(2, 0) +#define RTL8365MB_PORT_EFID_REG_BASE 0x0A32 +#define RTL8365MB_PORT_EFID_REG(_p) \ + (RTL8365MB_PORT_EFID_REG_BASE + ((_p) >> 2)) +#define RTL8365MB_PORT_EFID_OFFSET(_p) (((_p) & 0x3) << 2) +#define RTL8365MB_PORT_EFID_MASK(_p) \ + (RTL8365MB_EFID_MASK << RTL8365MB_PORT_EFID_OFFSET(_p)) + /* MSTP port state registers - indexed by tree instance */ #define RTL8365MB_MSTI_CTRL_BASE 0x0A00 #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ @@ -292,6 +307,72 @@ #define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) +/* Unknown unicast DA flooding port mask */ +#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG 0x0890 +#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_MASK 0x07FF + +/* Unknown multicast DA flooding port mask */ +#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG 0x0891 +#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_MASK 0x07FF + +/* Broadcast flooding port mask */ +#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG 0x0892 +#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_MASK 0x07FF + +#define RTL8365MB_SUPPORTED_BRIDGE_FLAGS \ + (BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD) + +/* Miscellaneous port configuration register, incl. VLAN egress mode */ +#define RTL8365MB_PORT_MISC_CFG_REG_BASE 0x000E +#define RTL8365MB_PORT_MISC_CFG_REG(_p) \ + (RTL8365MB_PORT_MISC_CFG_REG_BASE + ((_p) << 5)) +#define RTL8365MB_PORT_MISC_CFG_SMALL_TAG_IPG_MASK 0x8000 +#define RTL8365MB_PORT_MISC_CFG_TX_ITFSP_MODE_MASK 0x4000 +#define RTL8365MB_PORT_MISC_CFG_FLOWCTRL_INDEP_MASK 0x2000 +#define RTL8365MB_PORT_MISC_CFG_DOT1Q_REMARK_ENABLE_MASK 0x1000 +#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_FLOWCTRL_MASK 0x0800 +#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_IFG_MASK 0x0400 +#define RTL8365MB_PORT_MISC_CFG_RX_SPC_MASK 0x0200 +#define RTL8365MB_PORT_MISC_CFG_CRC_SKIP_MASK 0x0100 +#define RTL8365MB_PORT_MISC_CFG_PKTGEN_TX_FIRST_MASK 0x0080 +#define RTL8365MB_PORT_MISC_CFG_MAC_LOOPBACK_MASK 0x0040 +/* See &rtl8365mb_vlan_egress_mode */ +#define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK 0x0030 +#define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK 0x000F + +/** + * enum rtl8365mb_vlan_egress_mode - port VLAN egress mode + * @RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL: follow untag mask in VLAN4k table entry + * @RTL8365MB_VLAN_EGRESS_MODE_KEEP: the VLAN tag format of egressed packets + * will remain the same as their ingressed format, but the priority and VID + * fields may be altered + * @RTL8365MB_VLAN_EGRESS_MODE_PRI_TAG: always egress with priority tag + * @RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP: the VLAN tag format of egressed + * packets will remain the same as their ingressed format, and neither the + * priority nor VID fields can be altered + */ +enum rtl8365mb_vlan_egress_mode { + RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL = 0, + RTL8365MB_VLAN_EGRESS_MODE_KEEP = 1, + RTL8365MB_VLAN_EGRESS_MODE_PRI_TAG = 2, + RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP = 3, +}; + +/* VLAN control register */ +#define RTL8365MB_VLAN_CTRL_REG 0x07A8 +#define RTL8365MB_VLAN_CTRL_EN_MASK 0x0001 + +/* VLAN ingress filter register */ +#define RTL8365MB_VLAN_INGRESS_REG 0x07A9 +#define RTL8365MB_VLAN_INGRESS_MASK GENMASK(10, 0) +#define RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_OFFSET(_p) (_p) +#define RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(_p) BIT(_p) + +/* VLAN "transparent" setting registers */ +#define RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG_BASE 0x09D0 +#define RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG(_p) \ + (RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG_BASE + (_p)) + /* MIB counter value registers */ #define RTL8365MB_MIB_COUNTER_BASE 0x1000 #define RTL8365MB_MIB_COUNTER_REG(_x) (RTL8365MB_MIB_COUNTER_BASE + (_x)) @@ -544,6 +625,20 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { .jam_table = rtl8365mb_init_jam_8365mb_vc, .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), }, + { + .name = "RTL8367SB", + .chip_id = 0x6367, + .chip_ver = 0x0010, + .extints = { + { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) | + PHY_INTF(SGMII) | PHY_INTF(HSGMII) }, + { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + }, + .jam_table = rtl8365mb_init_jam_8365mb_vc, + .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + }, { .name = "RTL8367RB-VB", .chip_id = 0x6367, @@ -789,8 +884,8 @@ static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); if (ret) { dev_err(priv->dev, - "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy, - regnum, ocp_addr, ret); + "failed to read PHY%d reg %02x @ %04x, ret %pe\n", phy, + regnum, ocp_addr, ERR_PTR(ret)); return ret; } @@ -817,8 +912,8 @@ static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); if (ret) { dev_err(priv->dev, - "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy, - regnum, ocp_addr, ret); + "failed to write PHY%d reg %02x @ %04x, ret %pe\n", phy, + regnum, ocp_addr, ERR_PTR(ret)); return ret; } @@ -1068,8 +1163,8 @@ static void rtl8365mb_phylink_mac_config(struct phylink_config *config, ret = rtl8365mb_ext_config_rgmii(priv, port, state->interface); if (ret) dev_err(priv->dev, - "failed to configure RGMII mode on port %d: %d\n", - port, ret); + "failed to configure RGMII mode on port %d: %pe\n", + port, ERR_PTR(ret)); return; } @@ -1098,8 +1193,8 @@ static void rtl8365mb_phylink_mac_link_down(struct phylink_config *config, false, false); if (ret) dev_err(priv->dev, - "failed to reset forced mode on port %d: %d\n", - port, ret); + "failed to reset forced mode on port %d: %pe\n", + port, ERR_PTR(ret)); return; } @@ -1129,8 +1224,8 @@ static void rtl8365mb_phylink_mac_link_up(struct phylink_config *config, rx_pause); if (ret) dev_err(priv->dev, - "failed to force mode on port %d: %d\n", port, - ret); + "failed to force mode on port %d: %pe\n", port, + ERR_PTR(ret)); return; } @@ -1196,6 +1291,286 @@ static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); } +static int rtl8365mb_port_set_transparent(struct realtek_priv *priv, + int igr_port, int egr_port, + bool enable) +{ + dev_dbg(priv->dev, "%s transparent VLAN from %d to %d\n", + enable ? "Enable" : "Disable", igr_port, egr_port); + + /* "Transparent" between the two ports means that packets forwarded by + * igr_port and egressed on egr_port will not be filtered by the usual + * VLAN membership settings. + */ + return regmap_update_bits(priv->map, + RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG(egr_port), + BIT(igr_port), enable ? BIT(igr_port) : 0); +} + +static int rtl8365mb_port_set_ingress_filtering(struct realtek_priv *priv, + int port, bool enable) +{ + /* Ingress filtering enabled: Discard VLAN-tagged frames if the port is + * not a member of the VLAN with which the packet is associated. + * Untagged packets will also be discarded unless the port has a PVID + * programmed. Priority-tagged frames are treated as untagged frames. + * + * Ingress filtering disabled: Accept all tagged and untagged frames. + */ + return regmap_update_bits(priv->map, RTL8365MB_VLAN_INGRESS_REG, + RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(port), + enable ? + RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(port) : + 0); +} + +static int +rtl8365mb_port_set_vlan_egress_mode(struct realtek_priv *priv, int port, + enum rtl8365mb_vlan_egress_mode mode) +{ + u32 val; + + val = FIELD_PREP(RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK, mode); + return regmap_update_bits(priv->map, + RTL8365MB_PORT_MISC_CFG_REG(port), + RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK, val); +} + +static int rtl8365mb_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct netlink_ext_ack *extack) +{ + enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame; + enum rtl8365mb_vlan_egress_mode mode; + struct realtek_priv *priv = ds->priv; + u32 configured_ports = 0; + struct dsa_port *dp; + u16 pvid_vid; + int ret; + + dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, + vlan_filtering ? "enable" : "disable"); + + ret = rtl8365mb_vlan_port_get_framefilter(priv, port, + &prev_accepted_frame); + if (ret) { + NL_SET_ERR_MSG_MOD(extack, + "Failed to get current framefilter"); + return ret; + } + + /* While filtering, only accepts untagged frames if PVID is enabled */ + if (vlan_filtering) { + ret = rtl8365mb_vlan_port_get_pvid(priv, port, &pvid_vid); + if (ret) + return ret; + + if (pvid_vid) + accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME; + else + accepted_frame = RTL8365MB_FRAME_TYPE_TAGGED_ONLY; + } else { + accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME; + } + + /* When vlan filter is enable/disabled in a bridge, this function is + * called for all member ports. We need to enable/disable ingress + * VLAN membership check. + */ + ret = rtl8365mb_port_set_ingress_filtering(priv, port, vlan_filtering); + if (ret) + return ret; + + /* However, we also enable/disable egress filtering because the switch + * still consider the egress interface VLAN membership to forward the + * traffic. We enable/disable that check disabling/enabling transparent + * VLAN between the ingress port and all other available ports. + */ + dsa_switch_for_each_available_port(dp, ds) { + /* port isolation will still keep traffic inside the bridge */ + ret = rtl8365mb_port_set_transparent(priv, port, dp->index, + !vlan_filtering); + if (ret) + goto undo_transparent; + + configured_ports |= BIT(dp->index); + } + + if (accepted_frame != prev_accepted_frame) { + ret = rtl8365mb_vlan_port_set_framefilter(priv, port, + accepted_frame); + if (ret) { + NL_SET_ERR_MSG_MOD(extack, + "Failed to set port framefilter"); + goto undo_transparent; + } + } + + /* When VLAN filtering is disabled, preserve frames exactly as received. + * Otherwise, the VLAN egress pipeline may still alter tag state + * according to VLAN membership and untag configuration. + */ + if (vlan_filtering) + mode = RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL; + else + mode = RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP; + + ret = rtl8365mb_port_set_vlan_egress_mode(priv, port, mode); + if (ret) + goto undo_set_framefilter; + + return ret; + +undo_set_framefilter: + if (prev_accepted_frame != accepted_frame) + rtl8365mb_vlan_port_set_framefilter(priv, port, + prev_accepted_frame); +undo_transparent: + /* The DSA core guarantees this callback is only invoked on an actual + * state transition, ensuring the previous hardware state was the + * opposite (!vlan_filtering). It is also called during setup but, in + * that case, any failure here aborts the entire switch initialization. + * + * VLAN_INGRESS and VLAN_EGRESS_TRANSPARENT states are directly derived + * from vlan_filtering. That way, we can simply undo it without + * checking the current HW state as we do with VLAN_EGRESS_MODE. + */ + dsa_switch_for_each_port(dp, ds) { + if (configured_ports & BIT(dp->index)) + rtl8365mb_port_set_transparent(priv, port, dp->index, + vlan_filtering); + } + + rtl8365mb_port_set_ingress_filtering(priv, port, !vlan_filtering); + + return ret; +} + +static int rtl8365mb_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); + u16 pvid_vid; + struct realtek_priv *priv = ds->priv; + int ret; + + dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n", + vlan->vid, port, untagged ? "untagged" : "tagged", + pvid ? "PVID" : "no PVID"); + + /* VID == 0 is reserved in this driver */ + if (vlan->vid == 0) { + NL_SET_ERR_MSG_MOD(extack, + "VLAN 0 is reserved by this driver"); + return -EOPNOTSUPP; + } + + mutex_lock(&priv->vlan_lock); + + ret = rtl8365mb_vlan_port_get_pvid(priv, port, &pvid_vid); + if (ret) + goto out_unlock; + + /* Set PVID if needed */ + if (pvid) { + ret = rtl8365mb_vlan_pvid_port_set(ds, port, vlan->vid, + extack); + if (ret) + goto out_unlock; + } else { + /* or try to unset it if not */ + ret = rtl8365mb_vlan_pvid_port_clear(ds, port, vlan->vid); + if (ret) + goto out_unlock; + } + + /* add port to vlan4k. It knows nothing about PVID */ + ret = rtl8365mb_vlan_4k_port_add(ds, port, vlan, extack); + if (ret) + goto undo_set_pvid; + + ret = 0; + goto out_unlock; + +undo_set_pvid: + /* undo the pvid definition */ + if (pvid != (pvid_vid == vlan->vid)) { + if (pvid_vid) + (void)rtl8365mb_vlan_pvid_port_set(ds, port, pvid_vid, + NULL); + else + (void)rtl8365mb_vlan_pvid_port_clear(ds, port, + vlan->vid); + } +out_unlock: + mutex_unlock(&priv->vlan_lock); + return ret; +} + +static int rtl8365mb_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); + struct realtek_priv *priv = ds->priv; + int ret; + + dev_dbg(priv->dev, "del VLAN %d on port %d, %s, %s\n", + vlan->vid, port, untagged ? "untagged" : "tagged", + pvid ? "PVID" : "no PVID"); + + /* VID == 0 is reserved in this driver */ + if (vlan->vid == 0) + return -EOPNOTSUPP; + + mutex_lock(&priv->vlan_lock); + ret = rtl8365mb_vlan_pvid_port_clear(ds, port, vlan->vid); + if (ret) + goto out_unlock; + + ret = rtl8365mb_vlan_4k_port_del(ds, port, vlan); + /* There is little incentive to try to undo the removal of PVID (if it + * was really in use) as an error here might indicate the ASIC stopped + * to answer. + */ + +out_unlock: + mutex_unlock(&priv->vlan_lock); + return ret; +} + +/* VLAN support is always enabled in the switch. + * + * Standalone forwarding relies on transparent VLAN mode combined with per-port + * isolation masks restricting egress to CPU ports only. + * + */ +static int rtl8365mb_vlan_setup(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + struct dsa_port *dp; + int ret; + + dsa_switch_for_each_available_port(dp, ds) { + /* Disable vlan-filtering for all ports */ + ret = rtl8365mb_port_vlan_filtering(ds, dp->index, false, NULL); + if (ret) { + dev_err(priv->dev, + "Failed to disable vlan filtering on port %d\n", + dp->index); + return ret; + } + } + + /* VLAN is always enabled. */ + ret = regmap_update_bits(priv->map, RTL8365MB_VLAN_CTRL_REG, + RTL8365MB_VLAN_CTRL_EN_MASK, + FIELD_PREP(RTL8365MB_VLAN_CTRL_EN_MASK, 1)); + return ret; +} + static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, bool enable) { @@ -1208,10 +1583,87 @@ static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); } +static int rtl8365mb_port_set_ucast_flood(struct realtek_priv *priv, int port, + bool enable) +{ + /* Frames with unknown unicast DA will be flooded to a programmable + * port mask that by default includes all ports. Add or remove + * the specified port from this port mask accordingly. + */ + return regmap_update_bits(priv->map, + RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG, + BIT(port), enable ? BIT(port) : 0); +} + +static int rtl8365mb_port_set_mcast_flood(struct realtek_priv *priv, int port, + bool enable) +{ + return regmap_update_bits(priv->map, + RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG, + BIT(port), enable ? BIT(port) : 0); +} + +static int rtl8365mb_port_set_bcast_flood(struct realtek_priv *priv, int port, + bool enable) +{ + return regmap_update_bits(priv->map, + RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG, + BIT(port), enable ? BIT(port) : 0); +} + +static int rtl8365mb_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv = ds->priv; + + dev_dbg(priv->dev, "pre_bridge_flags port:%d flags:%lx supported:%lx\n", + port, flags.mask, RTL8365MB_SUPPORTED_BRIDGE_FLAGS); + + if (flags.mask & ~RTL8365MB_SUPPORTED_BRIDGE_FLAGS) + return -EINVAL; + + return 0; +} + +static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port, + u32 efid) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_EFID_REG(port), + RTL8365MB_PORT_EFID_MASK(port), + efid << RTL8365MB_PORT_EFID_OFFSET(port)); +} + +/* Port isolation manipulation functions. + * + * The port isolation register controls the forwarding mask of a given + * port. The switch will not forward packets ingressed on a given port + * to ports which are not enabled in its forwarding mask. + * + * The port forwarding mask has the highest priority in forwarding + * decisions. The only exception to this rule is when the switch + * receives a packet on its CPU port with ALLOW=0. In that case the TX + * field of the CPU tag will override the forwarding port mask. + */ static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, u32 mask) { - return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); + return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask); +} + +static int rtl8365mb_port_add_isolation(struct realtek_priv *priv, int port, + u32 mask) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask, mask); +} + +static int rtl8365mb_port_remove_isolation(struct realtek_priv *priv, int port, + u32 mask) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask, 0); } static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, @@ -1285,8 +1737,8 @@ static void rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *da mib->length, &data[i]); if (ret) { dev_err(priv->dev, - "failed to read port %d counters: %d\n", port, - ret); + "failed to read port %d counters: %pe\n", port, + ERR_PTR(ret)); break; } } @@ -1540,18 +1992,15 @@ static void rtl8365mb_stats_setup(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; struct dsa_switch *ds = &priv->ds; - int i; + struct dsa_port *dp; /* Per-chip global mutex to protect MIB counter access, since doing * so requires accessing a series of registers in a particular order. */ mutex_init(&mb->mib_lock); - for (i = 0; i < priv->num_ports; i++) { - struct rtl8365mb_port *p = &mb->ports[i]; - - if (dsa_is_unused_port(ds, i)) - continue; + dsa_switch_for_each_available_port(dp, ds) { + struct rtl8365mb_port *p = &mb->ports[dp->index]; /* Per-port spinlock to protect the stats64 data */ spin_lock_init(&p->stats_lock); @@ -1567,13 +2016,10 @@ static void rtl8365mb_stats_teardown(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; struct dsa_switch *ds = &priv->ds; - int i; + struct dsa_port *dp; - for (i = 0; i < priv->num_ports; i++) { - struct rtl8365mb_port *p = &mb->ports[i]; - - if (dsa_is_unused_port(ds, i)) - continue; + dsa_switch_for_each_available_port(dp, ds) { + struct rtl8365mb_port *p = &mb->ports[dp->index]; cancel_delayed_work_sync(&p->mib_work); } @@ -1632,13 +2078,17 @@ static irqreturn_t rtl8365mb_irq(int irq, void *data) for_each_set_bit(line, &line_changes, priv->num_ports) { int child_irq = irq_find_mapping(priv->irqdomain, line); + if (!child_irq) + continue; + handle_nested_irq(child_irq); } return IRQ_HANDLED; out_error: - dev_err(priv->dev, "failed to read interrupt status: %d\n", ret); + dev_err(priv->dev, "failed to read interrupt status: %pe\n", + ERR_PTR(ret)); out_none: return IRQ_NONE; @@ -1652,10 +2102,14 @@ static struct irq_chip rtl8365mb_irq_chip = { static int rtl8365mb_irq_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { - irq_set_chip_data(irq, domain->host_data); + struct realtek_priv *priv = domain->host_data; + struct rtl8365mb *mb = priv->chip_data; + + irq_set_chip_data(irq, priv); irq_set_chip_and_handler(irq, &rtl8365mb_irq_chip, handle_simple_irq); irq_set_nested_thread(irq, 1); irq_set_noprobe(irq); + irq_set_parent(irq, mb->irq); return 0; } @@ -1694,13 +2148,14 @@ static int rtl8365mb_irq_disable(struct realtek_priv *priv) static int rtl8365mb_irq_setup(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; + struct dsa_switch *ds = &priv->ds; struct device_node *intc; + struct dsa_port *dp; u32 irq_trig; int virq; int irq; u32 val; int ret; - int i; intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller"); if (!intc) { @@ -1711,13 +2166,19 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) /* rtl8365mb IRQs cascade off this one */ irq = of_irq_get(intc, 0); if (irq <= 0) { - if (irq != -EPROBE_DEFER) - dev_err(priv->dev, "failed to get parent irq: %d\n", - irq); - ret = irq ? irq : -EINVAL; + if (!irq) { + dev_err(priv->dev, "failed to map IRQ\n"); + ret = -EINVAL; + } else { + ret = dev_err_probe(priv->dev, irq, + "failed to get parent irq\n"); + } goto out_put_node; } + /* Store the irq so that we know to map and free it during teardown */ + mb->irq = irq; + priv->irqdomain = irq_domain_create_linear(of_fwnode_handle(intc), priv->num_ports, &rtl8365mb_irqdomain_ops, priv); if (!priv->irqdomain) { @@ -1726,8 +2187,8 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) goto out_put_node; } - for (i = 0; i < priv->num_ports; i++) { - virq = irq_create_mapping(priv->irqdomain, i); + dsa_switch_for_each_available_port(dp, ds) { + virq = irq_create_mapping(priv->irqdomain, dp->index); if (!virq) { dev_err(priv->dev, "failed to create irq domain mapping\n"); @@ -1776,13 +2237,11 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) ret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT, "rtl8365mb", priv); if (ret) { - dev_err(priv->dev, "failed to request irq: %d\n", ret); + dev_err(priv->dev, "failed to request irq: %pe\n", + ERR_PTR(ret)); goto out_remove_irqdomain; } - /* Store the irq so that we know to free it during teardown */ - mb->irq = irq; - ret = rtl8365mb_irq_enable(priv); if (ret) goto out_free_irq; @@ -1793,18 +2252,20 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) out_free_irq: free_irq(mb->irq, priv); - mb->irq = 0; out_remove_irqdomain: - for (i = 0; i < priv->num_ports; i++) { - virq = irq_find_mapping(priv->irqdomain, i); - irq_dispose_mapping(virq); + dsa_switch_for_each_port(dp, ds) { + virq = irq_find_mapping(priv->irqdomain, dp->index); + + if (virq) + irq_dispose_mapping(virq); } irq_domain_remove(priv->irqdomain); priv->irqdomain = NULL; out_put_node: + mb->irq = 0; of_node_put(intc); return ret; @@ -1813,8 +2274,9 @@ static int rtl8365mb_irq_setup(struct realtek_priv *priv) static void rtl8365mb_irq_teardown(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; + struct dsa_switch *ds = &priv->ds; + struct dsa_port *dp; int virq; - int i; if (mb->irq) { free_irq(mb->irq, priv); @@ -1822,9 +2284,15 @@ static void rtl8365mb_irq_teardown(struct realtek_priv *priv) } if (priv->irqdomain) { - for (i = 0; i < priv->num_ports; i++) { - virq = irq_find_mapping(priv->irqdomain, i); - irq_dispose_mapping(virq); + /* Unused ports with a linked PHY still have an active IRQ + * mapping that must be disposed of during teardown. Loop + * through all ports. + */ + dsa_switch_for_each_port(dp, ds) { + virq = irq_find_mapping(priv->irqdomain, dp->index); + + if (virq) + irq_dispose_mapping(virq); } irq_domain_remove(priv->irqdomain); @@ -1942,24 +2410,27 @@ static int rtl8365mb_setup(struct dsa_switch *ds) { struct realtek_priv *priv = ds->priv; struct rtl8365mb_cpu *cpu; - struct dsa_port *cpu_dp; + u32 downports_mask = 0; + u32 upports_mask = 0; struct rtl8365mb *mb; + struct dsa_port *dp; int ret; - int i; mb = priv->chip_data; cpu = &mb->cpu; ret = rtl8365mb_reset_chip(priv); if (ret) { - dev_err(priv->dev, "failed to reset chip: %d\n", ret); + dev_err(priv->dev, "failed to reset chip: %pe\n", + ERR_PTR(ret)); goto out_error; } /* Configure switch to vendor-defined initial state */ ret = rtl8365mb_switch_init(priv); if (ret) { - dev_err(priv->dev, "failed to initialize switch: %d\n", ret); + dev_err(priv->dev, "failed to initialize switch: %pe\n", + ERR_PTR(ret)); goto out_error; } @@ -1970,50 +2441,125 @@ static int rtl8365mb_setup(struct dsa_switch *ds) else if (ret) dev_info(priv->dev, "no interrupt support\n"); - /* Configure CPU tagging */ - dsa_switch_for_each_cpu_port(cpu_dp, ds) { - cpu->mask |= BIT(cpu_dp->index); - - if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) - cpu->trap_port = cpu_dp->index; + dsa_switch_for_each_port(dp, ds) { + /* Cascading (DSA links) is not supported yet. + * Historically, the driver has always been broken + * without a dedicated CPU port because CPU tagging + * would be disabled, rendering the switch entirely + * non-functional for DSA operations. + */ + if (dsa_port_is_dsa(dp)) { + dev_err(priv->dev, "Cascading (DSA link) not supported\n"); + ret = -EOPNOTSUPP; + goto out_teardown_irq; + } } - cpu->enable = cpu->mask > 0; - ret = rtl8365mb_cpu_config(priv); - if (ret) - goto out_teardown_irq; - /* Configure ports */ - for (i = 0; i < priv->num_ports; i++) { - struct rtl8365mb_port *p = &mb->ports[i]; - - if (dsa_is_unused_port(ds, i)) - continue; - - /* Forward only to the CPU */ - ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask); - if (ret) - goto out_teardown_irq; - - /* Disable learning */ - ret = rtl8365mb_port_set_learning(priv, i, false); - if (ret) - goto out_teardown_irq; + /* Start with all ports blocked, including unused ports */ + dsa_switch_for_each_port(dp, ds) { + struct rtl8365mb_port *p = &mb->ports[dp->index]; /* Set the initial STP state of all ports to DISABLED, otherwise * ports will still forward frames to the CPU despite being * administratively down by default. */ - rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED); + rtl8365mb_port_stp_state_set(ds, dp->index, BR_STATE_DISABLED); + + /* Start with all port completely isolated */ + ret = rtl8365mb_port_set_isolation(priv, dp->index, 0); + if (ret) + goto out_teardown_irq; + + /* Set the default EFID 0 for standalone mode */ + ret = rtl8365mb_port_set_efid(priv, dp->index, 0); + if (ret) + goto out_teardown_irq; + + /* Disable learning */ + ret = rtl8365mb_port_set_learning(priv, dp->index, false); + if (ret) + goto out_teardown_irq; + + /* Enable all types of flooding */ + ret = rtl83xx_setup_port_flood_control(priv, dp->index); + if (ret) + goto out_teardown_irq; /* Set up per-port private data */ p->priv = priv; - p->index = i; + p->index = dp->index; + + /* Collect CPU ports. If we support cascade switches, it should + * also include the upstream DSA ports. + */ + if (!dsa_port_is_cpu(dp)) + continue; + + upports_mask |= BIT(dp->index); } + /* Configure user ports */ + dsa_switch_for_each_port(dp, ds) { + if (!dsa_port_is_user(dp)) + continue; + + /* Forward only to the CPU */ + ret = rtl8365mb_port_set_isolation(priv, dp->index, + upports_mask); + if (ret) + goto out_teardown_irq; + + /* If we support cascade switches, it should also include the + * downstream DSA ports. + */ + downports_mask |= BIT(dp->index); + } + + /* Configure CPU tagging */ + /* If we support cascade switches, it should also include the upstream + * DSA ports. + */ + dsa_switch_for_each_cpu_port(dp, ds) { + /* Use the first CPU port as trap_port */ + if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) + cpu->trap_port = dp->index; + + /* Forward to all user ports */ + ret = rtl8365mb_port_set_isolation(priv, dp->index, + downports_mask); + if (ret) + goto out_teardown_irq; + } + + cpu->mask = upports_mask; + cpu->enable = cpu->mask > 0; + + if (!cpu->enable) { + dev_err(priv->dev, "no CPU port defined\n"); + ret = -EINVAL; + goto out_teardown_irq; + } + + ret = rtl8365mb_cpu_config(priv); + if (ret) + goto out_teardown_irq; + ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN); if (ret) goto out_teardown_irq; + ds->assisted_learning_on_cpu_port = true; + ds->fdb_isolation = true; + /* The EFID is 3 bits, but EFID 0 is reserved for standalone ports */ + ds->max_num_bridges = FIELD_MAX(RTL8365MB_EFID_MASK); + + ds->configure_vlan_while_not_filtering = true; + + /* Set up VLAN */ + ret = rtl8365mb_vlan_setup(ds); + if (ret) + goto out_teardown_irq; + ret = rtl83xx_setup_user_mdio(ds); if (ret) { dev_err(priv->dev, "could not set up MDIO bus\n"); @@ -2077,8 +2623,8 @@ static int rtl8365mb_detect(struct realtek_priv *priv) ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); if (ret) { - dev_err(priv->dev, "failed to read chip id and version: %d\n", - ret); + dev_err(priv->dev, "failed to read chip id and version: %pe\n", + ERR_PTR(ret)); return ret; } @@ -2123,7 +2669,20 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { .setup = rtl8365mb_setup, .teardown = rtl8365mb_teardown, .phylink_get_caps = rtl8365mb_phylink_get_caps, + .port_bridge_join = rtl83xx_port_bridge_join, + .port_bridge_leave = rtl83xx_port_bridge_leave, + .port_pre_bridge_flags = rtl8365mb_port_pre_bridge_flags, + .port_bridge_flags = rtl83xx_port_bridge_flags, .port_stp_state_set = rtl8365mb_port_stp_state_set, + .port_fast_age = rtl83xx_port_fast_age, + .port_fdb_add = rtl83xx_port_fdb_add, + .port_fdb_del = rtl83xx_port_fdb_del, + .port_fdb_dump = rtl83xx_port_fdb_dump, + .port_mdb_add = rtl83xx_port_mdb_add, + .port_mdb_del = rtl83xx_port_mdb_del, + .port_vlan_add = rtl8365mb_port_vlan_add, + .port_vlan_del = rtl8365mb_port_vlan_del, + .port_vlan_filtering = rtl8365mb_port_vlan_filtering, .get_strings = rtl8365mb_get_strings, .get_ethtool_stats = rtl8365mb_get_ethtool_stats, .get_sset_count = rtl8365mb_get_sset_count, @@ -2139,6 +2698,19 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = { static const struct realtek_ops rtl8365mb_ops = { .detect = rtl8365mb_detect, + .port_add_isolation = rtl8365mb_port_add_isolation, + .port_remove_isolation = rtl8365mb_port_remove_isolation, + .port_set_efid = rtl8365mb_port_set_efid, + .port_set_learning = rtl8365mb_port_set_learning, + .port_set_ucast_flood = rtl8365mb_port_set_ucast_flood, + .port_set_mcast_flood = rtl8365mb_port_set_mcast_flood, + .port_set_bcast_flood = rtl8365mb_port_set_bcast_flood, + .l2_add_uc = rtl8365mb_l2_add_uc, + .l2_del_uc = rtl8365mb_l2_del_uc, + .l2_get_next_uc = rtl8365mb_l2_get_next_uc, + .l2_add_mc = rtl8365mb_l2_add_mc, + .l2_del_mc = rtl8365mb_l2_del_mc, + .l2_flush = rtl8365mb_l2_flush, .phy_read = rtl8365mb_phy_read, .phy_write = rtl8365mb_phy_write, }; diff --git a/drivers/net/dsa/realtek/rtl8365mb_table.c b/drivers/net/dsa/realtek/rtl8365mb_table.c new file mode 100644 index 000000000000..f3c8749a2221 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_table.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Look-up table query interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin Šipraga + */ + +#include "rtl8365mb_table.h" +#include + +/* Table access control register */ +#define RTL8365MB_TABLE_CTRL_REG 0x0500 +/* Should be one of rtl8365mb_table enum members */ +#define RTL8365MB_TABLE_CTRL_TABLE_MASK GENMASK(2, 0) +/* Should be one of rtl8365mb_table_op enum members */ +#define RTL8365MB_TABLE_CTRL_OP_MASK GENMASK(3, 3) +/* Should be one of rtl8365mb_table_l2_method enum members */ +#define RTL8365MB_TABLE_CTRL_METHOD_MASK GENMASK(6, 4) +#define RTL8365MB_TABLE_CTRL_PORT_MASK GENMASK(11, 8) + +/* Table access address register */ +#define RTL8365MB_TABLE_ACCESS_ADDR_REG 0x0501 +#define RTL8365MB_TABLE_ADDR_MASK GENMASK(12, 0) + +/* Table status register */ +#define RTL8365MB_TABLE_STATUS_REG 0x0502 +#define RTL8365MB_TABLE_STATUS_ADDRESS_MASK GENMASK(10, 0) +/* set for L3, unset for L2 */ +#define RTL8365MB_TABLE_STATUS_ADDR_TYPE_MASK GENMASK(11, 11) +#define RTL8365MB_TABLE_STATUS_HIT_STATUS_MASK GENMASK(12, 12) +#define RTL8365MB_TABLE_STATUS_BUSY_FLAG_MASK GENMASK(13, 13) +#define RTL8365MB_TABLE_STATUS_ADDRESS_EXT_MASK GENMASK(14, 14) + +/* Table read/write registers */ +#define RTL8365MB_TABLE_WRITE_BASE 0x0510 +#define RTL8365MB_TABLE_WRITE_REG(_x) \ + (RTL8365MB_TABLE_WRITE_BASE + (_x)) +#define RTL8365MB_TABLE_READ_BASE 0x0520 +#define RTL8365MB_TABLE_READ_REG(_x) \ + (RTL8365MB_TABLE_READ_BASE + (_x)) +#define RTL8365MB_TABLE_10TH_DATA_MASK GENMASK(3, 0) +#define RTL8365MB_TABLE_WRITE_10TH_REG \ + RTL8365MB_TABLE_WRITE_REG(RTL8365MB_TABLE_ENTRY_MAX_SIZE - 1) + +static int rtl8365mb_table_poll_busy(struct realtek_priv *priv) +{ + u32 val; + + return regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_TABLE_STATUS_REG, val, + !FIELD_GET(RTL8365MB_TABLE_STATUS_BUSY_FLAG_MASK, val), + 10, 10000); +} + +int rtl8365mb_table_query(struct realtek_priv *priv, + enum rtl8365mb_table table, + enum rtl8365mb_table_op op, u16 *addr, + enum rtl8365mb_table_l2_method method, + u16 port, u16 *data, size_t size) +{ + bool addr_as_input = true; + bool write_data = false; + int ret = 0; + u32 cmd; + u32 val; + u32 hit; + + /* Prepare target table and operation (read or write) */ + cmd = 0; + cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_TABLE_MASK, table); + cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_OP_MASK, op); + if (op == RTL8365MB_TABLE_OP_READ && table == RTL8365MB_TABLE_L2) { + cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_METHOD_MASK, method); + switch (method) { + case RTL8365MB_TABLE_L2_METHOD_MAC: + /* + * Method MAC requires as input the same L2 table format + * you'll get as result. However, it might only use mac + * address and FID/VID fields. + */ + write_data = true; + + /* METHOD_MAC does not use addr as input, but may return + * the matched index. + */ + addr_as_input = false; + + break; + case RTL8365MB_TABLE_L2_METHOD_ADDR: + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT: + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC: + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC: + break; + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT: + cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_PORT_MASK, port); + break; + default: + return -EINVAL; + } + } else if (op == RTL8365MB_TABLE_OP_WRITE) { + write_data = true; + + /* Writing to L2 does not use addr as input, as the table index + * is derived from key fields. + */ + if (table == RTL8365MB_TABLE_L2) + addr_as_input = false; + } + + /* To prevent concurrent access to the look-up tables, take the regmap + * lock manually and access via the map_nolock regmap. + */ + mutex_lock(&priv->map_lock); + + /* Protect from a busy table access (i.e. previous access timeouts) */ + ret = rtl8365mb_table_poll_busy(priv); + if (ret) + goto out; + + /* Write entry data if writing to the table (or L2_METHOD_MAC) */ + if (write_data) { + /* bulk write data up to 9th word */ + ret = regmap_bulk_write(priv->map_nolock, + RTL8365MB_TABLE_WRITE_BASE, + data, + min_t(size_t, size, + RTL8365MB_TABLE_ENTRY_MAX_SIZE - + 1)); + if (ret) + goto out; + + /* 10th register uses only 4 least significant bits */ + if (size == RTL8365MB_TABLE_ENTRY_MAX_SIZE) { + val = FIELD_PREP(RTL8365MB_TABLE_10TH_DATA_MASK, + data[size - 1]); + ret = regmap_update_bits(priv->map_nolock, + RTL8365MB_TABLE_WRITE_10TH_REG, + RTL8365MB_TABLE_10TH_DATA_MASK, + val); + } + + if (ret) + goto out; + } + + /* Write address (if needed) */ + if (addr_as_input) { + ret = regmap_write(priv->map_nolock, + RTL8365MB_TABLE_ACCESS_ADDR_REG, + FIELD_PREP(RTL8365MB_TABLE_ADDR_MASK, + *addr)); + if (ret) + goto out; + } + + /* Execute */ + ret = regmap_write(priv->map_nolock, RTL8365MB_TABLE_CTRL_REG, cmd); + if (ret) + goto out; + + /* Poll for completion */ + ret = rtl8365mb_table_poll_busy(priv); + if (ret) + goto out; + + /* For both reads and writes to the L2 table, check status */ + if (table == RTL8365MB_TABLE_L2) { + ret = regmap_read(priv->map_nolock, RTL8365MB_TABLE_STATUS_REG, + &val); + if (ret) + goto out; + + /* Did the query find an entry? */ + hit = FIELD_GET(RTL8365MB_TABLE_STATUS_HIT_STATUS_MASK, val); + if (!hit) { + ret = -ENOENT; + goto out; + } + + /* If so, extract the address */ + *addr = 0; + *addr |= FIELD_GET(RTL8365MB_TABLE_STATUS_ADDRESS_MASK, val); + *addr |= FIELD_GET(RTL8365MB_TABLE_STATUS_ADDRESS_EXT_MASK, val) + << 11; + /* only set if it is a L3 address */ + *addr |= FIELD_GET(RTL8365MB_TABLE_STATUS_ADDR_TYPE_MASK, val) + << 12; + } + + /* Finally, get the table entry if we were reading */ + if (op == RTL8365MB_TABLE_OP_READ) { + ret = regmap_bulk_read(priv->map_nolock, + RTL8365MB_TABLE_READ_BASE, + data, size); + if (ret) + goto out; + + /* For the biggest table entries, the uppermost table + * entry register has space for only one nibble. Mask + * out the remainder bits. Empirically I saw nothing + * wrong with omitting this mask, but it may prevent + * unwanted behaviour. FYI. + */ + if (size == RTL8365MB_TABLE_ENTRY_MAX_SIZE) { + val = FIELD_GET(RTL8365MB_TABLE_10TH_DATA_MASK, + data[size - 1]); + data[size - 1] = val; + } + } + +out: + mutex_unlock(&priv->map_lock); + + return ret; +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_table.h b/drivers/net/dsa/realtek/rtl8365mb_table.h new file mode 100644 index 000000000000..41280eb6f8b0 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_table.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Look-up table query interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin Šipraga + */ + +#ifndef _REALTEK_RTL8365MB_TABLE_H +#define _REALTEK_RTL8365MB_TABLE_H + +#include +#include + +#include "realtek.h" + +#define RTL8365MB_TABLE_ENTRY_MAX_SIZE 10 + +/* + * enum rtl8365mb_table - available switch tables + * @RTL8365MB_TABLE_ACL_RULE: ACL rules + * @RTL8365MB_TABLE_ACL_ACTION: ACL actions + * @RTL8365MB_TABLE_CVLAN: VLAN4k configurations + * @RTL8365MB_TABLE_L2: filtering database (2K hash table) + * @RTL8365MB_TABLE_IGMP_GROUP: IGMP group database (readonly) + * + * NOTE: Don't change the enum values. They must concur with the field + * described by @RTL8365MB_TABLE_CTRL_TABLE_MASK. + */ +enum rtl8365mb_table { + RTL8365MB_TABLE_ACL_RULE = 1, + RTL8365MB_TABLE_ACL_ACTION = 2, + RTL8365MB_TABLE_CVLAN = 3, + RTL8365MB_TABLE_L2 = 4, + RTL8365MB_TABLE_IGMP_GROUP = 5, +}; + +/* + * enum rtl8365mb_table_op - table query operation + * @RTL8365MB_TABLE_OP_READ: read an entry from the target table + * @RTL8365MB_TABLE_OP_WRITE: write an entry to the target table + * + * NOTE: Don't change the enum values. They must concur with the field + * described by @RTL8365MB_TABLE_CTRL_OP_MASK. + */ +enum rtl8365mb_table_op { + RTL8365MB_TABLE_OP_READ = 0, + RTL8365MB_TABLE_OP_WRITE = 1, +}; + +/* + * enum rtl8365mb_table_l2_method - look-up method for read queries of L2 table + * @RTL8365MB_TABLE_L2_METHOD_MAC: look-up by source MAC address and FID (or + * VID) + * @RTL8365MB_TABLE_L2_METHOD_ADDR: look-up by entry address + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT: look-up next entry starting from the + * supplied address + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC: same as ADDR_NEXT but search only + * unicast addresses + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC: same as ADDR_NEXT but search only + * multicast addresses + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT: same as ADDR_NEXT_UC but + * search only entries with matching source port + * + * NOTE: Don't change the enum values. They must concur with the field + * described by @RTL8365MB_TABLE_CTRL_METHOD_MASK + */ +enum rtl8365mb_table_l2_method { + RTL8365MB_TABLE_L2_METHOD_MAC = 0, + RTL8365MB_TABLE_L2_METHOD_ADDR = 1, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT = 2, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC = 3, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC = 4, + /* + * RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC_L3 = 5, + * RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC_L2L3 = 6, + */ + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT = 7, +}; + +/* + * rtl8365mb_table_query() - read from or write to a switch table + * @priv: driver context + * @table: target table, see &enum rtl8365mb_table + * @op: read or write operation, see &enum rtl8365mb_table_op + * @addr: table address. For indexed tables, this selects the entry to access. + * For L2 read queries, it is ignored as input for MAC-based lookup + * methods and used as input for address-based lookup methods. On + * successful L2 queries, it is updated with the matched entry address. + * @method: L2 table lookup method, see &enum rtl8365mb_table_l2_method. + * Ignored for non-L2 tables. + * @port: for L2 read queries using method + * %RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, restrict the search + * to entries associated with this source port. Ignored otherwise. + * @data: data buffer used to read from or write to the table. For L2 MAC + * lookups, this buffer provides the lookup key and receives the + * matched entry contents on success. + * @size: size of @data in 16-bit words. The caller must ensure that @size + * matches the target table's entry size and does not exceed + * RTL8365MB_TABLE_ENTRY_MAX_SIZE. + * + * This function provides unified access to the internal tables of the switch. + * All tables except the L2 table are simple indexed tables, where @addr + * selects the entry and @op determines whether the access is a read or a + * write operation. + * + * The content of @data is used as input when writing to tables or when + * specifying the lookup key for L2 MAC searches, and as output for all + * successful read operations. It remains unchanged during write operations or + * failed read operations that return %-ENOENT. For other errors during read + * operations, it is undefined. + * + * The L2 table is a hash table and supports multiple lookup methods. For + * %RTL8365MB_TABLE_L2_METHOD_MAC, an entry is searched based on the MAC + * address and FID/VID fields provided in @data, using the same format as + * an L2 table entry. Address-based methods either read a specific entry + * (%RTL8365MB_TABLE_L2_METHOD_ADDR) or iterate over valid entries starting + * from @addr (%RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT and variants). When using + * %RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, only entries associated with + * the specified @port are considered. + * + * On successful L2 operations, @addr is updated with the matched table address + * or allocated entry address. If no matching entry is found, or if an L2 write + * operation fails (e.g., due to a full table during addition or a missing entry + * during deletion), %-ENOENT is returned and @addr remains unchanged. It is the + * caller's responsibility to map the returned error to the appropriate + * semantic error. + * + * @size must match the size of the target table entry, expressed in 16-bit + * words. + * + * Return: 0 on success, or a negative error code on failure. + */ +int rtl8365mb_table_query(struct realtek_priv *priv, + enum rtl8365mb_table table, + enum rtl8365mb_table_op op, u16 *addr, + enum rtl8365mb_table_l2_method method, + u16 port, u16 *data, size_t size); + +#endif /* _REALTEK_RTL8365MB_TABLE_H */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.c b/drivers/net/dsa/realtek/rtl8365mb_vlan.c new file mode 100644 index 000000000000..64dd8d90a5ee --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.c @@ -0,0 +1,944 @@ +// SPDX-License-Identifier: GPL-2.0 +/* VLAN configuration interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin Šipraga + * + * VLAN configuration takes place in two separate domains of the switch: the + * VLAN4k table and the VLAN membership configuration (MC) database. While the + * VLAN4k table is exhaustive and can be fully populated with 4096 VLAN + * configurations, the same does not hold for the VLAN membership configuration + * database, which is limited to 32 entries. + * + * The switch will normally only use the VLAN4k table when making forwarding + * decisions. The VLAN membership configuration database is a vestigial ASIC + * design and is only used for a few specific features in the rtl8365mb + * family. This means that the limit of 32 entries should not hinder us in + * programming a huge number of VLANs into the switch. + * + * One necessary use of the VLAN membership configuration database is for the + * programming of a port-based VLAN ID (PVID). The PVID is programmed on a + * per-port basis via register field, which refers to a specific VLAN membership + * configuration via an index 0~31. In order to maintain coherent behaviour on a + * port with a PVID, it is necessary to keep the VLAN configuration synchronized + * between the VLAN4k table and the VLAN membership configuration database. + * + * Since VLAN membership configs are a scarce resource, it will only be used + * when strictly needed (i.e. a VLAN with members using PVID). Otherwise, the + * VLAN4k will be enough. + * + * With some exceptions, the entries in both the VLAN4k table and the VLAN + * membership configuration database offer the same configuration options. The + * differences are as follows: + * + * 1. VLAN4k entries can specify whether to use Independent or Shared VLAN + * Learning (IVL or SVL respectively). VLAN membership config entries + * cannot. This underscores the fact that VLAN membership configs are not + * involved in the learning process of the ASIC. + * + * 2. VLAN membership config entries use an "enhanced VLAN ID" (efid), which has + * a range 0~8191 compared with the standard 0~4095 range of the VLAN4k + * table. This underscores the fact that VLAN membership configs can be used + * to group ports on a layer beyond the standard VLAN configuration, which + * may be useful for ACL rules which specify alternative forwarding + * decisions. + * + * VLANMC index 0 is reserved as a neutral PVID, used for standalone ports. + * + */ + +#include "rtl8365mb_vlan.h" +#include "rtl8365mb_table.h" +#include +#include +#include + +/* CVLAN (i.e. VLAN4k) table entry layout, u16[3] */ +#define RTL8365MB_CVLAN_ENTRY_SIZE 3 /* 48-bits */ +#define RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK GENMASK(7, 0) +#define RTL8365MB_CVLAN_MBR_LO_MASK GENMASK(7, 0) +#define RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK GENMASK(15, 8) +#define RTL8365MB_CVLAN_UNTAG_LO_MASK GENMASK(7, 0) +#define RTL8365MB_CVLAN_ENTRY_D1_FID_MASK GENMASK(3, 0) +#define RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK GENMASK(4, 4) +#define RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK GENMASK(7, 5) +#define RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK GENMASK(8, 8) +#define RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK GENMASK(13, 9) +#define RTL8365MB_CVLAN_METERIDX_LO_MASK GENMASK(4, 0) +#define RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK GENMASK(14, 14) +/* extends RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK */ +#define RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK GENMASK(2, 0) +#define RTL8365MB_CVLAN_MBR_HI_MASK GENMASK(10, 8) +/* extends RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK */ +#define RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK GENMASK(5, 3) +#define RTL8365MB_CVLAN_UNTAG_HI_MASK GENMASK(10, 8) +/* extends RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK */ +#define RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK GENMASK(6, 6) +#define RTL8365MB_CVLAN_METERIDX_HI_MASK GENMASK(5, 5) + +/* VLAN member configuration registers 0~31, u16[3] */ +#define RTL8365MB_VLAN_MC_BASE 0x0728 +#define RTL8365MB_VLAN_MC_ENTRY_SIZE 4 /* 64-bit */ +#define RTL8365MB_VLAN_MC_REG(index) \ + (RTL8365MB_VLAN_MC_BASE + \ + (RTL8365MB_VLAN_MC_ENTRY_SIZE * (index))) +#define RTL8365MB_VLAN_MC_D0_MBR_MASK GENMASK(10, 0) +#define RTL8365MB_VLAN_MC_D1_FID_MASK GENMASK(3, 0) + +#define RTL8365MB_VLAN_MC_D2_VBPEN_MASK GENMASK(0, 0) +#define RTL8365MB_VLAN_MC_D2_VBPRI_MASK GENMASK(3, 1) +#define RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK GENMASK(4, 4) +#define RTL8365MB_VLAN_MC_D2_METERIDX_MASK GENMASK(10, 5) +#define RTL8365MB_VLAN_MC_D3_EVID_MASK GENMASK(12, 0) + +/* Some limits for VLAN4k/VLAN membership config entries */ +#define RTL8365MB_PRIORITYMAX 7 +#define RTL8365MB_FIDMAX 15 +#define RTL8365MB_METERMAX 63 +#define RTL8365MB_VLAN_MCMAX 31 + +/* RTL8367S supports 4k vlans (vid<=4095) and 32 enhanced vlans + * for VIDs up to 8191 + */ +#define RTL8365MB_MAX_4K_VID 0x0FFF /* 4095 */ +#define RTL8365MB_MAX_MC_VID 0x1FFF /* 8191 */ + + /* Port-based VID registers 0~5 - each one holds an MC index for two ports */ +#define RTL8365MB_VLAN_PVID_CTRL_BASE 0x0700 +#define RTL8365MB_VLAN_PVID_CTRL_REG(_p) \ + (RTL8365MB_VLAN_PVID_CTRL_BASE + ((_p) >> 1)) +#define RTL8365MB_VLAN_PVID_CTRL_PORT0_MCIDX_MASK 0x001F +#define RTL8365MB_VLAN_PVID_CTRL_PORT1_MCIDX_MASK 0x1F00 +#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p) \ + (((_p) & 1) << 3) +#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \ + (0x1F << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p)) + +/* Frame type filtering registers */ +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE 0x07aa +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \ + (RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE + ((port) >> 3)) +/* required as FIELD_PREP cannot use non-constant masks */ +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port) \ + (0x3 << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port)) +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port) \ + (((port) & 0x7) << 1) + +/* + * struct rtl8365mb_vlan4k - VLAN4k table entry + * @vid: VLAN ID (0~4095) + * @member: port mask of ports in this VLAN + * @untag: port mask of ports which untag on egress + * @fid: filter ID - only used with SVL (unused) + * @priority: priority classification (unused) + * @priority_en: enable priority (unused) + * @policing_en: enable policing (unused) + * @ivl_en: enable IVL instead of default SVL + * @meteridx: metering index (unused) + * + * This structure is used to get/set entries in the VLAN4k table. The + * VLAN4k table dictates the VLAN configuration for the switch for the + * vast majority of features. + */ +struct rtl8365mb_vlan4k { + u16 vid; + u16 member; + u16 untag; + u8 fid : 4; + u8 priority : 3; + u8 priority_en : 1; + u8 policing_en : 1; + u8 ivl_en : 1; + u8 meteridx : 6; +}; + +/* + * struct rtl8365mb_vlanmc - VLAN membership config + * @evid: Enhanced VLAN ID (0~8191) + * @member: port mask of ports in this VLAN + * @fid: filter ID - only used with SVL (unused) + * @priority: priority classification (unused) + * @priority_en: enable priority (unused) + * @policing_en: enable policing (unused) + * @meteridx: metering index (unused) + * + * This structure is used to get/set entries in the VLAN membership + * configuration database. This feature is largely vestigial, but + * still needed for at least the following features: + * - PVID configuration + * - ACL configuration + * - selection of VLAN by the CPU tag when VSEL=1, although the switch + * can also select VLAN based on the VLAN tag if VSEL=0 + * + * This is a low-level structure and it is recommended to interface with + * the VLAN membership config database via &struct rtl8365mb_vlanmc_entry. + */ +struct rtl8365mb_vlanmc { + u16 evid; + u16 member; + u8 fid : 4; + u8 priority : 3; + u8 priority_en : 1; + u8 policing_en : 1; + u8 meteridx : 6; +}; + +static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid, + struct rtl8365mb_vlan4k *vlan4k) +{ + u16 data[RTL8365MB_CVLAN_ENTRY_SIZE]; + int val; + int ret; + + ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN, + RTL8365MB_TABLE_OP_READ, &vid, 0, 0, + data, ARRAY_SIZE(data)); + if (ret) + return ret; + + /* Unpack table entry */ + memset(vlan4k, 0, sizeof(*vlan4k)); + vlan4k->vid = vid; + + val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, data[0]); + vlan4k->member = FIELD_PREP(RTL8365MB_CVLAN_MBR_LO_MASK, val); + val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]); + vlan4k->member |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val); + + val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, data[0]); + vlan4k->untag = FIELD_PREP(RTL8365MB_CVLAN_UNTAG_LO_MASK, val); + val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]); + vlan4k->untag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val); + + vlan4k->fid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]); + vlan4k->priority_en = + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]); + vlan4k->priority = + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]); + vlan4k->policing_en = + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]); + + val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]); + val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val); + vlan4k->meteridx = val; + val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]); + val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val); + vlan4k->meteridx |= val; + + vlan4k->ivl_en = + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]); + + return 0; +} + +static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv, + const struct rtl8365mb_vlan4k *vlan4k) +{ + u16 data[RTL8365MB_CVLAN_ENTRY_SIZE] = { 0 }; + u16 vid; + int val; + + /* Pack table entry value */ + val = FIELD_GET(RTL8365MB_CVLAN_MBR_LO_MASK, vlan4k->member); + data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, val); + + val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_LO_MASK, vlan4k->untag); + data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, val); + + data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, vlan4k->fid); + data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, + vlan4k->priority_en); + data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, + vlan4k->priority); + data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, + vlan4k->policing_en); + + /* FIELD_* does not play nice with struct bitfield. */ + val = vlan4k->meteridx; + val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val); + data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val); + + data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, + vlan4k->ivl_en); + + val = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k->member); + data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val); + + val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k->untag); + data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val); + + val = vlan4k->meteridx; + val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val); + data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val); + + vid = vlan4k->vid; + return rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN, + RTL8365MB_TABLE_OP_WRITE, &vid, 0, 0, + data, ARRAY_SIZE(data)); +} + +static int +rtl8365mb_vlan_4k_port_set(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack, + bool include) +{ + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_vlan4k vlan4k = {0}; + int ret; + + dev_dbg(priv->dev, "%s VLAN %d 4K on port %d\n", + include ? "add" : "del", + vlan->vid, port); + + if (vlan->vid > RTL8365MB_MAX_4K_VID) { + NL_SET_ERR_MSG_MOD(extack, "VLAN ID greater than " + __stringify(RTL8365MB_MAX_4K_VID)); + return -EINVAL; + } + + ret = rtl8365mb_vlan_4k_read(priv, vlan->vid, &vlan4k); + if (ret) { + dev_err(priv->dev, "Failed to read VLAN 4k table\n"); + return ret; + } + + if (include) + vlan4k.member |= BIT(port); + else + vlan4k.member &= ~BIT(port); + + if (include && (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) + vlan4k.untag |= BIT(port); + else + vlan4k.untag &= ~BIT(port); + vlan4k.ivl_en = true; /* always use Independent VLAN Learning */ + + ret = rtl8365mb_vlan_4k_write(priv, &vlan4k); + if (ret) { + dev_err(priv->dev, "Failed to write VLAN 4k table\n"); + return ret; + } + + return 0; +} + +/* + * rtl8365mb_vlan_4k_port_add() - Add a port to a VLAN 4K table entry + * @ds: dsa switch instance + * @port: port index + * @vlan: switchdev VLAN object containing the target VID and flags + * @extack: netlink extended ACK for error reporting + * + * Adds the specified port to the hardware VLAN 4K membership table. + * + * Context: Can sleep. Must be called with &priv->vlan_lock held. + * Takes and releases &priv->map_lock. + * Return: 0 on success, or a negative error code on failure. + */ +int rtl8365mb_vlan_4k_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv = ds->priv; + + lockdep_assert_held(&priv->vlan_lock); + + return rtl8365mb_vlan_4k_port_set(ds, port, vlan, extack, true); +} + +/* + * rtl8365mb_vlan_4k_port_del() - Remove a port from a VLAN 4K table entry + * @ds: dsa switch instance + * @port: port index + * @vlan: switchdev VLAN object containing the target VID + * + * Removes the specified port from the hardware VLAN 4K membership table. + * + * Context: Can sleep. Must be called with &priv->vlan_lock held. + * Takes and releases &priv->map_lock. + * Return: 0 on success, or a negative error code on failure. + */ +int rtl8365mb_vlan_4k_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct realtek_priv *priv = ds->priv; + + lockdep_assert_held(&priv->vlan_lock); + + return rtl8365mb_vlan_4k_port_set(ds, port, vlan, NULL, false); +} + +static int rtl8365mb_vlan_mc_read(struct realtek_priv *priv, u32 index, + struct rtl8365mb_vlanmc *vlanmc) +{ + u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE]; + int ret; + + ret = regmap_bulk_read(priv->map, RTL8365MB_VLAN_MC_REG(index), &data, + RTL8365MB_VLAN_MC_ENTRY_SIZE); + if (ret) + return ret; + + vlanmc->member = FIELD_GET(RTL8365MB_VLAN_MC_D0_MBR_MASK, data[0]); + vlanmc->fid = FIELD_GET(RTL8365MB_VLAN_MC_D1_FID_MASK, data[1]); + vlanmc->meteridx = FIELD_GET(RTL8365MB_VLAN_MC_D2_METERIDX_MASK, + data[2]); + vlanmc->policing_en = FIELD_GET(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK, + data[2]); + vlanmc->priority = FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, data[2]); + vlanmc->priority_en = FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPEN_MASK, + data[2]); + vlanmc->evid = FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, data[3]); + + return 0; +} + +static int rtl8365mb_vlan_mc_write(struct realtek_priv *priv, u32 index, + const struct rtl8365mb_vlanmc *vlanmc) +{ + u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE] = { 0 }; + int ret; + + data[0] |= FIELD_PREP(RTL8365MB_VLAN_MC_D0_MBR_MASK, vlanmc->member); + data[1] |= FIELD_PREP(RTL8365MB_VLAN_MC_D1_FID_MASK, vlanmc->fid); + data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_METERIDX_MASK, + vlanmc->meteridx); + data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK, + vlanmc->policing_en); + data[2] |= + FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, vlanmc->priority); + data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPEN_MASK, + vlanmc->priority_en); + data[3] |= FIELD_PREP(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlanmc->evid); + + ret = regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data, + RTL8365MB_VLAN_MC_ENTRY_SIZE); + + return ret; +} + +static int rtl8365mb_vlan_mc_erase(struct realtek_priv *priv, u32 index) +{ + u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE] = { 0 }; + int ret; + + ret = regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data, + RTL8365MB_VLAN_MC_ENTRY_SIZE); + + return ret; +} + +/* + * rtl8365mb_vlan_mc_find() - find VLANMC index by VID or the first free index + * + * @priv: realtek_priv pointer + * @vid: VLAN ID + * @index: found index + * @first_free: found free index + * + * If a VLAN MC entry using @vid was found, @index will return the matched index + * and @first_free is undefined. If not found, @index will return 0 and + * @first_free will return the first found free index in VLAN MC or 0 if the + * table is full. + * + * Although 0 is a valid VLAN MC index, it is reserved for ports without PVID, + * including standalone, non-member ports. It uses VID == 0. + * + * Both @index and @first_free will be in the * 1..@RTL8365MB_VLAN_MCMAX range. + * + * Return: Returns 0 on success, a negative error on failure. + */ +static int rtl8365mb_vlan_mc_find(struct realtek_priv *priv, u16 vid, + u8 *index, u8 *first_free) +{ + u32 vlan_entry_d3; + u8 vlanmc_idx; + u16 evid; + int ret; + + *index = 0; + *first_free = 0; + + /* look for existing entry or an empty one */ + /* By design, VlanMC[0] is reserved as a neutral PVID value for + * standalone ports. It always has EVID == 0. That way, we assume that + * all entries after index 0 with VID == 0 are empty. + **/ + for (vlanmc_idx = 1; vlanmc_idx <= RTL8365MB_VLAN_MCMAX; vlanmc_idx++) { + /* just read the 4th word, where the evid is */ + ret = regmap_read(priv->map, + RTL8365MB_VLAN_MC_REG(vlanmc_idx) + 3, + &vlan_entry_d3); + if (ret) + return ret; + + evid = FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlan_entry_d3); + + if (evid == vid) { + *index = vlanmc_idx; + return 0; + } + + if (evid == 0x0 && *first_free < 1) + *first_free = vlanmc_idx; + } + return 0; +} + +static int rtl8365mb_vlan_port_get_pvid_idx(struct realtek_priv *priv, + int port, u8 *vlanmc_idx) +{ + u32 data; + int ret; + + ret = regmap_read(priv->map, RTL8365MB_VLAN_PVID_CTRL_REG(port), &data); + if (ret) + return ret; + + *vlanmc_idx = (data & RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port)) + >> RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port); + + return 0; +} + +/* + * rtl8365mb_vlan_mc_port_set() - include or exclude a port from VlanMC + * @ds: dsa switch + * @port: the port number + * @vid: the vlan VID to include/exclude @port + * @pvid: inform if vid is used as pvid in @port + * @extack: optional extack to return errors + * @include: whether to include or exclude @port + * + * This function is used to include/exclude ports to the VlanMC table. + * + * VlanMC stands for VLAN membership config and it is used exclusively for + * PVID. If @vlan members are not using PVID, this function will either + * remove or not create a new VlanMC entry. + * + * VlanMC members are used as a reference port map, cleaning the entry once + * no port is using it. + * + * Port PVID and accepted frame type are updated as well. + * + * Context: Can sleep. Must be called with &priv->vlan_lock held. + * Takes and releases &priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ +static +int rtl8365mb_vlan_mc_port_set(struct dsa_switch *ds, int port, + u16 vid, bool pvid, + struct netlink_ext_ack *extack, + bool include) +{ + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_vlanmc vlanmc = {0}; + u8 first_unused = 0; + u8 vlanmc_idx = 0; + int ret; + + dev_dbg(priv->dev, "%s VLAN %d MC on port %d\n", + include ? "add" : "del", + vid, port); + + if (vid > RTL8365MB_MAX_MC_VID) { + NL_SET_ERR_MSG_MOD(extack, "VLAN ID greater than " + __stringify(RTL8365MB_MAX_MC_VID)); + return -EINVAL; + } + + /* look for existing entry or an empty slot */ + ret = rtl8365mb_vlan_mc_find(priv, vid, &vlanmc_idx, + &first_unused); + if (ret) { + dev_err(priv->dev, "Failed to find a VLAN MC table index\n"); + return ret; + } + + if (vlanmc_idx) { + ret = rtl8365mb_vlan_mc_read(priv, vlanmc_idx, &vlanmc); + if (ret) { + dev_err(priv->dev, "Failed to read VLAN MC table\n"); + return ret; + } + } else if (include) { + /* for now, vlan_mc is only required for PVID. Defer allocation + * until at least one port uses PVID. + */ + if (!pvid) { + dev_dbg(priv->dev, + "Not creating VlanMC for vlan %d until a port uses PVID (%d does not)\n", + vid, port); + return 0; + } + + if (!first_unused) { + NL_SET_ERR_MSG_MOD(extack, "All VLAN MC entries (0.." + __stringify(RTL8365MB_VLAN_MCMAX) + ") are in use."); + return -ENOSPC; + } + + vlanmc_idx = first_unused; + vlanmc.evid = vid; + + } else /* excluding and VLANMC not found */ { + return 0; + } + + dev_dbg(priv->dev, + "VLAN %d (idx: %d) PVID curr members: %08x\n", + vid, vlanmc_idx, vlanmc.member); + + /* here we either have an existing VLANMC (with PVID members) or the + * added port is using this VLAN as PVID + */ + if (include) + vlanmc.member |= BIT(port); + else + vlanmc.member &= ~BIT(port); + + /* just like we don't need to create a VLAN_MC when there is no port + * using it as PVID, we can erase it when there is no more port using + * it as PVID. + */ + if (!vlanmc.member) { + dev_dbg(priv->dev, + "Clearing VlanMC index %d previously used by VID %d\n", + vlanmc_idx, vid); + ret = rtl8365mb_vlan_mc_erase(priv, vlanmc_idx); + } else { + dev_dbg(priv->dev, + "Saving VlanMC index %d with VID %d\n", + vlanmc_idx, vid); + ret = rtl8365mb_vlan_mc_write(priv, vlanmc_idx, &vlanmc); + } + if (ret) { + dev_err(priv->dev, "Failed to write vlan MC entry\n"); + return ret; + } + + return 0; +} + +static int rtl8365mb_vlan_port_set_pvid(struct realtek_priv *priv, + int port, u16 vlanmc_idx) +{ + int ret; + u32 val; + + dev_dbg(priv->dev, "set PVID IDX %d on port %d\n", vlanmc_idx, port); + + val = vlanmc_idx << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port); + ret = regmap_update_bits(priv->map, + RTL8365MB_VLAN_PVID_CTRL_REG(port), + RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port), + val); + if (ret) + return ret; + + return 0; +} + +static int rtl8365mb_vlan_get_pvid_mc(struct realtek_priv *priv, + int port, u8 *vlanmc_idx, + struct rtl8365mb_vlanmc *vlanmc) +{ + int ret; + + ret = rtl8365mb_vlan_port_get_pvid_idx(priv, port, vlanmc_idx); + if (ret) + return ret; + + memset(vlanmc, 0, sizeof(*vlanmc)); + + if (!*vlanmc_idx) + return 0; + + ret = rtl8365mb_vlan_mc_read(priv, *vlanmc_idx, vlanmc); + if (ret) + return ret; + + return 0; +} + +/* + * rtl8365mb_vlan_port_get_pvid - Retrieve the port PVID + * @priv: realtek switch private structure + * @port: port index + * @pvid: pointer to store the retrieved VLAN ID + * + * Returns the port PVID if defined or 0 if not. + * + * Context: Can sleep. Takes and releases &priv->map_lock. + * Return: 0 on success or a negative error code on failure. + */ +int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, int port, u16 *pvid) +{ + struct rtl8365mb_vlanmc vlanmc; + u8 vlanmc_idx; + int ret; + + ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx, &vlanmc); + if (ret) + return ret; + + *pvid = vlanmc.evid; + return 0; +} + +/* + * rtl8365mb_vlan_port_get_framefilter() - Get the ingress frame filtering mode + * for a port + * @priv: realtek switch private structure + * @port: port index + * @frame_type: pointer to store the retrieved ingress frame filter type + * + * Context: Can sleep. Takes and releases &priv->map_lock. + * Return: 0 on success, or a negative error code on failure. + */ +int +rtl8365mb_vlan_port_get_framefilter(struct realtek_priv *priv, + int port, + enum rtl8365mb_frame_ingress *frame_type) +{ + u32 val; + int ret; + + /* Even if ACCEPT_FRAME_TYPE_ANY, the switch will still check if the + * port is a member of vlan PVID + */ + + ret = regmap_read(priv->map, RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port), + &val); + if (ret) + return ret; + + *frame_type = field_get(RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port), + val); + + return 0; +} + +/* + * rtl8365mb_vlan_port_set_framefilter() - Set the ingress frame filtering mode + * for a port + * @priv: realtek switch private structure + * @port: port index + * @frame_type: the ingress frame filter type to configure + * + * Context: Can sleep. Takes and releases &priv->map_lock. + * Return: 0 on success, or a negative error code on failure. + */ +int +rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv, + int port, + enum rtl8365mb_frame_ingress frame_type) +{ + u32 val; + + /* Even if ACCEPT_FRAME_TYPE_ANY, the switch will still check if the + * port is a member of vlan PVID + */ + val = frame_type << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port); + + return regmap_update_bits(priv->map, + RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port), + RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port), + val); +} + +/* + * rtl8365mb_vlan_pvid_port_set() - Configure a port's PVID and associated + * VLANMC entry + * @ds: dsa switch instance + * @port: port index + * @vid: target VID + * @extack: netlink extended ACK for error reporting + * + * Allocates or reuses a hardware VLANMC entry to map the given port to its new + * PVID. Gracefully unwinds and restores previous configuration if a hardware + * write operation fails during execution. + * + * Context: Can sleep. Must be called with &priv->vlan_lock held. + * Takes and releases &priv->map_lock. + * Return: 0 on success, or a negative error code on failure. + */ +int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid, + struct netlink_ext_ack *extack) +{ + enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame; + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_vlanmc prev_vlanmc = {0}; + u8 _unused_first_free_idx; + u8 prev_vlanmc_idx; + u8 vlanmc_idx; + int ret; + + lockdep_assert_held(&priv->vlan_lock); + + /* Read the old PVID exclusively to undo in case of error */ + ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &prev_vlanmc_idx, + &prev_vlanmc); + if (ret) { + dev_err(priv->dev, "Failed to read current VLAN MC\n"); + return ret; + } + + ret = rtl8365mb_vlan_port_get_framefilter(priv, port, + &prev_accepted_frame); + if (ret) { + dev_err(priv->dev, "Failed to get current framefilter\n"); + return ret; + } + + /* Find or allocate a new vlan MC and add port to members, + * although members are not checked by the HW in vlan MC. + */ + ret = rtl8365mb_vlan_mc_port_set(ds, port, vid, true, extack, true); + if (ret) + return ret; + + /* look for existing entry */ + ret = rtl8365mb_vlan_mc_find(priv, vid, &vlanmc_idx, + &_unused_first_free_idx); + if (ret) { + dev_err(priv->dev, "Failed to find a VLAN MC table index\n"); + goto undo_vlan_mc_port_set; + } + + if (!vlanmc_idx) { + dev_err(priv->dev, "VLAN should already exist in VLAN MC\n"); + ret = -ENOENT; + goto undo_vlan_mc_port_set; + } + + ret = rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx); + if (ret) { + dev_err(priv->dev, "Failed to set port PVID\n"); + goto undo_vlan_mc_port_set; + } + + /* Changing accept frame is what enables PVID (if not enabled before) */ + accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME; + ret = rtl8365mb_vlan_port_set_framefilter(priv, port, accepted_frame); + if (ret) { + dev_err(priv->dev, "Failed to set port frame filter\n"); + goto undo_vlan_port_set_pvid; + } + + /* A VLAN can be added with PVID without removing from the old + * PVID VLAN. Clear PVID from the old VLAN MC (if needed). + */ + if (prev_vlanmc_idx && (prev_vlanmc.evid != vid)) { + ret = rtl8365mb_vlan_mc_port_set(ds, port, prev_vlanmc.evid, + false, NULL, false); + if (ret) { + dev_err(priv->dev, "Failed to clear old VLAN MC\n"); + goto undo_set_framefilter; + } + } + + return 0; + +undo_set_framefilter: + (void)rtl8365mb_vlan_port_set_framefilter(priv, port, + prev_accepted_frame); + +undo_vlan_port_set_pvid: + (void)rtl8365mb_vlan_port_set_pvid(priv, port, prev_vlanmc_idx); + +undo_vlan_mc_port_set: + if (prev_vlanmc.evid != vid) + (void)rtl8365mb_vlan_mc_port_set(ds, port, vid, false, NULL, + false); + + return ret; +} + +/* + * rtl8365mb_vlan_pvid_port_clear() - Remove a port's PVID configuration + * @ds: dsa switch instance + * @port: port index + * @vid: VLAN VID for PVID + * + * Resets the target port's hardware PVID allocation to 0. Cleans up and frees + * the associated VLANMC entry if no other ports are referencing it. + * + * Context: Can sleep. Must be called with &priv->vlan_lock held. + * Takes and releases &priv->map_lock. + * Return: 0 on success, or a negative error code on failure. + */ +int rtl8365mb_vlan_pvid_port_clear(struct dsa_switch *ds, int port, u16 vid) +{ + enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame; + struct realtek_priv *priv = ds->priv; + struct rtl8365mb_vlanmc vlanmc = {0}; + u8 vlanmc_idx; + int ret; + + lockdep_assert_held(&priv->vlan_lock); + + ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx, + &vlanmc); + if (ret) { + dev_err(priv->dev, "Failed to read current VLAN MC\n"); + return ret; + } + + /* Port is not using PVID. Nothing to remove. */ + if (!vlanmc_idx) + return 0; + + /* We are leaving a non PVID vlan, Nothing to remove. */ + if (vlanmc.evid != vid) + return 0; + + ret = rtl8365mb_vlan_port_get_framefilter(priv, port, + &prev_accepted_frame); + if (ret) { + dev_err(priv->dev, "Failed to get current framefilter\n"); + return ret; + } + + /* Changing accept frame is what really removes PVID. But only do + * that if we are filtering vlan + */ + if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { + accepted_frame = RTL8365MB_FRAME_TYPE_TAGGED_ONLY; + + ret = rtl8365mb_vlan_port_set_framefilter(priv, port, + accepted_frame); + if (ret) { + dev_err(priv->dev, "Failed to set port frame filter\n"); + return ret; + } + } else { + /* skip undo_set_framefilter */ + accepted_frame = prev_accepted_frame; + } + + ret = rtl8365mb_vlan_port_set_pvid(priv, port, 0); + if (ret) { + dev_err(priv->dev, "Failed to set port PVID to 0\n"); + goto undo_set_framefilter; + } + + /* Clears the VLAN MC membership and maybe VLAN MC entry if empty */ + ret = rtl8365mb_vlan_mc_port_set(ds, port, vlanmc.evid, + false, NULL, false); + if (ret) + goto undo_port_set_pvid; + + return 0; + +undo_port_set_pvid: + (void)rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx); + +undo_set_framefilter: + if (prev_accepted_frame != accepted_frame) + (void)rtl8365mb_vlan_port_set_framefilter(priv, port, + prev_accepted_frame); + + return ret; +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.h b/drivers/net/dsa/realtek/rtl8365mb_vlan.h new file mode 100644 index 000000000000..27a526b61873 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* VLAN configuration interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin Šipraga + * + */ + +#ifndef _REALTEK_RTL8365MB_VLAN_H +#define _REALTEK_RTL8365MB_VLAN_H + +#include + +#include "realtek.h" + +enum rtl8365mb_frame_ingress { + RTL8365MB_FRAME_TYPE_ANY_FRAME = 0, + RTL8365MB_FRAME_TYPE_TAGGED_ONLY, + RTL8365MB_FRAME_TYPE_UNTAGGED_ONLY, +}; + +int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, int port, + u16 *pvid); +int +rtl8365mb_vlan_port_get_framefilter(struct realtek_priv *priv, + int port, + enum rtl8365mb_frame_ingress *frame_type); +int +rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv, + int port, + enum rtl8365mb_frame_ingress frame_type); +int rtl8365mb_vlan_4k_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int rtl8365mb_vlan_4k_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); +int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid, + struct netlink_ext_ack *extack); +int rtl8365mb_vlan_pvid_port_clear(struct dsa_switch *ds, int port, u16 vid); +#endif /* _REALTEK_RTL8365MB_VLAN_H */ diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index d96ae72b0a5c..103039fe3086 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -573,8 +573,7 @@ static int rtl8366rb_set_addr(struct realtek_priv *priv) eth_random_addr(addr); - 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]); + dev_info(priv->dev, "set MAC: %pM\n", addr); val = addr[0] << 8 | addr[1]; ret = regmap_write(priv->map, RTL8366RB_SMAR0, val); if (ret) diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index 2b9bd4462714..71124ecca92f 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "realtek.h" #include "rtl83xx.h" @@ -155,6 +157,8 @@ rtl83xx_probe(struct device *dev, return ERR_PTR(-ENOMEM); mutex_init(&priv->map_lock); + mutex_init(&priv->vlan_lock); + mutex_init(&priv->l2_lock); rc.lock_arg = priv; priv->map = devm_regmap_init(dev, NULL, priv, &rc); @@ -325,6 +329,565 @@ void rtl83xx_reset_deassert(struct realtek_priv *priv) gpiod_set_value(priv->reset, false); } +/** + * rtl83xx_port_bridge_join() - join a port to a bridge + * @ds: DSA switch instance + * @port: port index + * @bridge: bridge being joined + * @tx_forward_offload: if the switch can offload TX forwarding + * @extack: netlink extended ack for reporting errors + * + * This function handles joining a port to a bridge. It updates the port + * isolation masks and EFID. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_forward_offload, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv = ds->priv; + struct dsa_port *dp; + u32 mask = 0; + int ret; + + if (!priv->ops->port_add_isolation) + return -EOPNOTSUPP; + + if (!priv->ops->port_set_learning) + return -EOPNOTSUPP; + + dev_dbg(priv->dev, "bridge %d join port %d\n", bridge.num, port); + + /* Add this port to the isolation group of every other port + * offloading this bridge. + */ + dsa_switch_for_each_user_port(dp, ds) { + /* Handle this port after */ + if (dp->index == port) + continue; + + /* Skip ports that are not in this bridge */ + if (!dsa_port_offloads_bridge(dp, &bridge)) + continue; + + ret = priv->ops->port_add_isolation(priv, dp->index, BIT(port)); + if (ret) + goto undo_isolation; + + mask |= BIT(dp->index); + } + + /* If we support cascade switches, it should also include the + * downstream DSA ports to the isolation group. + */ + + /* Add those ports to the isolation group of this port */ + ret = priv->ops->port_add_isolation(priv, port, mask); + if (ret) + goto undo_isolation; + + /* Use the bridge number as the EFID for this port */ + if (priv->ops->port_set_efid) { + ret = priv->ops->port_set_efid(priv, port, bridge.num); + if (ret) + goto undo_self_isolation; + } + + ret = priv->ops->port_set_learning(priv, port, true); + if (ret) + goto undo_efid; + + return 0; + +undo_efid: + if (priv->ops->port_set_efid) + priv->ops->port_set_efid(priv, port, 0); + +undo_self_isolation: + priv->ops->port_remove_isolation(priv, port, mask); + +undo_isolation: + dsa_switch_for_each_port(dp, ds) { + if (mask & BIT(dp->index)) + priv->ops->port_remove_isolation(priv, dp->index, + BIT(port)); + } + + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_bridge_join, "REALTEK_DSA"); + +/** + * rtl83xx_port_bridge_leave() - leave a bridge + * @ds: DSA switch instance + * @port: port index + * @bridge: bridge being left + * + * This function handles removing a port from a bridge. It updates the port + * isolation masks and EFID. + * + * Context: Can sleep. + * Return: nothing + */ +void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct realtek_priv *priv = ds->priv; + struct dsa_port *dp; + u32 mask = 0; + int ret; + + if (!priv->ops->port_remove_isolation) + return; + + if (!priv->ops->port_set_learning) + return; + + dev_dbg(priv->dev, "bridge %d leave port %d\n", bridge.num, port); + + /* Remove this port from the isolation group of every other + * port offloading this bridge. + */ + dsa_switch_for_each_user_port(dp, ds) { + /* Handle this port after */ + if (dp->index == port) + continue; + + /* Skip ports that are not in this bridge */ + if (!dsa_port_offloads_bridge(dp, &bridge)) + continue; + + ret = priv->ops->port_remove_isolation(priv, dp->index, + BIT(port)); + if (ret) + dev_err(priv->dev, + "failed to isolate port %d from port %d: %pe\n", + port, dp->index, ERR_PTR(ret)); + + mask |= BIT(dp->index); + } + + /* If we support cascade switches, it should also exclude the + * downstream DSA ports from the isolation group. + */ + + ret = priv->ops->port_set_learning(priv, port, false); + if (ret) + dev_err(priv->dev, + "failed to disable learning on port %d: %pe\n", + port, ERR_PTR(ret)); + + /* Remove those ports from the isolation group of this port */ + ret = priv->ops->port_remove_isolation(priv, port, mask); + if (ret) + dev_err(priv->dev, + "failed to remove isolation mask from port %d: %pe\n", + port, ERR_PTR(ret)); + + /* Revert to the default EFID 0 for standalone mode */ + if (priv->ops->port_set_efid) { + ret = priv->ops->port_set_efid(priv, port, 0); + if (ret) + dev_err(priv->dev, + "failed to clear EFID on port %d: %pe\n", + port, ERR_PTR(ret)); + } +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_bridge_leave, "REALTEK_DSA"); + +/** + * rtl83xx_port_fast_age() - flush dynamic FDB entries learned on a port + * @ds: DSA switch instance + * @port: port index + * + * This function requests the switch to age out dynamic FDB entries learned on + * @port. + * + * Context: Can sleep. + * Return: Nothing. + */ +void rtl83xx_port_fast_age(struct dsa_switch *ds, int port) +{ + struct realtek_priv *priv = ds->priv; + int ret; + + if (!priv->ops->l2_flush) { + dev_warn_once(priv->dev, "l2_flush op not defined\n"); + return; + } + + dev_dbg(priv->dev, "fast_age port %d\n", port); + + mutex_lock(&priv->l2_lock); + ret = priv->ops->l2_flush(priv, port, 0); + mutex_unlock(&priv->l2_lock); + if (ret) + dev_err(priv->dev, "failed to fast age on port %d: %d\n", port, + ret); +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_fast_age, "REALTEK_DSA"); + +/** + * rtl83xx_port_fdb_add() - add a static FDB entry to a port database + * @ds: DSA switch instance + * @port: port index + * @addr: MAC address to add + * @vid: VLAN ID associated with @addr + * @db: database where the entry should be added + * + * This function adds a static unicast FDB entry to the standalone port + * database or to a bridge database. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct realtek_priv *priv = ds->priv; + int efid; + int ret; + + if (is_multicast_ether_addr(addr)) + return -EOPNOTSUPP; + + if (!priv->ops->l2_add_uc) + return -EOPNOTSUPP; + + if (db.type != DSA_DB_PORT && db.type != DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + /* Bridge ports use bridge.num as EFID, while standalone ports use + * EFID 0. FDB entries for the CPU port follow the bridge EFID due + * to assisted learning. + */ + efid = db.type == DSA_DB_BRIDGE ? db.bridge.num : 0; + + dev_dbg(priv->dev, "%s: port:%d addr:%pM efid:%d vid:%d dbtype:%d\n", + __func__, port, addr, efid, vid, db.type); + + mutex_lock(&priv->l2_lock); + ret = priv->ops->l2_add_uc(priv, port, addr, efid, vid); + + mutex_unlock(&priv->l2_lock); + + if (ret) + dev_err(priv->dev, "fdb_add ERROR %pe\n", ERR_PTR(ret)); + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_fdb_add, "REALTEK_DSA"); + +/** + * rtl83xx_port_fdb_del() - delete a static FDB entry from a port database + * @ds: DSA switch instance + * @port: port index + * @addr: MAC address to delete + * @vid: VLAN ID associated with @addr + * @db: database where the entry should be removed + * + * This function deletes a static unicast FDB entry from the standalone port + * database or from a bridge database. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct realtek_priv *priv = ds->priv; + int efid; + int ret; + + if (is_multicast_ether_addr(addr)) + return -EOPNOTSUPP; + + if (!priv->ops->l2_del_uc) + return -EOPNOTSUPP; + + if (db.type != DSA_DB_PORT && db.type != DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + /* + * DSA_DB_BRIDGE ports use bridge number [1..N] as EFID, while + * DSA_DB_PORT use the default EFID (0), not used by any bridge. + */ + efid = db.type == DSA_DB_BRIDGE ? db.bridge.num : 0; + + dev_dbg(priv->dev, "%s: port:%d addr:%pM efid:%d vid:%d dbtype:%d\n", + __func__, port, addr, efid, vid, db.type); + + mutex_lock(&priv->l2_lock); + ret = priv->ops->l2_del_uc(priv, port, addr, efid, vid); + mutex_unlock(&priv->l2_lock); + + if (ret) + dev_err(priv->dev, "fdb_del ERROR %pe\n", ERR_PTR(ret)); + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_fdb_del, "REALTEK_DSA"); + +/** + * rtl83xx_port_fdb_dump() - iterate over FDB entries associated with a port + * @ds: DSA switch instance + * @port: port index + * @cb: callback invoked for each entry + * @data: opaque pointer passed to @cb + * + * This function walks the unicast FDB entries associated with @port and calls + * @cb for each matching entry. + * + * Context: Can sleep. + * Return: 0 on success, or negative value for failure. + */ +int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct realtek_fdb_entry entry = { 0 }; + struct realtek_priv *priv = ds->priv; + u16 start_addr, addr = 0; + int ret = 0; + + if (!priv->ops->l2_get_next_uc) + return -EOPNOTSUPP; + + mutex_lock(&priv->l2_lock); + while (true) { + start_addr = addr; + + dev_dbg(priv->dev, "l2_get_next_uc, addr:%d, port:%d\n", + addr, port); + ret = priv->ops->l2_get_next_uc(priv, &addr, port, &entry); + dev_dbg(priv->dev, + "%s addr:%d mac:%pM vid:%d static:%d ret:%pe\n", + __func__, addr, entry.mac_addr, entry.vid, + entry.is_static, ERR_PTR(ret)); + + if (ret == -ENOENT) { + /* If the table is empty, returns without errors. Note + * that the l2_get_next_uc overflow to the first match + * when it reaches the end of the table. + */ + ret = 0; + break; + } + + if (ret) + break; + + /* When the addr returned is before the requested one, it + * indicates that we reached the end. + */ + if (addr < start_addr) + break; + + ret = cb(entry.mac_addr, entry.vid, entry.is_static, data); + if (ret) + break; + + addr++; + } + mutex_unlock(&priv->l2_lock); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_fdb_dump, "REALTEK_DSA"); + +/** + * rtl83xx_port_mdb_add() - add a multicast database entry to a port database + * @ds: DSA switch instance + * @port: port index + * @mdb: multicast database entry to add + * @db: database where the entry should be added + * + * This function adds a multicast database entry to the standalone port + * database or to a bridge database. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct realtek_priv *priv = ds->priv; + const unsigned char *addr = mdb->addr; + u16 vid = mdb->vid; + int efid; + int ret; + + if (!priv->ops->l2_add_mc) + return -EOPNOTSUPP; + + if (db.type != DSA_DB_PORT && db.type != DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + /* EFID is not used by hardware MDB entries; debugging only */ + efid = db.type == DSA_DB_BRIDGE ? db.bridge.num : 0; + + dev_dbg(priv->dev, "%s: port:%d addr:%pM efid:%d vid:%d dbtype:%d\n", + __func__, port, addr, efid, vid, db.type); + + mutex_lock(&priv->l2_lock); + ret = priv->ops->l2_add_mc(priv, port, addr, vid); + mutex_unlock(&priv->l2_lock); + + if (ret) + dev_err(priv->dev, "mdb_add ERROR %pe\n", ERR_PTR(ret)); + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_mdb_add, "REALTEK_DSA"); + +/** + * rtl83xx_port_mdb_del() - delete a multicast database entry from a port + * database + * @ds: DSA switch instance + * @port: port index + * @mdb: multicast database entry to delete + * @db: database where the entry should be removed + * + * This function deletes a multicast database entry from the standalone port + * database or from a bridge database. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct realtek_priv *priv = ds->priv; + const unsigned char *addr = mdb->addr; + u16 vid = mdb->vid; + int efid; + int ret; + + if (!priv->ops->l2_del_mc) + return -EOPNOTSUPP; + + if (db.type != DSA_DB_PORT && db.type != DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + /* EFID is not used by hardware MDB entries; debugging only */ + efid = db.type == DSA_DB_BRIDGE ? db.bridge.num : 0; + + dev_dbg(priv->dev, "%s: port:%d addr:%pM efid:%d vid:%d dbtype:%d\n", + __func__, port, addr, efid, vid, db.type); + + mutex_lock(&priv->l2_lock); + ret = priv->ops->l2_del_mc(priv, port, addr, vid); + mutex_unlock(&priv->l2_lock); + + if (ret) + dev_err(priv->dev, "mdb_del ERROR %pe\n", ERR_PTR(ret)); + return ret; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_mdb_del, "REALTEK_DSA"); + +/** + * rtl83xx_port_bridge_flags() - set port bridge flags + * @ds: DSA switch instance + * @port: port index + * @flags: bridge port flags + * @extack: netlink extended ack for reporting errors + * + * This function handles setting bridge port flags like learning and flooding. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv = ds->priv; + bool enable; + int ret; + + if (flags.mask & BR_LEARNING) { + if (!priv->ops->port_set_learning) + return -EOPNOTSUPP; + + enable = !!(flags.val & BR_LEARNING); + ret = priv->ops->port_set_learning(priv, port, enable); + if (ret) + return ret; + } + + if (flags.mask & BR_FLOOD) { + if (!priv->ops->port_set_ucast_flood) + return -EOPNOTSUPP; + + enable = !!(flags.val & BR_FLOOD); + ret = priv->ops->port_set_ucast_flood(priv, port, enable); + if (ret) + return ret; + } + + if (flags.mask & BR_MCAST_FLOOD) { + if (!priv->ops->port_set_mcast_flood) + return -EOPNOTSUPP; + + enable = !!(flags.val & BR_MCAST_FLOOD); + ret = priv->ops->port_set_mcast_flood(priv, port, enable); + if (ret) + return ret; + } + + if (flags.mask & BR_BCAST_FLOOD) { + if (!priv->ops->port_set_bcast_flood) + return -EOPNOTSUPP; + + enable = !!(flags.val & BR_BCAST_FLOOD); + ret = priv->ops->port_set_bcast_flood(priv, port, enable); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_port_bridge_flags, "REALTEK_DSA"); + +/** + * rtl83xx_setup_port_flood_control() - setup default flood control for a port + * @priv: realtek_priv pointer + * @port: port index + * + * This function enables flooding for a given port. + * + * Context: Can sleep. + * Return: 0 on success, negative value for failure. + */ +int rtl83xx_setup_port_flood_control(struct realtek_priv *priv, int port) +{ + int ret; + + if (priv->ops->port_set_ucast_flood) { + ret = priv->ops->port_set_ucast_flood(priv, port, true); + if (ret) + return ret; + } + + if (priv->ops->port_set_mcast_flood) { + ret = priv->ops->port_set_mcast_flood(priv, port, true); + if (ret) + return ret; + } + + if (priv->ops->port_set_bcast_flood) { + ret = priv->ops->port_set_bcast_flood(priv, port, true); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_port_flood_control, "REALTEK_DSA"); + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("Realtek DSA switches common module"); diff --git a/drivers/net/dsa/realtek/rtl83xx.h b/drivers/net/dsa/realtek/rtl83xx.h index c8a0ff8fd75e..d86447121276 100644 --- a/drivers/net/dsa/realtek/rtl83xx.h +++ b/drivers/net/dsa/realtek/rtl83xx.h @@ -21,4 +21,31 @@ void rtl83xx_remove(struct realtek_priv *priv); void rtl83xx_reset_assert(struct realtek_priv *priv); void rtl83xx_reset_deassert(struct realtek_priv *priv); +int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_forward_offload, + struct netlink_ext_ack *extack); +void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge); +int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack); +int rtl83xx_setup_port_flood_control(struct realtek_priv *priv, int port); + +void rtl83xx_port_fast_age(struct dsa_switch *ds, int port); +int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db); +int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db); +int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data); +int rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); +int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); + #endif /* _RTL83XX_H */ diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c index fba926f85b47..7547999a113f 100644 --- a/drivers/net/dsa/sja1105/sja1105_flower.c +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -391,9 +391,9 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port, struct dsa_port *to_dp; to_dp = dsa_port_from_netdev(act->dev); - if (IS_ERR(to_dp)) { + if (IS_ERR(to_dp) || to_dp->ds != ds) { NL_SET_ERR_MSG_MOD(extack, - "Destination not a switch port"); + "Destination not a local switch port"); return -EOPNOTSUPP; } diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 2697073dbf90..1653e83d9123 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2847,7 +2847,8 @@ static void sja1105_mirror_del(struct dsa_switch *ds, int port, } static int sja1105_port_policer_add(struct dsa_switch *ds, int port, - const struct flow_action_police *policer) + const struct flow_action_police *policer, + struct netlink_ext_ack *extack) { struct sja1105_l2_policing_entry *policing; struct sja1105_private *priv = ds->priv; diff --git a/drivers/net/dsa/sja1105/sja1105_vl.c b/drivers/net/dsa/sja1105/sja1105_vl.c index 0ae9cb5ea8d1..e6ba9d4f8d1e 100644 --- a/drivers/net/dsa/sja1105/sja1105_vl.c +++ b/drivers/net/dsa/sja1105/sja1105_vl.c @@ -791,8 +791,7 @@ int sja1105_vl_stats(struct sja1105_private *priv, int port, pkts = timingerr + unreleased + lengtherr; flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0, - jiffies - rule->vl.stats.lastused, - FLOW_ACTION_HW_STATS_IMMEDIATE); + jiffies, FLOW_ACTION_HW_STATS_IMMEDIATE); rule->vl.stats.pkts = pkts; rule->vl.stats.lastused = jiffies; diff --git a/drivers/net/dsa/xrs700x/xrs700x_i2c.c b/drivers/net/dsa/xrs700x/xrs700x_i2c.c index 9b731dea78c1..e6a7839c4a04 100644 --- a/drivers/net/dsa/xrs700x/xrs700x_i2c.c +++ b/drivers/net/dsa/xrs700x/xrs700x_i2c.c @@ -127,8 +127,8 @@ static void xrs700x_i2c_shutdown(struct i2c_client *i2c) } static const struct i2c_device_id xrs700x_i2c_id[] = { - { "xrs700x-switch" }, - {} + { .name = "xrs700x-switch" }, + { } }; MODULE_DEVICE_TABLE(i2c, xrs700x_i2c_id); diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c index 5b66109ecc23..159b16606f6c 100644 --- a/drivers/net/dsa/yt921x.c +++ b/drivers/net/dsa/yt921x.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "yt921x.h" @@ -185,6 +186,16 @@ struct yt921x_reg_mdio { #define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds) #define to_device(priv) ((priv)->ds.dev) +static u32 ethaddr_hi4_to_u32(const unsigned char *addr) +{ + return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; +} + +static u32 ethaddr_lo2_to_u32(const unsigned char *addr) +{ + return (addr[4] << 8) | addr[5]; +} + static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp) { WARN_ON(!mutex_is_locked(&priv->reg_lock)); @@ -255,63 +266,136 @@ yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set) return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask); } -/* Some registers, like VLANn_CTRL, should always be written in 64-bit, even if - * you are to write only the lower / upper 32 bits. +/* Some multi-word registers, like VLANn_CTRL, should be treated as a single + * long register. More specifically, writes to parts of its words won't become + * visible, until the last word is written. * - * There is no such restriction for reading, but we still provide 64-bit read - * wrappers so that we always handle u64 values. + * Here we require full read and write operations over these registers to + * eliminate potential issues, although partial reads/writes are also possible. */ -static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp) +static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val) { - u32 lo; - u32 hi; - int res; - - res = yt921x_reg_read(priv, reg, &lo); - if (res) - return res; - res = yt921x_reg_read(priv, reg + 4, &hi); - if (res) - return res; - - *valp = ((u64)hi << 32) | lo; - return 0; -} - -static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val) -{ - int res; - - res = yt921x_reg_write(priv, reg, (u32)val); - if (res) - return res; - return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32)); + *lo &= ~lower_32_bits(mask); + *hi &= ~upper_32_bits(mask); + *lo |= lower_32_bits(val); + *hi |= upper_32_bits(val); } static int -yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 val) +yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals, + unsigned int num_regs) { int res; - u64 v; - u64 u; - res = yt921x_reg64_read(priv, reg, &v); + for (unsigned int i = 0; i < num_regs; i++) { + res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals, + unsigned int num_regs) +{ + int res; + + for (unsigned int i = 0; i < num_regs; i++) { + res = yt921x_reg_write(priv, reg + 4 * i, vals[i]); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, + const u32 *vals, unsigned int num_regs) +{ + bool changed = false; + u32 vs[4]; + int res; + + BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs)); + + res = yt921x_regs_read(priv, reg, vs, num_regs); if (res) return res; - u = v; - u &= ~mask; - u |= val; - if (u == v) + for (unsigned int i = 0; i < num_regs; i++) { + u32 u = vs[i]; + + u &= ~masks[i]; + u |= vals[i]; + if (u != vs[i]) + changed = true; + + vs[i] = u; + } + + if (!changed) return 0; - return yt921x_reg64_write(priv, reg, u); + return yt921x_regs_write(priv, reg, vs, num_regs); } -static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 mask) +static int +yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, + unsigned int num_regs) { - return yt921x_reg64_update_bits(priv, reg, mask, 0); + bool changed = false; + u32 vs[4]; + int res; + + BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs)); + + res = yt921x_regs_read(priv, reg, vs, num_regs); + if (res) + return res; + + for (unsigned int i = 0; i < num_regs; i++) { + u32 u = vs[i]; + + u &= ~masks[i]; + if (u != vs[i]) + changed = true; + + vs[i] = u; + } + + if (!changed) + return 0; + + return yt921x_regs_write(priv, reg, vs, num_regs); +} + +static int +yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals) +{ + return yt921x_regs_write(priv, reg, vals, 2); +} + +static int +yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, + const u32 *vals) +{ + return yt921x_regs_update_bits(priv, reg, masks, vals, 2); +} + +static int +yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks) +{ + return yt921x_regs_clear_bits(priv, reg, masks, 2); +} + +static int +yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals) +{ + return yt921x_regs_write(priv, reg, vals, 3); } static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp) @@ -1007,6 +1091,13 @@ yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) return res; } +static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port) +{ + struct dsa_port *dp = dsa_to_port(&priv->ds, port); + + return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN; +} + static int yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { @@ -1038,6 +1129,1361 @@ static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port) return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN; } +/* v * 2^e */ +static u64 ldexpu64(u64 v, int e) +{ + return e >= 0 ? v << e : v >> -e; +} + +/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */ +static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C) +{ + int e = 2 * unit + C + YT921X_TOKEN_RATE_C; + + return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000); +} + +static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C) +{ + int e = 2 * unit + C + YT921X_TOKEN_RATE_C; + + return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns); +} + +/* burst = 2^C * token * 4^unit */ +static u32 burst2token(u64 burst, int unit, int C) +{ + return ldexpu64(burst, -(2 * unit + C)); +} + +static u64 token2burst(u32 token, int unit, int C) +{ + return ldexpu64(token, 2 * unit + C); +} + +struct yt921x_marker { + u32 cir; + u32 cbs; + u32 ebs; + int unit; + bool pkt_mode; +}; + +#define YT921X_MARKER_PKT_MODE BIT(0) +#define YT921X_MARKER_SINGLE_BUCKET BIT(1) + +static int +yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst, + unsigned int flags, unsigned int slot_ns, u32 cir_max, + u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port, + struct netlink_ext_ack *extack) +{ + const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C : + YT921X_TOKEN_BYTE_C; + struct device *dev = to_device(priv); + struct yt921x_marker m; + u64 burst_est; + u64 burst_sug; + u64 burst_max; + u64 rate_max; + + m.unit = unit_max; + rate_max = token2rate(cir_max, slot_ns, m.unit, C); + burst_max = token2burst(cbs_max, m.unit, C); + + /* Check for unusual values */ + if (rate > rate_max || burst > burst_max) { + NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate"); + return -ERANGE; + } + + /* Check for matching burst */ + burst_est = div_u64(slot_ns * rate, 1000000000); + burst_sug = burst_est; + if (flags & YT921X_MARKER_PKT_MODE) + burst_sug++; + else + burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) + + ETH_FCS_LEN; + if (burst_sug > burst) + NL_SET_ERR_MSG_FMT_MOD(extack, + "Consider match rate %llu with burst at least %llu", + rate, burst_sug); + + /* Select unit */ + for (; m.unit > 0; m.unit--) { + if (rate > (rate_max >> 2) || burst > (burst_max >> 2)) + break; + rate_max >>= 2; + burst_max >>= 2; + } + + /* Calculate information rate and bucket size */ + m.cir = rate2token(rate, slot_ns, m.unit, C); + if (!m.cir) + m.cir = 1; + else if (WARN_ON(m.cir > cir_max)) + m.cir = cir_max; + m.cbs = burst2token(burst, m.unit, C); + if (!m.cbs) + m.cbs = 1; + else if (WARN_ON(m.cbs > cbs_max)) + m.cbs = cbs_max; + + /* Cut EBS */ + m.ebs = 0; + if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) { + /* We don't have a chance to adjust rate when MTU is changed */ + if (flags & YT921X_MARKER_PKT_MODE) + burst_est++; + else + burst_est += YT921X_FRAME_SIZE_MAX; + + if (burst_est < burst) { + u32 pbs = m.cbs; + + m.cbs = burst2token(burst_est, m.unit, C); + if (!m.cbs) + m.cbs = 1; + else if (WARN_ON(m.cbs > cbs_max)) + m.cbs = cbs_max; + + if (pbs > m.cbs) + m.ebs = pbs - m.cbs; + } + } + + dev_dbg(dev, + "slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n", + slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs); + + m.pkt_mode = flags & YT921X_MARKER_PKT_MODE; + *marker = m; + return 0; +} + +static int +yt921x_marker_tfm_police(struct yt921x_marker *marker, + const struct flow_action_police *police, + unsigned int flags, struct yt921x_priv *priv, int port, + struct netlink_ext_ack *extack) +{ + bool pkt_mode = !!police->rate_pkt_ps; + u64 burst; + u64 rate; + + rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps; + burst = pkt_mode ? police->burst_pkt : police->burst; + if (pkt_mode) + flags |= YT921X_MARKER_PKT_MODE; + + return yt921x_marker_tfm(marker, rate, burst, flags, + priv->meter_slot_ns, YT921X_METER_CIR_MAX, + YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX, + priv, port, extack); +} + +static int +yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst, + unsigned int flags, struct yt921x_priv *priv, int port, + struct netlink_ext_ack *extack) +{ + return yt921x_marker_tfm(marker, rate, burst, flags, + priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX, + YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX, + priv, port, extack); +} + +static int +yt921x_police_validate(const struct flow_action_police *police, + const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (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 (police->notexceed.act_id != FLOW_ACTION_PIPE && + 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 (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act && + !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; + } + + /* mtu defaults to unlimited but we got 2040 here, don't know why */ + if (police->peakrate_bytes_ps || police->avrate || police->overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int +yt921x_meter_config(struct yt921x_priv *priv, unsigned int id, + const struct yt921x_marker *marker) +{ + u32 ctrls[3]; + + ctrls[0] = 0; + ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir); + ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) | + YT921X_METER_CTRLc_DROP_R | + YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN | + YT921X_METER_CTRLc_METER_EN; + if (marker->pkt_mode) + ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE; + update_ctrls_unaligned(&ctrls[0], &ctrls[1], + YT921X_METER_CTRLab_EBS_M, + YT921X_METER_CTRLab_EBS(marker->ebs)); + update_ctrls_unaligned(&ctrls[1], &ctrls[2], + YT921X_METER_CTRLbc_CBS_M, + YT921X_METER_CTRLbc_CBS(marker->cbs)); + + return yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls); +} + +static void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = to_yt921x_priv(ds); + struct device *dev = to_device(priv); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on", + port, res); +} + +static int +yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port, + const struct flow_action_police *police, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = to_yt921x_priv(ds); + struct yt921x_marker marker; + u32 ctrl; + int res; + + res = yt921x_police_validate(police, NULL, NULL, extack); + if (res) + return res; + + res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack); + if (res) + return res; + + mutex_lock(&priv->reg_lock); + res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker); + if (res) + goto end; + + ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN; + res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl); +end: + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port, + const struct tc_tbf_qopt_offload *qopt) +{ + struct yt921x_priv *priv = to_yt921x_priv(ds); + struct netlink_ext_ack *extack = qopt->extack; + u32 ctrls[2]; + int res; + + if (qopt->parent != TC_H_ROOT) + return -EOPNOTSUPP; + + switch (qopt->command) { + case TC_TBF_STATS: + /* Unfortunately the convention for TC_*_STATS is a mess, + * neither 0 nor -EOPNOTSUPP is perfect here. + */ + return -EOPNOTSUPP; + case TC_TBF_DESTROY: + ctrls[0] = 0; + ctrls[1] = 0; + break; + case TC_TBF_REPLACE: { + const struct tc_tbf_qopt_offload_replace_params *p; + struct yt921x_marker marker; + + p = &qopt->replace_params; + + res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps, + p->max_size, + YT921X_MARKER_SINGLE_BUCKET, + priv, port, extack); + if (res) + return res; + + ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) | + YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs); + ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) | + YT921X_PORT_SHAPE_CTRLb_EN; + break; + } + default: + return -EOPNOTSUPP; + } + + mutex_lock(&priv->reg_lock); + res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data) +{ + switch (type) { + case TC_SETUP_QDISC_TBF: { + const struct tc_tbf_qopt_offload *qopt = type_data; + + return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt); + } + default: + return -EOPNOTSUPP; + } +} + +/* ACL: 48 blocks * 8 entries + * + * One rule can span multiple entries, but within a block. + */ + +static void +yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset, + u32 flags, bool set) +{ + if (set) + entry->key[offset] |= flags; + entry->mask[offset] |= flags; +} + +static unsigned int +yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries, + unsigned int size, bool set) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_IPV4_DA: + case YT921X_ACL_TYPE_IPV4_SA: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_IPV4_FRAG, set); + return size; + case YT921X_ACL_TYPE_IPV6_DA3: + case YT921X_ACL_TYPE_IPV6_SA3: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_IPV6_xA3_FRAG, + set); + return size; + case YT921X_ACL_TYPE_MISC: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_MISC_FRAG, set); + return size; + case YT921X_ACL_TYPE_L4: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_L4_FRAG, set); + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set); + + return size + 1; +} + +static unsigned int +yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries, + unsigned int size, bool set) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_IPV6_DA2: + case YT921X_ACL_TYPE_IPV6_SA2: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG, + set); + return size; + case YT921X_ACL_TYPE_MISC: + yt921x_acl_entry_set(&entries[i], 0, + YT921X_ACL_BINa_MISC_FIRST_FRAG, + set); + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + yt921x_acl_entry_set(&entries[size], 0, + YT921X_ACL_BINa_MISC_FIRST_FRAG, set); + + return size + 1; +} + +static unsigned int +yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries, + unsigned int size, enum yt921x_l3_type type) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_MAC_DA0: + case YT921X_ACL_TYPE_MAC_SA0: + entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M; + return size; + case YT921X_ACL_TYPE_MISC: + entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type); + entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M; + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type); + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M; + + return size + 1; +} + +static unsigned int +yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries, + unsigned int size, enum yt921x_l4_type type) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_IPV4_DA: + case YT921X_ACL_TYPE_IPV4_SA: + entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M; + return size; + case YT921X_ACL_TYPE_IPV6_DA0: + case YT921X_ACL_TYPE_IPV6_DA1: + case YT921X_ACL_TYPE_IPV6_DA2: + case YT921X_ACL_TYPE_IPV6_DA3: + case YT921X_ACL_TYPE_IPV6_SA0: + case YT921X_ACL_TYPE_IPV6_SA1: + case YT921X_ACL_TYPE_IPV6_SA2: + case YT921X_ACL_TYPE_IPV6_SA3: + entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M; + return size; + case YT921X_ACL_TYPE_L4: + entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M; + return size; + case YT921X_ACL_TYPE_MISC: + entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M; + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) | + YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M; + + return size + 1; +} + +static struct yt921x_acl_entry * +yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep, + u32 type) +{ + unsigned int size = *sizep; + + if (size >= YT921X_ACL_ENT_PER_BLK) + return NULL; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type); + + (*sizep)++; + return &entries[size]; +} + +static struct yt921x_acl_entry * +yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep, + u32 type) +{ + for (unsigned int i = 0; i < *sizep; i++) + if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) == + type) + return &entries[i]; + return yt921x_acl_entries_new(entries, sizep, type); +} + +static void +yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord, + u16 ports_mask) +{ + struct yt921x_acl_entry *entries = aclrule->entries; + + for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) { + entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) | + YT921X_ACL_KEYb_ORD(ord); + } +} + +struct yt921x_acl_rule_ext { + struct yt921x_acl_rule r; + + struct yt921x_marker marker; +}; + +static int +yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, + const struct flow_cls_offload *cls) +{ + const struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct yt921x_acl_entry *entries = ruleext->r.entries; + struct netlink_ext_ack *extack = cls->common.extack; + const struct flow_dissector *dissector; + struct yt921x_acl_entry *entry; + unsigned int size = 0; + bool use_dport; + bool use_sport; + + /* Incomplete and probably won't, since it supports custom u32 filters. + * New adapters are welcome. + */ + dissector = rule->match.dissector; + if (dissector->used_keys & + ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) | + BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) | + BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IP) | + BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used"); + return -EOPNOTSUPP; + } + + /* Entries */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_match_ipv4_addrs match; + + flow_rule_match_ipv4_addrs(rule, &match); + + if (match.mask->dst) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV4_DA); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->dst); + entry->mask[0] |= ntohl(match.mask->dst); + } + + if (match.mask->src) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV4_SA); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->src); + entry->mask[0] |= ntohl(match.mask->src); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + struct flow_match_ipv6_addrs match; + + flow_rule_match_ipv6_addrs(rule, &match); + + for (unsigned int i = 0; i < 4; i++) { + if (!match.mask->dst.s6_addr32[i]) + continue; + + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV6_DA0 + i); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]); + entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]); + } + + for (unsigned int i = 0; i < 4; i++) { + if (!match.mask->src.s6_addr32[i]) + continue; + + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV6_SA0 + i); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->src.s6_addr32[i]); + entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]); + } + } + + use_dport = false; + use_sport = false; + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports match; + + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_L4); + if (!entry) + goto err; + + flow_rule_match_ports(rule, &match); + + use_dport = !!match.mask->dst; + use_sport = !!match.mask->src; + + entry->key[0] |= (ntohs(match.key->dst) << 16) | + ntohs(match.key->src); + entry->mask[0] |= (ntohs(match.mask->dst) << 16) | + ntohs(match.mask->src); + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) { + struct flow_match_ports_range match; + + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_L4); + if (!entry) + goto err; + + flow_rule_match_ports_range(rule, &match); + + if ((use_dport && match.mask->tp.dst) || + (use_sport && match.mask->tp.src)) { + NL_SET_ERR_MSG_MOD(extack, + "Port mask and range are mutually exclusive"); + return -EINVAL; + } + + if (match.mask->tp.dst) { + entry->key[0] |= ntohs(match.key->tp_min.dst) << 16; + entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN; + entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16; + } + + if (match.mask->tp.src) { + entry->key[0] |= ntohs(match.key->tp_min.src); + entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN; + entry->mask[0] |= ntohs(match.key->tp_max.src); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + u32 mask; + + flow_rule_match_eth_addrs(rule, &match); + + mask = ethaddr_hi4_to_u32(match.mask->dst); + if (mask) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_MAC_DA0); + if (!entry) + goto err; + + entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst); + entry->mask[0] |= mask; + } + + mask = ethaddr_hi4_to_u32(match.mask->src); + if (mask) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_MAC_SA0); + if (!entry) + goto err; + + entry->key[0] |= ethaddr_hi4_to_u32(match.key->src); + entry->mask[0] |= mask; + } + + mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) | + ethaddr_lo2_to_u32(match.mask->src); + if (mask) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_MAC_DA1_SA1); + if (!entry) + goto err; + + entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) | + ethaddr_lo2_to_u32(match.key->src); + entry->mask[0] |= mask; + } + } + + /* Entries + Misc */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic match; + + flow_rule_match_basic(rule, &match); + + if (match.mask->n_proto) { + enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER; + + if (match.mask->n_proto == htons(~0)) + switch (match.key->n_proto) { + case htons(ETH_P_IP): + l3type = YT921X_L3_TYPE_IPV4; + break; + case htons(ETH_P_IPV6): + l3type = YT921X_L3_TYPE_IPV6; + break; + case htons(ETH_P_ARP): + l3type = YT921X_L3_TYPE_ARP; + break; + case htons(ETH_P_LLDP): + l3type = YT921X_L3_TYPE_LLDP; + break; + case htons(ETH_P_PAE): + l3type = YT921X_L3_TYPE_PAE; + break; + case htons(ETH_P_CFM): + l3type = YT921X_L3_TYPE_ERP; + break; + } + + if (l3type != YT921X_L3_TYPE_OTHER) { + size = yt921x_acl_entries_set_l3_type(entries, + size, + l3type); + if (!size) + goto err; + } else { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_ETHERTYPE); + if (!entry) + goto err; + + entry->key[0] |= ntohs(match.key->n_proto); + entry->mask[0] |= ntohs(match.mask->n_proto); + } + } + + if (match.mask->ip_proto) { + enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER; + + if (match.mask->ip_proto == (u8)~0) + switch (match.key->ip_proto) { + case IPPROTO_TCP: + l4type = YT921X_L4_TYPE_TCP; + break; + case IPPROTO_UDP: + l4type = YT921X_L4_TYPE_UDP; + break; + case IPPROTO_UDPLITE: + l4type = YT921X_L4_TYPE_UDPLITE; + break; + case IPPROTO_ICMP: + l4type = YT921X_L4_TYPE_ICMP; + break; + case IPPROTO_IGMP: + l4type = YT921X_L4_TYPE_IGMP; + break; + } + + if (l4type != YT921X_L4_TYPE_OTHER) { + size = yt921x_acl_entries_set_l4_type(entries, + size, + l4type); + if (!size) + goto err; + } else { + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_MISC); + if (!entry) + goto err; + + entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto); + entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto); + } + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { + u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG; + struct flow_match_control match; + + flow_rule_match_control(rule, &match); + if (!flow_rule_is_supp_control_flags(supp_flags, + match.mask->flags, extack)) + return -EOPNOTSUPP; + + if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { + bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT; + + size = yt921x_acl_entries_set_is_fragment(entries, size, + set); + if (!size) + goto err; + } + if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { + bool set = match.key->flags & FLOW_DIS_FIRST_FRAG; + + size = yt921x_acl_entries_set_first_frag(entries, size, + set); + if (!size) + goto err; + } + } + + /* Misc only */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) { + struct flow_match_ip match; + + flow_rule_match_ip(rule, &match); + if (match.mask->ttl) { + NL_SET_ERR_MSG_MOD(extack, + "Matching on TTL not supported"); + return -EOPNOTSUPP; + } + + if (match.mask->tos) { + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_MISC); + if (!entry) + goto err; + + entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos); + entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) { + struct flow_match_tcp match; + + flow_rule_match_tcp(rule, &match); + if (match.mask->flags & htons(~0xff)) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags"); + return -EOPNOTSUPP; + } + + if (match.mask->flags) { + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_MISC); + if (!entry) + goto err; + + entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags)); + entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags)); + } + } + + if (!size) { + NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen"); + return -EOPNOTSUPP; + } + + ruleext->r.mask = (1 << size) - 1; + return 0; + +err: + NL_SET_ERR_MSG_MOD(extack, "Rule too complex"); + return -EOPNOTSUPP; +} + +static int +yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext, + const struct flow_cls_offload *cls, + struct yt921x_priv *priv, int port) +{ + const struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + const struct flow_action *flow_action = &rule->action; + struct netlink_ext_ack *extack = cls->common.extack; + enum flow_action_id redir_act = NUM_FLOW_ACTIONS; + const struct flow_action_entry *act; + u32 *action = ruleext->r.action; + bool seen_priority = false; + const char *reason = NULL; + bool seen_police = false; + unsigned int i; + int res; + + memset(action, 0, 3 * sizeof(*action)); + flow_action_for_each(i, act, flow_action) + switch (act->id) { + case FLOW_ACTION_ACCEPT: + case FLOW_ACTION_DROP: + case FLOW_ACTION_REDIRECT: + if (redir_act != NUM_FLOW_ACTIONS && + redir_act != act->id) { + reason = "Different redirect actions"; + goto fallback; + } + redir_act = act->id; + + switch (act->id) { + case FLOW_ACTION_ACCEPT: + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_FWD; + break; + case FLOW_ACTION_DROP: + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_REDIR; + break; + case FLOW_ACTION_REDIRECT: { + struct dsa_port *to_dp; + + to_dp = dsa_port_from_netdev(act->dev); + if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) { + reason = "Redirect to non-local port"; + goto fallback; + } + + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_REDIR | + YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index); + break; + } + default: + break; + } + break; + case FLOW_ACTION_PRIORITY: + if (seen_priority) { + action[0] &= ~YT921X_ACL_ACTa_PRIO_EN; + action[1] &= ~YT921X_ACL_ACTb_PRIO_M; + + reason = "Multiple priority actions"; + goto fallback; + } + seen_priority = true; + + if (act->priority >= YT921X_PRIO_NUM) { + NL_SET_ERR_MSG_MOD(extack, + "Priority value is too high"); + return -EOPNOTSUPP; + } + action[0] |= YT921X_ACL_ACTa_PRIO_EN; + action[1] |= YT921X_ACL_ACTb_PRIO(act->priority); + break; + case FLOW_ACTION_POLICE: { + const struct flow_action_police *police = &act->police; + + if (seen_police) { + action[0] &= ~YT921X_ACL_ACTa_METER_EN; + + reason = "Multiple police actions"; + goto fallback; + } + seen_police = true; + + res = yt921x_police_validate(police, flow_action, act, + extack); + if (res) + return res; + + res = yt921x_marker_tfm_police(&ruleext->marker, police, + 0, priv, port, extack); + if (res) + return res; + + action[0] |= YT921X_ACL_ACTa_METER_EN; + break; + } + default: +fallback: + if (cls->common.skip_sw) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "Action not supported when skip_sw: %s", + reason); + return -EOPNOTSUPP; + } + fallthrough; + case FLOW_ACTION_TRAP: + redir_act = FLOW_ACTION_TRAP; + + action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M & + ~YT921X_ACL_ACTc_FWD_M; + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_TRAP; + break; + } + + ruleext->r.sw_assisted = !cls->common.skip_sw; + return 0; +} + +static int +yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port, + const struct flow_cls_offload *cls, bool ingress, + struct yt921x_priv *priv) +{ + struct netlink_ext_ack *extack = cls->common.extack; + int res; + + if (!ingress) { + NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); + return -EOPNOTSUPP; + } + + if (cls->common.chain_index) { + NL_SET_ERR_MSG(extack, "Only chain 0 is supported"); + return -EOPNOTSUPP; + } + + res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port); + if (res) + return res; + res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls); + if (res) + return res; + + yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port)); + ruleext->r.tag = cls->cookie; + ruleext->r.type = TC_SETUP_CLSFLOWER; + return 0; +} + +static unsigned int +yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type, + unsigned long tag) +{ + for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) { + const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid]; + + if (!aclblk) + continue; + + for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) + if (aclblk->rules[i] && aclblk->rules[i]->tag == tag && + aclblk->rules[i]->type == type) + return YT921X_ACL_ENT_PER_BLK * blkid + i; + } + + return UINT_MAX; +} + +static unsigned int +yt921x_acl_reserve(struct yt921x_priv *priv, unsigned int entscnt, + struct netlink_ext_ack *extack) +{ + int candidates[YT921X_ACL_ENT_PER_BLK + 1]; + unsigned int acl_used_cnt = 0; + + if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK)) + return UINT_MAX; + + for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++) + candidates[i] = -1; + for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) { + unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]); + + candidates[blk_used_cnt] = i; + acl_used_cnt += blk_used_cnt; + } + + if (acl_used_cnt >= YT921X_ACL_NUM) { + NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached"); + return UINT_MAX; + } + if (acl_used_cnt + entscnt <= YT921X_ACL_NUM) + for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1; + i-- > 0;) + if (candidates[i] >= 0) + return YT921X_ACL_ENT_PER_BLK * candidates[i] + + ffz(priv->acl_masks[candidates[i]]); + + NL_SET_ERR_MSG_MOD(extack, + "ACL entry allocation failed, simplify your rules or remove existing rules"); + return UINT_MAX; +} + +static int +yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask) +{ + const struct yt921x_acl_rule *aclrule; + const struct yt921x_acl_blk *aclblk; + unsigned int blkid; + unsigned int binid; + unsigned long mask; + u32 zeros[3] = {}; + unsigned int i; + unsigned int o; + u32 ctrl; + int res; + + blkid = entid / YT921X_ACL_ENT_PER_BLK; + binid = entid % YT921X_ACL_ENT_PER_BLK; + aclblk = priv->acl_blks[blkid]; + aclrule = aclblk->rules[binid]; + + /* Write actions */ + res = yt921x_reg96_write(priv, YT921X_ACLn_ACT(entid), + aclrule ? aclrule->action : zeros); + if (res) + return res; + + /* Select the block */ + ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid); + res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl); + if (res) + return res; + + /* Write keys and masks */ + ctrl = 0; + for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) + ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i); + + mask = entsmask; + i = 0; + for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) { + res = yt921x_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o), + aclrule ? aclrule->entries[i].key : + zeros); + if (res) + return res; + + res = yt921x_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o), + aclrule ? aclrule->entries[i].mask : + zeros); + if (res) + return res; + + ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o); + i++; + } + + res = yt921x_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl); + if (res) + return res; + + ctrl = 0; + for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) { + const struct yt921x_acl_rule *other = aclblk->rules[i]; + + if (!other) + continue; + + mask = other->mask; + for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) + ctrl |= YT921X_ACL_ENTRY_ENm(o) | + YT921X_ACL_ENTRY_GRPIDm(o, i); + } + res = yt921x_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl); + if (res) + return res; + + /* Commit the block */ + ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid); + res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl); + if (res) + return res; + + return 0; +} + +static int +yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type, + unsigned long tag) +{ + struct yt921x_acl_rule *aclrule; + struct yt921x_acl_blk *aclblk; + unsigned int binid; + unsigned int blkid; + unsigned int entid; + int res; + + entid = yt921x_acl_find(priv, type, tag); + if (entid == UINT_MAX) + return -ENOENT; + + blkid = entid / YT921X_ACL_ENT_PER_BLK; + binid = entid % YT921X_ACL_ENT_PER_BLK; + aclblk = priv->acl_blks[blkid]; + aclrule = aclblk->rules[binid]; + + aclblk->rules[binid] = NULL; + res = yt921x_acl_commit(priv, entid, aclrule->mask); + /* the kernel never rolls back on failure */ + + if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN) + clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M, + aclrule->action[0]), + priv->meters_map); + priv->acl_masks[blkid] &= ~aclrule->mask; + kvfree(aclrule); + if (!priv->acl_masks[blkid]) { + kvfree(aclblk); + priv->acl_blks[blkid] = NULL; + } + return res; +} + +static int +yt921x_acl_add(struct yt921x_priv *priv, + const struct yt921x_acl_rule_ext *ruleext, + struct netlink_ext_ack *extack) +{ + unsigned int entscnt = hweight8(ruleext->r.mask); + struct yt921x_acl_rule *aclrule; + struct yt921x_acl_blk *aclblk; + bool use_trap = false; + unsigned int meterid; + unsigned long mask; + unsigned int binid; + unsigned int blkid; + unsigned int entid; + unsigned int o; + int res; + + /* Allocate resources */ + entid = yt921x_acl_reserve(priv, entscnt, extack); + if (entid == UINT_MAX) + return -EOPNOTSUPP; + + if (!(ruleext->r.action[0] & YT921X_ACL_ACTa_METER_EN)) { + meterid = YT921X_METER_NUM; + } else { + meterid = find_first_zero_bit(priv->meters_map, + YT921X_METER_NUM); + if (meterid < YT921X_METER_NUM) { + res = yt921x_meter_config(priv, meterid, + &ruleext->marker); + if (res) + return res; + } else if (ruleext->r.sw_assisted) { + use_trap = true; + } else { + NL_SET_ERR_MSG_MOD(extack, + "No more meters available"); + return -EOPNOTSUPP; + } + } + + /* Prepare acl block ctrlblk */ + blkid = entid / YT921X_ACL_ENT_PER_BLK; + binid = entid % YT921X_ACL_ENT_PER_BLK; + aclblk = priv->acl_blks[blkid]; + if (!aclblk) { + aclblk = kvzalloc_obj(*aclblk); + if (!aclblk) + return -ENOMEM; + priv->acl_blks[blkid] = aclblk; + } + + /* Prepare acl rule ctrlblk */ + aclrule = kvmemdup(&ruleext->r, + offsetof(struct yt921x_acl_rule, entries[entscnt]), + GFP_KERNEL); + if (!aclrule) { + res = -ENOMEM; + goto err; + } + + /* Replace the placeholder resource IDs */ + aclrule->mask = 0; + mask = priv->acl_masks[blkid]; + for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) { + aclrule->mask |= BIT(o); + entscnt--; + if (!entscnt) + break; + } + + if (use_trap) { + aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M & + ~YT921X_ACL_ACTc_FWD_M; + aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_TRAP; + } + if (meterid < YT921X_METER_NUM) + aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid); + else + aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN; + + /* Write rules */ + aclblk->rules[binid] = aclrule; + res = yt921x_acl_commit(priv, entid, aclrule->mask); + if (res) { + aclblk->rules[binid] = NULL; + kvfree(aclrule); + goto err; + } + + if (meterid < YT921X_METER_NUM) + set_bit(meterid, priv->meters_map); + priv->acl_masks[blkid] |= aclrule->mask; + return 0; + +err: + if (!priv->acl_masks[blkid]) { + kvfree(aclblk); + priv->acl_blks[blkid] = NULL; + } + return res; +} + +static int +yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct yt921x_priv *priv = to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct netlink_ext_ack *extack = cls->common.extack; + struct yt921x_priv *priv = to_yt921x_priv(ds); + struct yt921x_acl_rule_ext ruleext; + int res; + + res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress, + priv); + if (res) + return res; + + mutex_lock(&priv->reg_lock); + res = yt921x_acl_add(priv, &ruleext, extack); + mutex_unlock(&priv->reg_lock); + + return res; +} + static int yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress) { @@ -1328,12 +2774,12 @@ yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr, u32 ctrl; int res; - ctrl = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; + ctrl = ethaddr_hi4_to_u32(addr); res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl); if (res) return res; - ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | (addr[4] << 8) | addr[5]; + ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | ethaddr_lo2_to_u32(addr); return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); } @@ -1844,33 +3290,31 @@ yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering) return 0; } -static int -yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) +static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) { - u64 mask64; + u32 masks[2]; - mask64 = YT921X_VLAN_CTRL_PORTS(port) | - YT921X_VLAN_CTRL_UNTAG_PORTn(port); + masks[0] = YT921X_VLAN_CTRLa_PORTn(port); + masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); - return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64); + return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks); } static int yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged) { - u64 mask64; - u64 ctrl64; + u32 masks[2]; + u32 ctrls[2]; - mask64 = YT921X_VLAN_CTRL_PORTn(port) | - YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask); - ctrl64 = mask64; + masks[0] = YT921X_VLAN_CTRLa_PORTn(port) | + YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask); + ctrls[0] = masks[0]; - mask64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port); - if (untagged) - ctrl64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port); + masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); + ctrls[1] = untagged ? masks[1] : 0; return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid), - mask64, ctrl64); + masks, ctrls); } static int @@ -2318,8 +3762,8 @@ yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge, const struct switchdev_vlan_msti *msti) { struct yt921x_priv *priv = to_yt921x_priv(ds); - u64 mask64; - u64 ctrl64; + u32 masks[2]; + u32 ctrls[2]; int res; if (!msti->vid) @@ -2327,12 +3771,14 @@ yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge, if (!msti->msti || msti->msti >= YT921X_MSTI_NUM) return -EINVAL; - mask64 = YT921X_VLAN_CTRL_STP_ID_M; - ctrl64 = YT921X_VLAN_CTRL_STP_ID(msti->msti); + masks[0] = 0; + ctrls[0] = 0; + masks[1] = YT921X_VLAN_CTRLb_STP_ID_M; + ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti); mutex_lock(&priv->reg_lock); res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid), - mask64, ctrl64); + masks, ctrls); mutex_unlock(&priv->reg_lock); return res; @@ -2992,6 +4438,7 @@ static int yt921x_chip_detect(struct yt921x_priv *priv) u32 chipid; u32 major; u32 mode; + u32 val; int res; res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); @@ -3026,12 +4473,27 @@ static int yt921x_chip_detect(struct yt921x_priv *priv) return -ENODEV; } + res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val); + if (res) + return res; + switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) { + case 0: + priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6; + break; + case YT921X_SYS_CLK_143M: + priv->cycle_ns = 7; + break; + default: + priv->cycle_ns = 8; + } + /* Print chipid here since we are interested in lower 16 bits */ dev_info(dev, "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n", info->name, chipid, mode, extmode); priv->info = info; + return 0; } @@ -3083,7 +4545,7 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv) { struct dsa_switch *ds = &priv->ds; unsigned long cpu_ports_mask; - u64 ctrl64; + u32 ctrls[2]; u32 ctrl; int port; int res; @@ -3144,8 +4606,51 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv) /* Tagged VID 0 should be treated as untagged, which confuses the * hardware a lot */ - ctrl64 = YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M; - res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64); + ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M; + ctrls[1] = 0; + res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls); + if (res) + return res; + + return 0; +} + +static int yt921x_chip_setup_tc(struct yt921x_priv *priv) +{ + unsigned int op_ns; + u32 ctrl; + int res; + + op_ns = 8 * priv->cycle_ns; + + ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN); + res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl); + if (res) + return res; + priv->meter_slot_ns = ctrl * op_ns; + + ctrl = max(priv->port_shape_slot_ns / op_ns, + YT921X_PORT_SHAPE_SLOT_MIN); + res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl); + if (res) + return res; + priv->port_shape_slot_ns = ctrl * op_ns; + + return 0; +} + +static int yt921x_chip_setup_acl(struct yt921x_priv *priv) +{ + u32 ctrl; + int res; + + ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M; + res = yt921x_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl); + if (res) + return res; + + ctrl = YT921X_ACL_PORT_PORTS_M; + res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl); if (res) return res; @@ -3198,7 +4703,7 @@ static int yt921x_chip_setup(struct yt921x_priv *priv) u32 ctrl; int res; - ctrl = YT921X_FUNC_MIB; + ctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER; res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl); if (res) return res; @@ -3207,6 +4712,14 @@ static int yt921x_chip_setup(struct yt921x_priv *priv) if (res) return res; + res = yt921x_chip_setup_tc(priv); + if (res) + return res; + + res = yt921x_chip_setup_acl(priv); + if (res) + return res; + #if IS_ENABLED(CONFIG_DCB) res = yt921x_chip_setup_qos(priv); if (res) @@ -3298,6 +4811,13 @@ static const struct dsa_switch_ops yt921x_dsa_switch_ops = { /* mtu */ .port_change_mtu = yt921x_dsa_port_change_mtu, .port_max_mtu = yt921x_dsa_port_max_mtu, + /* rate */ + .port_policer_del = yt921x_dsa_port_policer_del, + .port_policer_add = yt921x_dsa_port_policer_add, + .port_setup_tc = yt921x_dsa_port_setup_tc, + /* acl */ + .cls_flower_del = yt921x_dsa_cls_flower_del, + .cls_flower_add = yt921x_dsa_cls_flower_add, /* hsr */ .port_hsr_leave = dsa_port_simple_hsr_leave, .port_hsr_join = dsa_port_simple_hsr_join, @@ -3374,6 +4894,20 @@ static void yt921x_mdio_remove(struct mdio_device *mdiodev) dsa_unregister_switch(&priv->ds); + for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) { + struct yt921x_acl_blk *aclblk = priv->acl_blks[i]; + + if (!aclblk) + continue; + for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) { + struct yt921x_acl_rule *aclrule = aclblk->rules[j]; + + if (!aclrule) + continue; + kvfree(aclrule); + } + kvfree(aclblk); + } mutex_destroy(&priv->reg_lock); } diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h index 3f129b8d403f..555046526669 100644 --- a/drivers/net/dsa/yt921x.h +++ b/drivers/net/dsa/yt921x.h @@ -23,6 +23,8 @@ #define YT921X_RST_HW BIT(31) #define YT921X_RST_SW BIT(1) #define YT921X_FUNC 0x80004 +#define YT921X_FUNC_METER BIT(4) +#define YT921X_FUNC_ACL BIT(2) #define YT921X_FUNC_MIB BIT(1) #define YT921X_CHIP_ID 0x80008 #define YT921X_CHIP_ID_MAJOR GENMASK(31, 16) @@ -239,6 +241,11 @@ #define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0) #define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x)) #define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3) +#define YT921X_SYS_CLK 0xe0040 +#define YT921X_SYS_CLK_SEL_M GENMASK(1, 0) /* unknown: 167M */ +#define YT9215_SYS_CLK_125M 0 +#define YT9218_SYS_CLK_167M 0 +#define YT921X_SYS_CLK_143M 1 #define YT921X_EXT_MBUS_OP 0x6a000 #define YT921X_INT_MBUS_OP 0xf0000 @@ -414,6 +421,10 @@ enum yt921x_app_selector { #define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2) #define YT921X_CPU_COPY_TO_INT_CPU BIT(1) #define YT921X_CPU_COPY_TO_EXT_CPU BIT(0) +#define YT921X_ACL_PERMIT_UNMATCH 0x1806a0 +#define YT921X_ACL_PERMIT_UNMATCH_PORTS_M GENMASK(10, 0) +#define YT921X_ACL_PERMIT_UNMATCH_PORTS(x) FIELD_PREP(YT921X_ACL_PERMIT_UNMATCH_PORTS_M, (x)) +#define YT921X_ACL_PERMIT_UNMATCH_PORTn(port) BIT(port) #define YT921X_ACT_UNK_UCAST 0x180734 #define YT921X_ACT_UNK_MCAST 0x180738 #define YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA BIT(23) @@ -429,24 +440,267 @@ enum yt921x_app_selector { #define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0) #define YT921X_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan)) -#define YT921X_VLAN_CTRL_UNTAG_PORTS_M GENMASK_ULL(50, 40) -#define YT921X_VLAN_CTRL_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_UNTAG_PORTS_M, (x)) -#define YT921X_VLAN_CTRL_UNTAG_PORTn(port) BIT_ULL((port) + 40) -#define YT921X_VLAN_CTRL_STP_ID_M GENMASK_ULL(39, 36) -#define YT921X_VLAN_CTRL_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRL_STP_ID_M, (x)) -#define YT921X_VLAN_CTRL_SVLAN_EN BIT_ULL(35) -#define YT921X_VLAN_CTRL_FID_M GENMASK_ULL(34, 23) -#define YT921X_VLAN_CTRL_FID(x) FIELD_PREP(YT921X_VLAN_CTRL_FID_M, (x)) -#define YT921X_VLAN_CTRL_LEARN_DIS BIT_ULL(22) -#define YT921X_VLAN_CTRL_PRIO_EN BIT_ULL(21) -#define YT921X_VLAN_CTRL_PRIO_M GENMASK_ULL(20, 18) -#define YT921X_VLAN_CTRL_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRL_PRIO_M, (x)) -#define YT921X_VLAN_CTRL_PORTS_M GENMASK_ULL(17, 7) -#define YT921X_VLAN_CTRL_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_PORTS_M, (x)) -#define YT921X_VLAN_CTRL_PORTn(port) BIT_ULL((port) + 7) -#define YT921X_VLAN_CTRL_BYPASS_1X_AC BIT_ULL(6) -#define YT921X_VLAN_CTRL_METER_EN BIT_ULL(5) -#define YT921X_VLAN_CTRL_METER_ID_M GENMASK_ULL(4, 0) +#define YT921X_VLAN_CTRLb_UNTAG_PORTS_M GENMASK(18, 8) +#define YT921X_VLAN_CTRLb_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLb_UNTAG_PORTS_M, (x)) +#define YT921X_VLAN_CTRLb_UNTAG_PORTn(port) BIT((port) + 8) +#define YT921X_VLAN_CTRLb_STP_ID_M GENMASK(7, 4) +#define YT921X_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRLb_STP_ID_M, (x)) +#define YT921X_VLAN_CTRLb_SVLAN_EN BIT(3) +#define YT921X_VLAN_CTRLab_FID_M GENMASK_ULL(34, 23) +#define YT921X_VLAN_CTRLab_FID(x) FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x)) +#define YT921X_VLAN_CTRLa_LEARN_DIS BIT(22) +#define YT921X_VLAN_CTRLa_PRIO_EN BIT(21) +#define YT921X_VLAN_CTRLa_PRIO_M GENMASK(20, 18) +#define YT921X_VLAN_CTRLa_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRLa_PRIO_M, (x)) +#define YT921X_VLAN_CTRLa_PORTS_M GENMASK(17, 7) +#define YT921X_VLAN_CTRLa_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLa_PORTS_M, (x)) +#define YT921X_VLAN_CTRLa_PORTn(port) BIT((port) + 7) +#define YT921X_VLAN_CTRLa_BYPASS_1X_AC BIT(6) +#define YT921X_VLAN_CTRLa_METER_EN BIT(5) +#define YT921X_VLAN_CTRLa_METER_ID_M GENMASK(4, 0) + +#define YT921X_ACLn_ACT(n) (0x1c0000 + 0x10 * (n)) +#define YT921X_ACL_ACTc_STAG_M GENMASK(26, 25) +#define YT921X_ACL_ACTc_STAG(x) FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x)) +#define YT921X_ACL_ACTc_STAG_DONTCARE YT921X_ACL_ACTc_STAG(0) +#define YT921X_ACL_ACTc_STAG_UNTAG YT921X_ACL_ACTc_STAG(1) +#define YT921X_ACL_ACTc_STAG_TAG YT921X_ACL_ACTc_STAG(2) +#define YT921X_ACL_ACTc_STAG_KEEP YT921X_ACL_ACTc_STAG(3) +#define YT921X_ACL_ACTc_CTAG_M GENMASK(24, 23) +#define YT921X_ACL_ACTc_CTAG(x) FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x)) +#define YT921X_ACL_ACTc_CTAG_DONTCARE YT921X_ACL_ACTc_CTAG(0) +#define YT921X_ACL_ACTc_CTAG_UNTAG YT921X_ACL_ACTc_CTAG(1) +#define YT921X_ACL_ACTc_CTAG_TAG YT921X_ACL_ACTc_CTAG(2) +#define YT921X_ACL_ACTc_CTAG_KEEP YT921X_ACL_ACTc_CTAG(3) +#define YT921X_ACL_ACTc_FWD_M GENMASK(22, 21) +#define YT921X_ACL_ACTc_FWD(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x)) +#define YT921X_ACL_ACTc_FWD_FWD YT921X_ACL_ACTc_FWD(0) +#define YT921X_ACL_ACTc_FWD_COPY YT921X_ACL_ACTc_FWD(1) +#define YT921X_ACL_ACTc_FWD_REDIR YT921X_ACL_ACTc_FWD(2) +#define YT921X_ACL_ACTc_FWD_TRAP YT921X_ACL_ACTc_FWD(3) +#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M GENMASK(20, 10) +#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x)) +#define YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port) BIT((port) + 10) +#define YT921X_ACL_ACTc_FWD_EN BIT(9) +#define YT921X_ACL_ACTc_SDEI BIT(8) +#define YT921X_ACL_ACTc_SDEI_REPLACE BIT(7) +#define YT921X_ACL_ACTc_SPRI_M GENMASK(6, 4) +#define YT921X_ACL_ACTc_SPRI(x) FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x)) +#define YT921X_ACL_ACTc_SPRI_REPLACE BIT(3) +#define YT921X_ACL_ACTbc_SVID_M GENMASK_ULL(34, 23) +#define YT921X_ACL_ACTbc_SVID(x) FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x)) +#define YT921X_ACL_ACTb_SVID_REPLACE BIT(22) +#define YT921X_ACL_ACTb_CDEI BIT(21) +#define YT921X_ACL_ACTb_CDEI_REPLACE BIT(20) +#define YT921X_ACL_ACTb_CPRI_M GENMASK(19, 17) +#define YT921X_ACL_ACTb_CPRI(x) FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x)) +#define YT921X_ACL_ACTb_CPRI_REPLACE BIT(16) +#define YT921X_ACL_ACTb_CVID_M GENMASK(15, 4) +#define YT921X_ACL_ACTb_CVID(x) FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x)) +#define YT921X_ACL_ACTb_CVID_REPLACE BIT(3) +#define YT921X_ACL_ACTb_PRIO_M GENMASK(2, 0) +#define YT921X_ACL_ACTb_PRIO(x) FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x)) +#define YT921X_ACL_ACTa_PRIO_EN BIT(31) +#define YT921X_ACL_ACTa_COLOR_M GENMASK(30, 29) +#define YT921X_ACL_ACTa_COLOR(x) FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x)) +#define YT921X_ACL_ACTa_COLOR_GREEN YT921X_ACL_ACTa_COLOR(0) +#define YT921X_ACL_ACTa_COLOR_YELLOW YT921X_ACL_ACTa_COLOR(1) +#define YT921X_ACL_ACTa_COLOR_RED YT921X_ACL_ACTa_COLOR(2) +#define YT921X_ACL_ACTa_COLOR_EN BIT(28) +#define YT921X_ACL_ACTa_DSCP_M GENMASK(27, 22) +#define YT921X_ACL_ACTa_DSCP(x) FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x)) +#define YT921X_ACL_ACTa_DSCP_REPLACE BIT(21) +#define YT921X_ACL_ACTa_METER_ID_M GENMASK(20, 15) +#define YT921X_ACL_ACTa_METER_ID(x) FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x)) +#define YT921X_ACL_ACTa_METER_EN BIT(14) +#define YT921X_ACL_ACTa_MIRROR_EN BIT(13) +#define YT921X_ACL_ACTa_FLOWSTAT_EN BIT(12) +#define YT921X_ACL_ACTa_FLOWSTAT_ID_M GENMASK(11, 6) +#define YT921X_ACL_ACTa_FLOWSTAT_ID(x) FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x)) +#define YT921X_ACL_ACTa_GPIO_EN BIT(5) +#define YT921X_ACL_ACTa_GPIO_PIN_M GENMASK(4, 1) +#define YT921X_ACL_ACTa_GPIO_PIN(x) FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x)) +#define YT921X_ACL_ACTa_INTR_EN BIT(0) +#define YT921X_ACL_BLK_KEEP 0x201000 +#define YT921X_ACL_BLK_KEEP_GRPIDn_M(bin) (7 << (4 * (bin) + 1)) +#define YT921X_ACL_BLK_KEEP_GRPIDn(bin, x) ((x) << (4 * (bin) + 1)) +#define YT921X_ACL_BLK_KEEP_KEEPn(bin) BIT(4 * (bin)) +#define YT921X_ACL_PORT 0x202000 +#define YT921X_ACL_PORT_PORTS_M GENMASK(10, 0) +#define YT921X_ACL_PORT_PORTS(x) FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x)) +#define YT921X_ACL_PORT_PORTn(port) BIT(port) +#define YT921X_ACL_BLK_CMD 0x202004 +#define YT921X_ACL_BLK_CMD_BLKID_M GENMASK(6, 1) +#define YT921X_ACL_BLK_CMD_BLKID(x) FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x)) +#define YT921X_ACL_BLK_CMD_MODIFY BIT(0) +#define YT921X_ACLn_ENTRY(blk) (0x203000 + 4 * (blk)) +#define YT921X_ACL_ENTRY_GRPIDm_M(bin) (7 << (4 * (bin) + 1)) +#define YT921X_ACL_ENTRY_GRPIDm(bin, x) ((x) << (4 * (bin) + 1)) +#define YT921X_ACL_ENTRY_ENm(bin) BIT(4 * (bin)) +#define YT921X_ACLn_KEYm(blk, bin) (0x204000 + 0x200 * (bin) + 8 * (blk)) +#define YT921X_ACL_KEYb_ORD_M GENMASK(29, 21) +#define YT921X_ACL_KEYb_ORD(x) FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x)) +#define YT921X_ACL_KEYb_SPORTS_M GENMASK(20, 10) +#define YT921X_ACL_KEYb_SPORTS(x) FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x)) +#define YT921X_ACL_KEYb_SPORTn(port) BIT((port) + 10) +#define YT921X_ACL_KEYb_REVERSE BIT(9) /* reverse match */ +#define YT921X_ACL_KEYb_TYPE_M GENMASK(8, 4) +#define YT921X_ACL_KEYb_TYPE(x) FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x)) +/* KEY_* fields need no masks */ +#define YT921X_ACLn_MASKm(blk, bin) (0x205000 + 0x200 * (bin) + 8 * (blk)) + +enum yt921x_acl_type { + YT921X_ACL_TYPE_NA, + YT921X_ACL_TYPE_MAC_DA0, + YT921X_ACL_TYPE_MAC_SA0, + YT921X_ACL_TYPE_MAC_DA1_SA1, + YT921X_ACL_TYPE_VLAN, + YT921X_ACL_TYPE_VTAG, + YT921X_ACL_TYPE_IPV4_DA, + YT921X_ACL_TYPE_IPV4_SA, + YT921X_ACL_TYPE_IPV6_DA0, + YT921X_ACL_TYPE_IPV6_DA1, + YT921X_ACL_TYPE_IPV6_DA2, + YT921X_ACL_TYPE_IPV6_DA3, + YT921X_ACL_TYPE_IPV6_SA0, + YT921X_ACL_TYPE_IPV6_SA1, + YT921X_ACL_TYPE_IPV6_SA2, + YT921X_ACL_TYPE_IPV6_SA3, + YT921X_ACL_TYPE_MISC, + YT921X_ACL_TYPE_L4, + YT921X_ACL_TYPE_UDF0, + YT921X_ACL_TYPE_UDF1, + YT921X_ACL_TYPE_UDF2, + YT921X_ACL_TYPE_UDF3, + YT921X_ACL_TYPE_UDF4, + YT921X_ACL_TYPE_UDF5, + YT921X_ACL_TYPE_UDF6, + YT921X_ACL_TYPE_UDF7, + YT921X_ACL_TYPE_ETHERTYPE, + YT921X_ACL_TYPE_NUM +}; + +/* Range: turn KEY:MASK into MIN:MAX */ + +#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M GENMASK(3, 0) +#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x)) +#define YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M GENMASK(31, 0) + +#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x)) +#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M GENMASK(31, 16) +#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M GENMASK(15, 0) + +#define YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN BIT(31) +#define YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_VLAN_CDEI BIT(3) +#define YT921X_ACL_BINb_VLAN_CPRI_M GENMASK(2, 0) +#define YT921X_ACL_BINb_VLAN_CPRI(x) FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x)) +#define YT921X_ACL_BINa_VLAN_CTAG_FMT_M GENMASK(31, 30) +#define YT921X_ACL_BINa_VLAN_CTAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x)) +#define YT921X_ACL_BINa_VLAN_SDEI BIT(29) +#define YT921X_ACL_BINa_VLAN_SPRI_M GENMASK(28, 26) +#define YT921X_ACL_BINa_VLAN_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x)) +#define YT921X_ACL_BINa_VLAN_STAG_FMT_M GENMASK(25, 24) +#define YT921X_ACL_BINa_VLAN_STAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x)) +#define YT921X_ACL_BINa_VLAN_SVID_M GENMASK(23, 12) +#define YT921X_ACL_BINa_VLAN_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x)) +#define YT921X_ACL_BINa_VLAN_CVID_M GENMASK(11, 0) +#define YT921X_ACL_BINa_VLAN_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x)) + +#define YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN BIT(31) +#define YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN BIT(30) +#define YT921X_ACL_BINa_VTAG_CDEI BIT(31) +#define YT921X_ACL_BINa_VTAG_CPRI_M GENMASK(30, 28) +#define YT921X_ACL_BINa_VTAG_CPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x)) +#define YT921X_ACL_BINa_VTAG_SDEI BIT(27) +#define YT921X_ACL_BINa_VTAG_SPRI_M GENMASK(26, 24) +#define YT921X_ACL_BINa_VTAG_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x)) +#define YT921X_ACL_BINa_VTAG_SVID_M GENMASK(23, 12) +#define YT921X_ACL_BINa_VTAG_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x)) +#define YT921X_ACL_BINa_VTAG_CVID_M GENMASK(11, 0) +#define YT921X_ACL_BINa_VTAG_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x)) + +#define YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_IPV4_FRAG BIT(3) +#define YT921X_ACL_BINb_IPV4_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_IPV4_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_IPV4_ADDR_M GENMASK(31, 0) + +#define YT921X_ACL_BINb_IPV6_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_IPV6_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_IPV6_ADDRx_M GENMASK(31, 0) + +#define YT921X_ACL_BINb_IPV6_xA1_IP_OPTION BIT(3) + +#define YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG BIT(3) + +#define YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_IPV6_xA3_FRAG BIT(3) + +#define YT921X_ACL_BINb_MISC_FRAG BIT(3) +#define YT921X_ACL_BINb_MISC_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_MISC_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_MISC_PPPOE_FLAG BIT(30) +#define YT921X_ACL_BINa_MISC_FIRST_FRAG BIT(29) +#define YT921X_ACL_BINa_MISC_IP_OPTION BIT(28) +#define YT921X_ACL_BINa_MISC_TCP_FLAGS_M GENMASK(27, 20) +#define YT921X_ACL_BINa_MISC_TCP_FLAGS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x)) +#define YT921X_ACL_BINa_MISC_IP_PROTO_M GENMASK(19, 12) +#define YT921X_ACL_BINa_MISC_IP_PROTO(x) FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x)) +#define YT921X_ACL_BINa_MISC_TOS_M GENMASK(11, 4) +#define YT921X_ACL_BINa_MISC_TOS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x)) +#define YT921X_ACL_BINa_MISC_L3_TYPE_M GENMASK(3, 0) +#define YT921X_ACL_BINa_MISC_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x)) + +#define YT921X_ACL_KEYb_L4_DPORT_RANGE_EN BIT(31) +#define YT921X_ACL_KEYb_L4_SPORT_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_L4_FRAG BIT(3) +#define YT921X_ACL_BINb_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_L4_DPORT_M GENMASK(31, 16) +#define YT921X_ACL_BINa_L4_SPORT_M GENMASK(15, 0) + +#define YT921X_ACL_BINb_UDF_IS_IGMP BIT(0) +#define YT921X_ACL_BINa_UDF_UDF0_M GENMASK(31, 16) +#define YT921X_ACL_BINa_UDF_UDF0(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x)) +#define YT921X_ACL_BINa_UDF_UDF1_M GENMASK(15, 0) +#define YT921X_ACL_BINa_UDF_UDF1(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x)) + +#define YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M GENMASK(15, 0) +#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x) FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x)) + +enum yt921x_l2_type { + YT921X_L2_TYPE_ETH, + YT921X_L2_TYPE_ETHV2, + YT921X_L2_TYPE_ETHSAP, + YT921X_L2_TYPE_ETHSNAP, +}; + +enum yt921x_l3_type { + YT921X_L3_TYPE_OTHER, + YT921X_L3_TYPE_IPV4, + YT921X_L3_TYPE_IPV6, + YT921X_L3_TYPE_ARP, + YT921X_L3_TYPE_LLDP, + YT921X_L3_TYPE_PAE, + YT921X_L3_TYPE_ERP, + YT921X_L3_TYPE_SLOW_PROTOCOL, +}; + +enum yt921x_l4_type { + YT921X_L4_TYPE_OTHER, + YT921X_L4_TYPE_TCP, + YT921X_L4_TYPE_UDP, + YT921X_L4_TYPE_UDPLITE, + YT921X_L4_TYPE_ICMP, + YT921X_L4_TYPE_IGMP, + YT921X_L4_TYPE_MLD, + YT921X_L4_TYPE_ND, +}; #define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */ #define YT921X_TPID_IGR_TPID_M GENMASK(15, 0) @@ -464,6 +718,47 @@ enum yt921x_app_selector { #define YT921X_LAG_HASH_MAC_SA BIT(2) #define YT921X_LAG_HASH_MAC_DA BIT(1) #define YT921X_LAG_HASH_SRC_PORT BIT(0) +#define YT921X_UDFn_CTRL(x) (0x210094 + 4 * (x)) +#define YT921X_UDF_CTRL_UDF_TYPE_M GENMASK(8, 7) +#define YT921X_UDF_CTRL_UDF_TYPE(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x)) +#define YT921X_UDF_CTRL_UDF_TYPE_ETH YT921X_UDF_CTRL_UDF_TYPE(0) +#define YT921X_UDF_CTRL_UDF_TYPE_L3 YT921X_UDF_CTRL_UDF_TYPE(1) +#define YT921X_UDF_CTRL_UDF_TYPE_L4 YT921X_UDF_CTRL_UDF_TYPE(2) +#define YT921X_UDF_CTRL_UDF_OFFSET_M GENMASK(6, 0) +#define YT921X_UDF_CTRL_UDF_OFFSET(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x)) + +#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port)) +#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */ +#define YT921X_METER_SLOT 0x220104 +#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0) +#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port)) +#define YT921X_PORT_METER_EN BIT(4) +#define YT921X_PORT_METER_ID_M GENMASK(3, 0) +#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x)) +#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x)) +#define YT921X_METER_CTRLc_METER_EN BIT(14) +#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */ +#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11) +#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x)) +#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0) +#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1) +#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2) +#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3) +#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10) +#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7) +#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x)) +#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP BIT(6) /* +GAP_VALUE bytes each packet */ +#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */ +#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */ +#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20) +#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x)) +#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2) +#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x)) +#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18) +#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x)) +#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0) +#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x)) +#define YT921X_METERn_STAT(x) (0x221000 + 8 * (x)) #define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port)) #define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN BIT(31) @@ -492,6 +787,19 @@ enum yt921x_app_selector { #define YT921X_MIRROR_PORT_M GENMASK(3, 0) #define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x)) +#define YT921X_PORT_SHAPE_SLOT 0x34000c +#define YT921X_PORT_SHAPE_SLOT_SLOT_M GENMASK(11, 0) +#define YT921X_PORTn_SHAPE_CTRL(port) (0x354000 + 8 * (port)) +#define YT921X_PORT_SHAPE_CTRLb_EN BIT(4) +#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE BIT(3) /* 0: byte rate mode */ +#define YT921X_PORT_SHAPE_CTRLb_UNIT_M GENMASK(2, 0) +#define YT921X_PORT_SHAPE_CTRLb_UNIT(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x)) +#define YT921X_PORT_SHAPE_CTRLa_CBS_M GENMASK(31, 18) +#define YT921X_PORT_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x)) +#define YT921X_PORT_SHAPE_CTRLa_CIR_M GENMASK(17, 0) +#define YT921X_PORT_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x)) +#define YT921X_PORTn_SHAPE_STAT(port) (0x356000 + 4 * (port)) + #define YT921X_EDATA_EXTMODE 0xfb #define YT921X_EDATA_LEN 0x100 @@ -508,6 +816,20 @@ enum yt921x_fdb_entry_status { #define YT921X_MSTI_NUM 16 +#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */ +#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */ +#define YT921X_TOKEN_RATE_C -15 +/* Custom meters only, not including dedicated port meters (11) */ +#define YT921X_METER_NUM 64 +#define YT921X_METER_SLOT_MIN 80 +#define YT921X_METER_UNIT_MAX ((1 << 3) - 1) +#define YT921X_METER_CIR_MAX ((1 << 18) - 1) +#define YT921X_METER_CBS_MAX ((1 << 16) - 1) +#define YT921X_PORT_SHAPE_SLOT_MIN 80 +#define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1) +#define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1) +#define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1) + #define YT921X_LAG_NUM 2 #define YT921X_LAG_PORT_NUM 4 @@ -523,6 +845,11 @@ enum yt921x_fdb_entry_status { #define YT921X_TAG_LEN 8 +#define YT921X_ACL_BLK_NUM 48 +#define YT921X_ACL_ENT_PER_BLK 8 +#define YT921X_ACL_NUM (YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK) +#define YT921X_UDF_NUM 8 + /* 8 internal + 2 external + 1 mcu */ #define YT921X_PORT_NUM 11 @@ -581,6 +908,26 @@ struct yt921x_mib { u64 tx_oam; }; +struct yt921x_acl_entry { + u32 key[2]; + u32 mask[2]; +}; + +struct yt921x_acl_rule { + unsigned long tag; + enum tc_setup_type type; + + u32 action[3]; + bool sw_assisted; + + u8 mask; + struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK]; +}; + +struct yt921x_acl_blk { + struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK]; +}; + struct yt921x_port { unsigned char index; @@ -602,8 +949,11 @@ struct yt921x_priv { struct dsa_switch ds; const struct yt921x_info *info; + unsigned int meter_slot_ns; + unsigned int port_shape_slot_ns; /* cache of dsa_cpu_ports(ds) */ u16 cpu_ports_mask; + unsigned char cycle_ns; /* protect the access to the switch registers */ struct mutex reg_lock; @@ -617,6 +967,11 @@ struct yt921x_priv { struct yt921x_port ports[YT921X_PORT_NUM]; u16 eee_ports_mask; + + DECLARE_BITMAP(meters_map, YT921X_METER_NUM); + + u8 acl_masks[YT921X_ACL_BLK_NUM]; + struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM]; }; #endif diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index f8a4eb365c3d..36a4e85c6668 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -47,10 +47,11 @@ static int numdummies = 1; /* fake multicast ability */ -static void set_multicast_list(struct net_device *dev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int set_multicast_list(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { + return 0; } static void dummy_get_stats64(struct net_device *dev, @@ -158,7 +159,7 @@ static int __init dummy_init_one(void) return -ENOMEM; dev_dummy->rtnl_link_ops = &dummy_link_ops; - err = register_netdevice(dev_dummy); + err = register_netdev(dev_dummy); if (err < 0) goto err; return 0; @@ -176,15 +177,11 @@ static int __init dummy_init_module(void) if (err < 0) return err; - rtnl_net_lock(&init_net); - for (i = 0; i < numdummies && !err; i++) { err = dummy_init_one(); cond_resched(); } - rtnl_net_unlock(&init_net); - if (err < 0) rtnl_link_unregister(&dummy_link_ops); diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index f23be7425daf..3438168befa6 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -438,7 +438,7 @@ static const struct pnp_device_id el3_pnp_ids[] = { { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */ { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */ { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */ - { .id = "" } + { } }; MODULE_DEVICE_TABLE(pnp, el3_pnp_ids); diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 4fe4efdb3737..79beb86437df 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -376,54 +376,54 @@ static struct vortex_chip_info { static const struct pci_device_id vortex_pci_tbl[] = { - { 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C590 }, - { 0x10B7, 0x5920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C592 }, - { 0x10B7, 0x5970, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C597 }, - { 0x10B7, 0x5950, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_1 }, - { 0x10B7, 0x5951, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_2 }, + { PCI_DEVICE(0x10B7, 0x5900), .driver_data = CH_3C590 }, + { PCI_DEVICE(0x10B7, 0x5920), .driver_data = CH_3C592 }, + { PCI_DEVICE(0x10B7, 0x5970), .driver_data = CH_3C597 }, + { PCI_DEVICE(0x10B7, 0x5950), .driver_data = CH_3C595_1 }, + { PCI_DEVICE(0x10B7, 0x5951), .driver_data = CH_3C595_2 }, - { 0x10B7, 0x5952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_3 }, - { 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_1 }, - { 0x10B7, 0x9001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_2 }, - { 0x10B7, 0x9004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_3 }, - { 0x10B7, 0x9005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_4 }, + { PCI_DEVICE(0x10B7, 0x5952), .driver_data = CH_3C595_3 }, + { PCI_DEVICE(0x10B7, 0x9000), .driver_data = CH_3C900_1 }, + { PCI_DEVICE(0x10B7, 0x9001), .driver_data = CH_3C900_2 }, + { PCI_DEVICE(0x10B7, 0x9004), .driver_data = CH_3C900_3 }, + { PCI_DEVICE(0x10B7, 0x9005), .driver_data = CH_3C900_4 }, - { 0x10B7, 0x9006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_5 }, - { 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL }, - { 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 }, - { 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 }, - { 0x10B7, 0x9054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_TX }, - { 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 }, + { PCI_DEVICE(0x10B7, 0x9006), .driver_data = CH_3C900_5 }, + { PCI_DEVICE(0x10B7, 0x900A), .driver_data = CH_3C900B_FL }, + { PCI_DEVICE(0x10B7, 0x9050), .driver_data = CH_3C905_1 }, + { PCI_DEVICE(0x10B7, 0x9051), .driver_data = CH_3C905_2 }, + { PCI_DEVICE(0x10B7, 0x9054), .driver_data = CH_3C905B_TX }, + { PCI_DEVICE(0x10B7, 0x9055), .driver_data = CH_3C905B_1 }, - { 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 }, - { 0x10B7, 0x905A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_FX }, - { 0x10B7, 0x9200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C }, - { 0x10B7, 0x9202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C9202 }, - { 0x10B7, 0x9800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C980 }, - { 0x10B7, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C9805 }, + { PCI_DEVICE(0x10B7, 0x9058), .driver_data = CH_3C905B_2 }, + { PCI_DEVICE(0x10B7, 0x905A), .driver_data = CH_3C905B_FX }, + { PCI_DEVICE(0x10B7, 0x9200), .driver_data = CH_3C905C }, + { PCI_DEVICE(0x10B7, 0x9202), .driver_data = CH_3C9202 }, + { PCI_DEVICE(0x10B7, 0x9800), .driver_data = CH_3C980 }, + { PCI_DEVICE(0x10B7, 0x9805), .driver_data = CH_3C9805 }, - { 0x10B7, 0x7646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CSOHO100_TX }, - { 0x10B7, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C555 }, - { 0x10B7, 0x6055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C556 }, - { 0x10B7, 0x6056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C556B }, - { 0x10B7, 0x5b57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575 }, + { PCI_DEVICE(0x10B7, 0x7646), .driver_data = CH_3CSOHO100_TX }, + { PCI_DEVICE(0x10B7, 0x5055), .driver_data = CH_3C555 }, + { PCI_DEVICE(0x10B7, 0x6055), .driver_data = CH_3C556 }, + { PCI_DEVICE(0x10B7, 0x6056), .driver_data = CH_3C556B }, + { PCI_DEVICE(0x10B7, 0x5b57), .driver_data = CH_3C575 }, - { 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_1 }, - { 0x10B7, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575 }, - { 0x10B7, 0x5257, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575CT }, - { 0x10B7, 0x6560, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE656 }, - { 0x10B7, 0x6562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656 }, + { PCI_DEVICE(0x10B7, 0x5057), .driver_data = CH_3C575_1 }, + { PCI_DEVICE(0x10B7, 0x5157), .driver_data = CH_3CCFE575 }, + { PCI_DEVICE(0x10B7, 0x5257), .driver_data = CH_3CCFE575CT }, + { PCI_DEVICE(0x10B7, 0x6560), .driver_data = CH_3CCFE656 }, + { PCI_DEVICE(0x10B7, 0x6562), .driver_data = CH_3CCFEM656 }, - { 0x10B7, 0x6564, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656_1 }, - { 0x10B7, 0x4500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C450 }, - { 0x10B7, 0x9201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C920 }, - { 0x10B7, 0x1201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C982A }, - { 0x10B7, 0x1202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C982B }, + { PCI_DEVICE(0x10B7, 0x6564), .driver_data = CH_3CCFEM656_1 }, + { PCI_DEVICE(0x10B7, 0x4500), .driver_data = CH_3C450 }, + { PCI_DEVICE(0x10B7, 0x9201), .driver_data = CH_3C920 }, + { PCI_DEVICE(0x10B7, 0x1201), .driver_data = CH_3C982A }, + { PCI_DEVICE(0x10B7, 0x1202), .driver_data = CH_3C982B }, - { 0x10B7, 0x9056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_905BT4 }, - { 0x10B7, 0x9210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_920B_EMB_WNM }, + { PCI_DEVICE(0x10B7, 0x9056), .driver_data = CH_905BT4 }, + { PCI_DEVICE(0x10B7, 0x9210), .driver_data = CH_920B_EMB_WNM }, - {0,} /* 0 terminated list. */ + { } /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, vortex_pci_tbl); diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index aaaff3ba43ef..fadc86907ec2 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -198,33 +198,54 @@ static struct typhoon_card_info typhoon_card_info[] = { * bits 12-16 indicate card type: (0) client and (1) server */ static const struct pci_device_id typhoon_pci_tbl[] = { - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990, - PCI_ANY_ID, PCI_ANY_ID, 0, 0,TYPHOON_TX }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_95, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX95 }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_97, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX97 }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B, - PCI_ANY_ID, 0x1000, 0, 0, TYPHOON_TXM }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B, - PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FXM }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B, - PCI_ANY_ID, 0x2000, 0, 0, TYPHOON_BSVR }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX, - PCI_ANY_ID, 0x1101, 0, 0, TYPHOON_FX95 }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX, - PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FX97 }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX, - PCI_ANY_ID, 0x2101, 0, 0, TYPHOON_FX95SVR }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX, - PCI_ANY_ID, 0x2102, 0, 0, TYPHOON_FX97SVR }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR95, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR95 }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR97, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR97 }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR }, - { 0, } + { + PCI_VDEVICE(3COM, PCI_DEVICE_ID_3COM_3CR990), + .driver_data = TYPHOON_TX, + }, { + PCI_VDEVICE(3COM, PCI_DEVICE_ID_3COM_3CR990_TX_95), + .driver_data = TYPHOON_TX95, + }, { + PCI_VDEVICE(3COM, PCI_DEVICE_ID_3COM_3CR990_TX_97), + .driver_data = TYPHOON_TX97, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990B, + PCI_ANY_ID, 0x1000), + .driver_data = TYPHOON_TXM, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990B, + PCI_ANY_ID, 0x1102), + .driver_data = TYPHOON_FXM, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990B, + PCI_ANY_ID, 0x2000), + .driver_data = TYPHOON_BSVR, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990_FX, + PCI_ANY_ID, 0x1101), + .driver_data = TYPHOON_FX95, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990_FX, + PCI_ANY_ID, 0x1102), + .driver_data = TYPHOON_FX97, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990_FX, + PCI_ANY_ID, 0x2101), + .driver_data = TYPHOON_FX95SVR, + }, { + PCI_VDEVICE_SUB(3COM, PCI_DEVICE_ID_3COM_3CR990_FX, + PCI_ANY_ID, 0x2102), + .driver_data = TYPHOON_FX97SVR, + }, { + PCI_VDEVICE(3COM, PCI_DEVICE_ID_3COM_3CR990SVR95), + .driver_data = TYPHOON_SVR95, + }, { + PCI_VDEVICE(3COM, PCI_DEVICE_ID_3COM_3CR990SVR97), + .driver_data = TYPHOON_SVR97, + }, { + PCI_VDEVICE(3COM, PCI_DEVICE_ID_3COM_3CR990SVR), + .driver_data = TYPHOON_SVR, + }, + { } }; MODULE_DEVICE_TABLE(pci, typhoon_pci_tbl); diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index 5d12a595ab19..57d73a7747ec 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -90,7 +90,6 @@ config MCF8390 config NE2000 tristate "NE2000/NE1000 support" depends on (ISA || (Q40 && m) || MACH_TX49XX || ATARI_ETHERNEC) - select NETDEV_LEGACY_INIT if ISA select CRC32 help If you have a network (Ethernet) card of this type, say Y here. diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c index fd9dcdc356e6..9a59915436d2 100644 --- a/drivers/net/ethernet/8390/hydra.c +++ b/drivers/net/ethernet/8390/hydra.c @@ -66,8 +66,8 @@ static void hydra_block_output(struct net_device *dev, int count, static void hydra_remove_one(struct zorro_dev *z); static struct zorro_device_id hydra_zorro_tbl[] = { - { ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET }, - { 0 } + { .id = ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET }, + { } }; MODULE_DEVICE_TABLE(zorro, hydra_zorro_tbl); diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c index 961019c32842..1cbd5a4977ce 100644 --- a/drivers/net/ethernet/8390/ne.c +++ b/drivers/net/ethernet/8390/ne.c @@ -50,7 +50,6 @@ static const char version2[] = #include #include #include -#include #include @@ -182,7 +181,6 @@ static void ne_block_input(struct net_device *dev, int count, static void ne_block_output(struct net_device *dev, const int count, const unsigned char *buf, const int start_page); - /* Probe for various non-shared-memory ethercards. NEx000-clone boards have a Station Address PROM (SAPROM) in the packet @@ -918,31 +916,47 @@ static void __init ne_add_devices(void) } } -static int __init ne_init(void) +/* + * This structure holds boot-time configured netdevice settings. They + * are then used in the device probing. + */ +struct netdev_boot_setup { + char name[IFNAMSIZ]; + struct ifmap map; +}; +#define NETDEV_BOOT_SETUP_MAX 8 + +/* Boot time configuration table */ +static struct netdev_boot_setup dev_boot_setup[NETDEV_BOOT_SETUP_MAX]; + +/** + * netdev_boot_setup_check - check boot time settings + * @dev: the netdevice + * + * Check boot time settings for the device. + * The found settings are set for the device to be used + * later in the device probing. + * Returns 0 if no settings found, 1 if they are. + */ +static int netdev_boot_setup_check(struct net_device *dev) { - int retval; + struct netdev_boot_setup *s = dev_boot_setup; + int i; - if (IS_MODULE(CONFIG_NE2000)) - ne_add_devices(); - - retval = platform_driver_probe(&ne_driver, ne_drv_probe); - - if (IS_MODULE(CONFIG_NE2000) && retval) { - if (io[0] == 0) - pr_notice("ne.c: You must supply \"io=0xNNN\"" - " value(s) for ISA cards.\n"); - ne_loop_rm_unreg(1); - return retval; + for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { + if (s[i].name[0] != '\0' && s[i].name[0] != ' ' && + !strcmp(dev->name, s[i].name)) { + dev->irq = s[i].map.irq; + dev->base_addr = s[i].map.base_addr; + dev->mem_start = s[i].map.mem_start; + dev->mem_end = s[i].map.mem_end; + return 1; + } } - - /* Unregister unused platform_devices. */ - ne_loop_rm_unreg(0); - return retval; + return 0; } -module_init(ne_init); -#if !defined(MODULE) && defined(CONFIG_NETDEV_LEGACY_INIT) -struct net_device * __init ne_probe(int unit) +static struct net_device * __init ne_probe(int unit) { int this_dev; struct net_device *dev; @@ -982,8 +996,187 @@ struct net_device * __init ne_probe(int unit) return ERR_PTR(-ENODEV); } + +/****************************************************************************** + * + * Device Boot-time Settings Routines + * + ******************************************************************************/ + +/** + * netdev_boot_base - get address from boot time settings + * @prefix: prefix for network device + * @unit: id for network device + * + * Check boot time settings for the base address of device. + * The found settings are set for the device to be used + * later in the device probing. + * Returns 0 if no settings found. + */ +static unsigned long netdev_boot_base(const char *prefix, int unit) +{ + const struct netdev_boot_setup *s = dev_boot_setup; + char name[IFNAMSIZ]; + int i; + + sprintf(name, "%s%d", prefix, unit); + + /* + * If device already registered then return base of 1 + * to indicate not to probe for this interface + */ + if (__dev_get_by_name(&init_net, name)) + return 1; + + for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) + if (!strcmp(name, s[i].name)) + return s[i].map.base_addr; + return 0; +} + +#if !defined(MODULE) && defined(CONFIG_ISA) +/** + * netdev_boot_setup_add - add new setup entry + * @name: name of the device + * @map: configured settings for the device + * + * Adds new setup entry to the dev_boot_setup list. The function + * returns 0 on error and 1 on success. This is a generic routine to + * all netdevices. + */ +static int netdev_boot_setup_add(char *name, struct ifmap *map) +{ + struct netdev_boot_setup *s; + int i; + + s = dev_boot_setup; + for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) { + if (s[i].name[0] == '\0' || s[i].name[0] == ' ') { + strscpy_pad(s[i].name, name); + memcpy(&s[i].map, map, sizeof(s[i].map)); + break; + } + } + + return i >= NETDEV_BOOT_SETUP_MAX ? 0 : 1; +} + +/* + * Saves at boot time configured settings for any netdevice. + */ +static int __init netdev_boot_setup(char *str) +{ + int ints[5]; + struct ifmap map; + + str = get_options(str, ARRAY_SIZE(ints), ints); + if (!str || !*str) + return 0; + + /* Save settings */ + memset(&map, 0, sizeof(map)); + if (ints[0] > 0) + map.irq = ints[1]; + if (ints[0] > 1) + map.base_addr = ints[2]; + if (ints[0] > 2) + map.mem_start = ints[3]; + if (ints[0] > 3) + map.mem_end = ints[4]; + + /* Add new entry to the list */ + return netdev_boot_setup_add(str, &map); +} + +__setup("netdev=", netdev_boot_setup); + +static int __init ether_boot_setup(char *str) +{ + return netdev_boot_setup(str); +} +__setup("ether=", ether_boot_setup); #endif +/* A unified ethernet device probe. This is the easiest way to have every + * ethernet adaptor have the name "eth[0123...]". + */ + +struct devprobe2 { + struct net_device *(*probe)(int unit); + int status; /* non-zero if autoprobe has failed */ +}; + +static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe) +{ + struct net_device *dev; + + for (; p->probe; p++) { + if (autoprobe && p->status) + continue; + dev = p->probe(unit); + if (!IS_ERR(dev)) + return 0; + if (autoprobe) + p->status = PTR_ERR(dev); + } + return -ENODEV; +} + +static struct devprobe2 isa_probes[] __initdata = { + {ne_probe, 0}, + {NULL, 0}, +}; + +/* Unified ethernet device probe, segmented per architecture and + * per bus interface. This drives the legacy devices only for now. + */ + +static void __init ethif_probe2(int unit) +{ + unsigned long base_addr = netdev_boot_base("eth", unit); + + if (base_addr == 1) + return; + + probe_list2(unit, isa_probes, base_addr == 0); +} + +/* Statically configured drivers -- order matters here. */ +static int __init net_olddevs_init(void) +{ + int num; + + for (num = 0; num < 8; ++num) + ethif_probe2(num); + + return 0; +} + +static int __init ne_init(void) +{ + int retval; + + if (IS_MODULE(CONFIG_NE2000)) + ne_add_devices(); + else + net_olddevs_init(); + + retval = platform_driver_probe(&ne_driver, ne_drv_probe); + + if (IS_MODULE(CONFIG_NE2000) && retval) { + if (io[0] == 0) + pr_notice("ne.c: You must supply \"io=0xNNN\"" + " value(s) for ISA cards.\n"); + ne_loop_rm_unreg(1); + return retval; + } + + /* Unregister unused platform_devices. */ + ne_loop_rm_unreg(0); + return retval; +} +module_init(ne_init); + static void __exit ne_exit(void) { platform_driver_unregister(&ne_driver); diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 1a34da07c0db..b1a94c97c0b9 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -143,18 +143,18 @@ static struct { static const struct pci_device_id ne2k_pci_tbl[] = { - { 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 }, - { 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 }, - { 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 }, - { 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 }, - { 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC }, - { 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 }, - { 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 }, - { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F }, - { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 }, - { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 }, - { 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a }, - { 0, } + { PCI_DEVICE(0x10ec, 0x8029), .driver_data = CH_RealTek_RTL_8029 }, + { PCI_DEVICE(0x1050, 0x0940), .driver_data = CH_Winbond_89C940 }, + { PCI_DEVICE(0x11f6, 0x1401), .driver_data = CH_Compex_RL2000 }, + { PCI_DEVICE(0x8e2e, 0x3000), .driver_data = CH_KTI_ET32P2 }, + { PCI_DEVICE(0x4a14, 0x5000), .driver_data = CH_NetVin_NV5000SC }, + { PCI_DEVICE(0x1106, 0x0926), .driver_data = CH_Via_86C926 }, + { PCI_DEVICE(0x10bd, 0x0e34), .driver_data = CH_SureCom_NE34 }, + { PCI_DEVICE(0x1050, 0x5a5a), .driver_data = CH_Winbond_W89C940F }, + { PCI_DEVICE(0x12c3, 0x0058), .driver_data = CH_Holtek_HT80232 }, + { PCI_DEVICE(0x12c3, 0x5598), .driver_data = CH_Holtek_HT80229 }, + { PCI_DEVICE(0x8c4a, 0x1980), .driver_data = CH_Winbond_89C940_8c4a }, + { } }; MODULE_DEVICE_TABLE(pci, ne2k_pci_tbl); diff --git a/drivers/net/ethernet/8390/xsurf100.c b/drivers/net/ethernet/8390/xsurf100.c index fe7a74707aa4..370c19aa5fb9 100644 --- a/drivers/net/ethernet/8390/xsurf100.c +++ b/drivers/net/ethernet/8390/xsurf100.c @@ -357,8 +357,8 @@ static void xsurf100_remove(struct zorro_dev *zdev) } static const struct zorro_device_id xsurf100_zorro_tbl[] = { - { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF100, }, - { 0 } + { .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF100 }, + { } }; MODULE_DEVICE_TABLE(zorro, xsurf100_zorro_tbl); diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c index c24dd4fe7a10..cb1d5ed20874 100644 --- a/drivers/net/ethernet/8390/zorro8390.c +++ b/drivers/net/ethernet/8390/zorro8390.c @@ -262,9 +262,9 @@ static void zorro8390_remove_one(struct zorro_dev *z) } static struct zorro_device_id zorro8390_zorro_tbl[] = { - { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2, }, - { ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, }, - { 0 } + { .id = ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2 }, + { .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF }, + { } }; MODULE_DEVICE_TABLE(zorro, zorro8390_zorro_tbl); diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index b8f70e2a1763..78c79ad7bba5 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -22,6 +22,7 @@ source "drivers/net/ethernet/aeroflex/Kconfig" source "drivers/net/ethernet/agere/Kconfig" source "drivers/net/ethernet/airoha/Kconfig" source "drivers/net/ethernet/alacritech/Kconfig" +source "drivers/net/ethernet/alibaba/Kconfig" source "drivers/net/ethernet/allwinner/Kconfig" source "drivers/net/ethernet/altera/Kconfig" source "drivers/net/ethernet/amazon/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 57344fec6ce0..bba55d9af387 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_NET_VENDOR_ADI) += adi/ obj-$(CONFIG_NET_VENDOR_AGERE) += agere/ obj-$(CONFIG_NET_VENDOR_AIROHA) += airoha/ obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/ +obj-$(CONFIG_NET_VENDOR_ALIBABA) += alibaba/ obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ obj-$(CONFIG_ALTERA_TSE) += altera/ obj-$(CONFIG_NET_VENDOR_AMAZON) += amazon/ diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index e1b8794b14c9..f1109d90e1fc 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -278,8 +278,8 @@ enum chipset { }; static const struct pci_device_id starfire_pci_tbl[] = { - { PCI_VDEVICE(ADAPTEC, 0x6915), CH_6915 }, - { 0, } + { PCI_VDEVICE(ADAPTEC, 0x6915), .driver_data = CH_6915 }, + { } }; MODULE_DEVICE_TABLE(pci, starfire_pci_tbl); diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 0f6e5373029a..1b465a167672 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -4084,9 +4084,9 @@ static int et131x_pci_setup(struct pci_dev *pdev, } static const struct pci_device_id et131x_pci_table[] = { - { PCI_VDEVICE(ATT, ET131X_PCI_DEVICE_ID_GIG), 0UL}, - { PCI_VDEVICE(ATT, ET131X_PCI_DEVICE_ID_FAST), 0UL}, - { 0,} + { PCI_VDEVICE(ATT, ET131X_PCI_DEVICE_ID_GIG) }, + { PCI_VDEVICE(ATT, ET131X_PCI_DEVICE_ID_FAST) }, + { } }; MODULE_DEVICE_TABLE(pci, et131x_pci_table); diff --git a/drivers/net/ethernet/airoha/Kconfig b/drivers/net/ethernet/airoha/Kconfig index ad3ce501e7a5..1f6640a15fc9 100644 --- a/drivers/net/ethernet/airoha/Kconfig +++ b/drivers/net/ethernet/airoha/Kconfig @@ -29,6 +29,6 @@ config NET_AIROHA_FLOW_STATS bool "Airoha flow stats" depends on NET_AIROHA && NET_AIROHA_NPU help - Enable Aiorha flowtable statistic counters. + Enable Airoha flowtable statistic counters. endif #NET_VENDOR_AIROHA diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 31cdb11cd78d..64dde6464f3f 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -71,21 +71,76 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank, airoha_qdma_set_irqmask(irq_bank, index, mask, 0); } -static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) +static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) { - struct airoha_eth *eth = port->qdma->eth; - u32 val, reg; + u8 ref_addr[ETH_ALEN] __aligned(2); + struct airoha_eth *eth = dev->eth; + u32 reg, val, lmin, lmax; + int i; - reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H - : REG_FE_WAN_MAC_H; + eth_zero_addr(ref_addr); + lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5]; + lmax = lmin; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *iter_dev; + struct net_device *netdev; + + iter_dev = port->devs[j]; + if (!iter_dev || iter_dev == dev) + continue; + + if (airoha_is_lan_gdm_dev(iter_dev) != + airoha_is_lan_gdm_dev(dev)) + continue; + + netdev = netdev_from_priv(iter_dev); + if (netdev->reg_state != NETREG_REGISTERED) + continue; + + ether_addr_copy(ref_addr, netdev->dev_addr); + val = (netdev->dev_addr[3] << 16) | + (netdev->dev_addr[4] << 8) | netdev->dev_addr[5]; + if (val < lmin) + lmin = val; + if (val > lmax) + lmax = val; + } + } + + if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) { + /* According to the HW design, hw mac address MSBs must be + * the same for each net_device with the same LAN/WAN + * configuration. + */ + struct net_device *netdev = netdev_from_priv(dev); + + dev_warn(eth->dev, + "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n", + netdev->name, ref_addr[0], ref_addr[1], + ref_addr[2]); + dev_warn(eth->dev, "FE hw forwarding won't work properly\n"); + + return -EINVAL; + } + + reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H; val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; airoha_fe_wr(eth, reg, val); - val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; - airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); - airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); + airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin); + airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax); - airoha_ppe_init_upd_mem(port); + airoha_ppe_init_upd_mem(dev, addr); + + return 0; } static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, @@ -101,13 +156,13 @@ static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, FIELD_PREP(GDM_UCFQ_MASK, val)); } -static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port, - bool enable) +static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable) { - struct airoha_eth *eth = port->qdma->eth; + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; u32 vip_port; - vip_port = eth->soc->ops.get_vip_port(port, port->nbq); + vip_port = eth->soc->ops.get_vip_port(port, dev->nbq); if (enable) { airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); @@ -201,15 +256,13 @@ static void airoha_fe_vip_setup(struct airoha_eth *eth) static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth, u32 port, u32 queue) { - u32 val; - airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR, PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK, FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) | FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue)); - val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL); - return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val); + return airoha_fe_get(eth, REG_FE_PSE_QUEUE_CFG_VAL, + PSE_CFG_OQ_RSV_MASK); } static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, @@ -227,9 +280,7 @@ static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth, static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth) { - u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET); - - return FIELD_GET(PSE_ALLRSV_MASK, val); + return airoha_fe_get(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK); } static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, @@ -247,8 +298,7 @@ static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth, FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); /* modify hthd */ - tmp = airoha_fe_rr(eth, PSE_FQ_CFG); - fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp); + fq_limit = airoha_fe_get(eth, PSE_FQ_CFG, PSE_FQ_LIMIT_MASK); tmp = fq_limit - all_rsv - 0x20; airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD, PSE_SHARE_USED_HTHD_MASK, @@ -294,24 +344,24 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth) FIELD_PREP(PSE_ALLRSV_MASK, all_rsv)); } - /* CMD1 */ + /* CDM1 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q, PSE_QUEUE_RSV_PAGES); - /* GMD1 */ + /* GDM1 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q, PSE_QUEUE_RSV_PAGES); - /* GMD2 */ + /* GDM2 */ for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0); - /* GMD3 */ + /* GDM3 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q, PSE_QUEUE_RSV_PAGES); /* PPE1 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) { - if (q < pse_port_num_queues[FE_PSE_PORT_PPE1]) + if (q < pse_port_num_queues[FE_PSE_PORT_PPE1] / 2) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, PSE_QUEUE_RSV_PAGES); else @@ -340,7 +390,7 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth) q, 0); } } - /* GMD4 */ + /* GDM4 */ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++) airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q, PSE_QUEUE_RSV_PAGES); @@ -506,6 +556,14 @@ static int airoha_fe_init(struct airoha_eth *eth) airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), GDM_PAD_EN_MASK); airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), GDM_PAD_EN_MASK); + /* Enable split for MIB counters for GDM3 and GDM4 */ + airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX), + FE_GDM_TX_MIB_SPLIT_EN_MASK | + FE_GDM_RX_MIB_SPLIT_EN_MASK); + airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM4_IDX), + FE_GDM_TX_MIB_SPLIT_EN_MASK | + FE_GDM_RX_MIB_SPLIT_EN_MASK); + airoha_fe_crsn_qsel_init(eth); airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK); @@ -548,9 +606,10 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) q->queued++; nframes++; + offset += AIROHA_RX_HEADROOM; e->buf = page_address(page) + offset; e->dma_addr = page_pool_get_dma_addr(page) + offset; - e->dma_len = SKB_WITH_OVERHEAD(q->buf_size); + e->dma_len = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size)); val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len); WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); @@ -571,24 +630,26 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) return nframes; } -static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, - struct airoha_qdma_desc *desc) +static struct airoha_gdm_dev * +airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc) { - u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1)); + struct airoha_gdm_port *port; + u16 p, d; - sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); - switch (sport) { - case 0x10 ... 0x14: - port = 0; - break; - case 0x2 ... 0x4: - port = sport - 1; - break; - default: - return -EINVAL; - } + if (eth->soc->ops.get_dev_from_sport(desc, &p, &d)) + return ERR_PTR(-ENODEV); - return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; + if (p >= ARRAY_SIZE(eth->ports)) + return ERR_PTR(-ENODEV); + + port = eth->ports[p]; + if (!port) + return ERR_PTR(-ENODEV); + + if (d >= ARRAY_SIZE(port->devs)) + return ERR_PTR(-ENODEV); + + return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV); } static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) @@ -603,8 +664,9 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) struct airoha_queue_entry *e = &q->entry[q->tail]; struct airoha_qdma_desc *desc = &q->desc[q->tail]; u32 hash, reason, msg1, desc_ctrl; - struct airoha_gdm_port *port; - int data_len, len, p; + struct airoha_gdm_dev *dev; + struct net_device *netdev; + int data_len, len; struct page *page; desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); @@ -616,30 +678,31 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) q->tail = (q->tail + 1) % q->ndesc; q->queued--; - dma_sync_single_for_cpu(eth->dev, e->dma_addr, - SKB_WITH_OVERHEAD(q->buf_size), dir); + dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len, + dir); page = virt_to_head_page(e->buf); len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); - data_len = q->skb ? q->buf_size - : SKB_WITH_OVERHEAD(q->buf_size); + data_len = q->skb ? AIROHA_RX_LEN(q->buf_size) : e->dma_len; if (!len || data_len < len) goto free_frag; - p = airoha_qdma_get_gdm_port(eth, desc); - if (p < 0 || !eth->ports[p]) + dev = airoha_qdma_get_gdm_dev(eth, desc); + if (IS_ERR(dev)) goto free_frag; - port = eth->ports[p]; + netdev = netdev_from_priv(dev); if (!q->skb) { /* first buffer */ - q->skb = napi_build_skb(e->buf, q->buf_size); + q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM, + q->buf_size); if (!q->skb) goto free_frag; + skb_reserve(q->skb, AIROHA_RX_HEADROOM); __skb_put(q->skb, len); skb_mark_for_recycle(q->skb); - q->skb->dev = port->dev; - q->skb->protocol = eth_type_trans(q->skb, port->dev); + q->skb->dev = netdev; + q->skb->protocol = eth_type_trans(q->skb, netdev); q->skb->ip_summed = CHECKSUM_UNNECESSARY; skb_record_rx_queue(q->skb, qid); } else { /* scattered frame */ @@ -657,7 +720,9 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl)) continue; - if (netdev_uses_dsa(port->dev)) { + if (netdev_uses_dsa(netdev)) { + struct airoha_gdm_port *port = dev->port; + /* PPE module requires untagged packets to work * properly and it provides DSA port index via the * DMA descriptor. Report DSA tag to the DSA stack @@ -851,19 +916,29 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - int j; + int d; if (!port) continue; - if (port->qdma != qdma) - continue; + for (d = 0; d < ARRAY_SIZE(port->devs); d++) { + struct airoha_gdm_dev *dev = port->devs[d]; + struct net_device *netdev; + int j; - for (j = 0; j < port->dev->num_tx_queues; j++) { - if (airoha_qdma_get_txq(qdma, j) != qid) + if (!dev) continue; - netif_wake_subqueue(port->dev, j); + if (dev->qdma != qdma) + continue; + + netdev = netdev_from_priv(dev); + for (j = 0; j < netdev->num_tx_queues; j++) { + if (airoha_qdma_get_txq(qdma, j) != qid) + continue; + + netif_wake_subqueue(netdev, j); + } } } q->txq_stopped = false; @@ -1256,7 +1331,7 @@ static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma) FIELD_PREP(CNTR_CHAN_MASK, i)); /* Tx-fwd transferred count */ airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0); - airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1), + airoha_qdma_wr(qdma, REG_CNTR_CFG((i << 1) + 1), CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK | CNTR_ALL_DSCP_RING_EN_MASK | FIELD_PREP(CNTR_SRC_MASK, 1) | @@ -1562,205 +1637,264 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma) } } -static void airoha_update_hw_stats(struct airoha_gdm_port *port) +static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev) { - struct airoha_eth *eth = port->qdma->eth; + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; u32 val, i = 0; - spin_lock(&port->stats.lock); - u64_stats_update_begin(&port->stats.syncp); + /* Read relevant MIB for GDM with multiple port attached */ + if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX) + airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id), + FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK, + FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) | + FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq)); + + u64_stats_update_begin(&dev->stats.syncp); /* TX */ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); - port->stats.tx_ok_pkts += ((u64)val << 32); + dev->stats.tx_ok_pkts += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); - port->stats.tx_ok_pkts += val; + dev->stats.tx_ok_pkts += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); - port->stats.tx_ok_bytes += ((u64)val << 32); + dev->stats.tx_ok_bytes += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); - port->stats.tx_ok_bytes += val; + dev->stats.tx_ok_bytes += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); - port->stats.tx_drops += val; + dev->stats.tx_drops += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); - port->stats.tx_broadcast += val; + dev->stats.tx_broadcast += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); - port->stats.tx_multicast += val; + dev->stats.tx_multicast += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); - port->stats.tx_len[i] += val; + dev->stats.tx_len[i] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); - port->stats.tx_len[i] += ((u64)val << 32); + dev->stats.tx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); - port->stats.tx_len[i] += ((u64)val << 32); + dev->stats.tx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); - port->stats.tx_len[i] += ((u64)val << 32); + dev->stats.tx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); - port->stats.tx_len[i] += ((u64)val << 32); + dev->stats.tx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); - port->stats.tx_len[i] += ((u64)val << 32); + dev->stats.tx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); - port->stats.tx_len[i] += ((u64)val << 32); + dev->stats.tx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); - port->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += val; /* RX */ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); - port->stats.rx_ok_pkts += ((u64)val << 32); + dev->stats.rx_ok_pkts += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); - port->stats.rx_ok_pkts += val; + dev->stats.rx_ok_pkts += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); - port->stats.rx_ok_bytes += ((u64)val << 32); + dev->stats.rx_ok_bytes += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); - port->stats.rx_ok_bytes += val; + dev->stats.rx_ok_bytes += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); - port->stats.rx_drops += val; + dev->stats.rx_drops += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); - port->stats.rx_broadcast += val; + dev->stats.rx_broadcast += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); - port->stats.rx_multicast += val; + dev->stats.rx_multicast += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); - port->stats.rx_errors += val; + dev->stats.rx_errors += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); - port->stats.rx_crc_error += val; + dev->stats.rx_crc_error += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); - port->stats.rx_over_errors += val; + dev->stats.rx_over_errors += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); - port->stats.rx_fragment += val; + dev->stats.rx_fragment += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); - port->stats.rx_jabber += val; + dev->stats.rx_jabber += val; i = 0; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); - port->stats.rx_len[i] += val; + dev->stats.rx_len[i] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); - port->stats.rx_len[i] += ((u64)val << 32); + dev->stats.rx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); - port->stats.rx_len[i] += ((u64)val << 32); + dev->stats.rx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); - port->stats.rx_len[i] += ((u64)val << 32); + dev->stats.rx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); - port->stats.rx_len[i] += ((u64)val << 32); + dev->stats.rx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); - port->stats.rx_len[i] += ((u64)val << 32); + dev->stats.rx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); - port->stats.rx_len[i] += ((u64)val << 32); + dev->stats.rx_len[i] += ((u64)val << 32); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); - port->stats.rx_len[i++] += val; + dev->stats.rx_len[i++] += val; - /* reset mib counters */ - airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id), - FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); - - u64_stats_update_end(&port->stats.syncp); - spin_unlock(&port->stats.lock); + u64_stats_update_end(&dev->stats.syncp); } -static int airoha_dev_open(struct net_device *dev) +static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) { - int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN; - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_qdma *qdma = port->qdma; - u32 pse_port = FE_PSE_PORT_PPE1; + struct airoha_gdm_port *port = dev->port; + int i; - netif_tx_start_all_queues(dev); - err = airoha_set_vip_for_gdm_port(port, true); + spin_lock(&port->stats_lock); + + for (i = 0; i < ARRAY_SIZE(port->devs); i++) { + if (port->devs[i]) + airoha_dev_get_hw_stats(port->devs[i]); + } + + /* Reset MIB counters */ + airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id), + FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); + + spin_unlock(&port->stats_lock); +} + +static int airoha_dev_open(struct net_device *netdev) +{ + int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + u32 cur_len, pse_port = FE_PSE_PORT_PPE1; + struct airoha_qdma *qdma = dev->qdma; + + netif_tx_start_all_queues(netdev); + err = airoha_set_vip_for_gdm_port(dev, true); if (err) return err; - if (netdev_uses_dsa(dev)) + if (netdev_uses_dsa(netdev)) airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), GDM_STAG_EN_MASK); else airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), GDM_STAG_EN_MASK); - airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), - GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | - FIELD_PREP(GDM_LONG_LEN_MASK, len)); + cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id), + GDM_LONG_LEN_MASK); + if (!port->users || len > cur_len) { + /* Opening a sibling net_device with a larger MTU updates the + * MTU of already running devices. This is required to allow + * multiple net_devices with different MTUs to share the same + * GDM port. + */ + airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, len)); + } + port->users++; airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK | GLOBAL_CFG_RX_DMA_EN_MASK); - atomic_inc(&qdma->users); + qdma->users++; - if (port->id == AIROHA_GDM2_IDX && - airoha_ppe_is_enabled(qdma->eth, 1)) { - /* For PPE2 always use secondary cpu port. */ + if (!airoha_is_lan_gdm_dev(dev) && + airoha_ppe_is_enabled(qdma->eth, 1)) pse_port = FE_PSE_PORT_PPE2; - } airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), pse_port); return 0; } -static int airoha_dev_stop(struct net_device *dev) +static void airoha_set_port_mtu(struct airoha_eth *eth, + struct airoha_gdm_port *port) { - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_qdma *qdma = port->qdma; + u32 len = 0; int i; - netif_tx_disable(dev); - airoha_set_vip_for_gdm_port(port, false); - for (i = 0; i < dev->num_tx_queues; i++) - netdev_tx_reset_subqueue(dev, i); + for (i = 0; i < ARRAY_SIZE(port->devs); i++) { + struct airoha_gdm_dev *dev = port->devs[i]; + struct net_device *netdev; - airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), - FE_PSE_PORT_DROP); + if (!dev) + continue; - if (atomic_dec_and_test(&qdma->users)) { + netdev = netdev_from_priv(dev); + if (netif_running(netdev)) + len = max_t(u32, len, netdev->mtu); + } + len += ETH_HLEN + ETH_FCS_LEN; + + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), + GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_LONG_LEN_MASK, len)); +} + +static int airoha_dev_stop(struct net_device *netdev) +{ + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + struct airoha_qdma *qdma = dev->qdma; + int i; + + netif_tx_disable(netdev); + airoha_set_vip_for_gdm_port(dev, false); + for (i = 0; i < netdev->num_tx_queues; i++) + netdev_tx_reset_subqueue(netdev, i); + + if (--port->users) + airoha_set_port_mtu(dev->eth, port); + else + airoha_set_gdm_port_fwd_cfg(qdma->eth, + REG_GDM_FWD_CFG(port->id), + FE_PSE_PORT_DROP); + + if (!--qdma->users) { airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK | GLOBAL_CFG_RX_DMA_EN_MASK); @@ -1776,26 +1910,36 @@ static int airoha_dev_stop(struct net_device *dev) return 0; } -static int airoha_dev_set_macaddr(struct net_device *dev, void *p) +static int airoha_dev_set_macaddr(struct net_device *netdev, void *p) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct sockaddr *addr = p; int err; - err = eth_mac_addr(dev, p); + err = eth_prepare_mac_addr_change(netdev, p); if (err) return err; - airoha_set_macaddr(port, dev->dev_addr); + err = airoha_set_macaddr(dev, addr->sa_data); + if (err) + return err; + + eth_commit_mac_addr_change(netdev, p); return 0; } -static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) +static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) { - struct airoha_eth *eth = port->qdma->eth; + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; u32 val, pse_port, chan; int i, src_port; + src_port = eth->soc->ops.get_sport(port, dev->nbq); + if (src_port < 0) + return src_port; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), FE_PSE_PORT_DROP); airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), @@ -1825,10 +1969,6 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); - src_port = eth->soc->ops.get_src_port_id(port, port->nbq); - if (src_port < 0) - return src_port; - airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, FIELD_PREP(WAN0_MASK, src_port)); @@ -1839,10 +1979,10 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); for (i = 0; i < eth->soc->num_ppe; i++) - airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX); + airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX); if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { - u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); + u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq); airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, __field_prep(mask, AIROHA_GDM2_IDX)); @@ -1851,92 +1991,129 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) return 0; } -static int airoha_dev_init(struct net_device *dev) +static struct airoha_gdm_dev * +airoha_get_wan_gdm_dev(struct airoha_eth *eth) { - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_eth *eth = port->eth; int i; + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + + if (dev && !airoha_is_lan_gdm_dev(dev)) + return dev; + } + } + + return NULL; +} + +static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev) +{ + struct net_device *netdev = netdev_from_priv(dev); + struct airoha_eth *eth = dev->eth; + int ppe_id; + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ - port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)]; - port->dev->irq = port->qdma->irq_banks[0].irq; - airoha_set_macaddr(port, dev->dev_addr); + dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; + netdev->irq = dev->qdma->irq_banks[0].irq; + + ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); + airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); +} + +static int airoha_dev_init(struct net_device *netdev) +{ + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; switch (port->id) { case AIROHA_GDM3_IDX: case AIROHA_GDM4_IDX: - /* If GDM2 is active we can't enable loopback */ - if (!eth->ports[1]) { - int err; - - err = airoha_set_gdm2_loopback(port); - if (err) - return err; - } + if (airoha_get_wan_gdm_dev(dev->eth)) + break; + fallthrough; + case AIROHA_GDM2_IDX: + /* GDM2 is always used as wan */ + dev->flags |= AIROHA_PRIV_F_WAN; break; default: break; } - for (i = 0; i < eth->soc->num_ppe; i++) - airoha_ppe_set_cpu_port(port, i, - airoha_get_fe_port(port)); + airoha_dev_set_qdma(dev); + airoha_set_macaddr(dev, netdev->dev_addr); + + if (!airoha_is_lan_gdm_dev(dev) && + (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) { + int err; + + err = airoha_enable_gdm2_loopback(dev); + if (err) + return err; + } return 0; } -static void airoha_dev_get_stats64(struct net_device *dev, +static void airoha_dev_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *storage) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); unsigned int start; - airoha_update_hw_stats(port); + airoha_update_hw_stats(dev); do { - start = u64_stats_fetch_begin(&port->stats.syncp); - storage->rx_packets = port->stats.rx_ok_pkts; - storage->tx_packets = port->stats.tx_ok_pkts; - storage->rx_bytes = port->stats.rx_ok_bytes; - storage->tx_bytes = port->stats.tx_ok_bytes; - storage->multicast = port->stats.rx_multicast; - storage->rx_errors = port->stats.rx_errors; - storage->rx_dropped = port->stats.rx_drops; - storage->tx_dropped = port->stats.tx_drops; - storage->rx_crc_errors = port->stats.rx_crc_error; - storage->rx_over_errors = port->stats.rx_over_errors; - } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + start = u64_stats_fetch_begin(&dev->stats.syncp); + storage->rx_packets = dev->stats.rx_ok_pkts; + storage->tx_packets = dev->stats.tx_ok_pkts; + storage->rx_bytes = dev->stats.rx_ok_bytes; + storage->tx_bytes = dev->stats.tx_ok_bytes; + storage->multicast = dev->stats.rx_multicast; + storage->rx_errors = dev->stats.rx_errors; + storage->rx_dropped = dev->stats.rx_drops; + storage->tx_dropped = dev->stats.tx_drops; + storage->rx_crc_errors = dev->stats.rx_crc_error; + storage->rx_over_errors = dev->stats.rx_over_errors; + } while (u64_stats_fetch_retry(&dev->stats.syncp, start)); } -static int airoha_dev_change_mtu(struct net_device *dev, int mtu) +static int airoha_dev_change_mtu(struct net_device *netdev, int mtu) { - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_eth *eth = port->qdma->eth; - u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; - airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), - GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_LONG_LEN_MASK, len)); - WRITE_ONCE(dev->mtu, mtu); + WRITE_ONCE(netdev->mtu, mtu); + if (port->users) + airoha_set_port_mtu(dev->eth, port); return 0; } -static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, +static u16 airoha_dev_select_queue(struct net_device *netdev, + struct sk_buff *skb, struct net_device *sb_dev) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; int queue, channel; /* For dsa device select QoS channel according to the dsa user port * index, rely on port id otherwise. Select QoS queue based on the * skb priority. */ - channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id; + channel = netdev_uses_dsa(netdev) ? skb_get_queue_mapping(skb) : port->id; channel = channel % AIROHA_NUM_QOS_CHANNELS; queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */ queue = channel * AIROHA_NUM_QOS_QUEUES + queue; - return queue < dev->num_tx_queues ? queue : 0; + return queue < netdev->num_tx_queues ? queue : 0; } static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev) @@ -1983,10 +2160,10 @@ static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev) #endif } -int airoha_get_fe_port(struct airoha_gdm_port *port) +int airoha_get_fe_port(struct airoha_gdm_dev *dev) { - struct airoha_qdma *qdma = port->qdma; - struct airoha_eth *eth = qdma->eth; + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; switch (eth->soc->version) { case 0x7583: @@ -2000,10 +2177,10 @@ int airoha_get_fe_port(struct airoha_gdm_port *port) } static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, - struct net_device *dev) + struct net_device *netdev) { - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_qdma *qdma = port->qdma; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma = dev->qdma; u32 nr_frags, tag, msg0, msg1, len; struct airoha_queue_entry *e; struct netdev_queue *txq; @@ -2015,7 +2192,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, u8 fport; qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); - tag = airoha_get_dsa_tag(skb, dev); + tag = airoha_get_dsa_tag(skb, netdev); msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, qid / AIROHA_NUM_QOS_QUEUES) | @@ -2041,8 +2218,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, } } - fport = airoha_get_fe_port(port); - msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + fport = airoha_get_fe_port(dev); + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_NBOQ_MASK, dev->nbq) | + FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); q = &qdma->q_tx[qid]; @@ -2051,7 +2229,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, spin_lock_bh(&q->lock); - txq = skb_get_tx_queue(dev, skb); + txq = skb_get_tx_queue(netdev, skb); nr_frags = 1 + skb_shinfo(skb)->nr_frags; if (q->queued + nr_frags >= q->ndesc) { @@ -2075,9 +2253,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, dma_addr_t addr; u32 val; - addr = dma_map_single(dev->dev.parent, data, len, + addr = dma_map_single(netdev->dev.parent, data, len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev->dev.parent, addr))) + if (unlikely(dma_mapping_error(netdev->dev.parent, addr))) goto error_unmap; list_move_tail(&e->list, &tx_list); @@ -2126,7 +2304,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, error_unmap: list_for_each_entry(e, &tx_list, list) { - dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len, + dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len, DMA_TO_DEVICE); e->dma_addr = 0; } @@ -2135,38 +2313,38 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, spin_unlock_bh(&q->lock); error: dev_kfree_skb_any(skb); - dev->stats.tx_dropped++; + netdev->stats.tx_dropped++; return NETDEV_TX_OK; } -static void airoha_ethtool_get_drvinfo(struct net_device *dev, +static void airoha_ethtool_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_eth *eth = port->qdma->eth; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_eth *eth = dev->eth; strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver)); strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info)); } -static void airoha_ethtool_get_mac_stats(struct net_device *dev, +static void airoha_ethtool_get_mac_stats(struct net_device *netdev, struct ethtool_eth_mac_stats *stats) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); unsigned int start; - airoha_update_hw_stats(port); + airoha_update_hw_stats(dev); do { - start = u64_stats_fetch_begin(&port->stats.syncp); - stats->FramesTransmittedOK = port->stats.tx_ok_pkts; - stats->OctetsTransmittedOK = port->stats.tx_ok_bytes; - stats->MulticastFramesXmittedOK = port->stats.tx_multicast; - stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast; - stats->FramesReceivedOK = port->stats.rx_ok_pkts; - stats->OctetsReceivedOK = port->stats.rx_ok_bytes; - stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast; - } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + start = u64_stats_fetch_begin(&dev->stats.syncp); + stats->FramesTransmittedOK = dev->stats.tx_ok_pkts; + stats->OctetsTransmittedOK = dev->stats.tx_ok_bytes; + stats->MulticastFramesXmittedOK = dev->stats.tx_multicast; + stats->BroadcastFramesXmittedOK = dev->stats.tx_broadcast; + stats->FramesReceivedOK = dev->stats.rx_ok_pkts; + stats->OctetsReceivedOK = dev->stats.rx_ok_bytes; + stats->BroadcastFramesReceivedOK = dev->stats.rx_broadcast; + } while (u64_stats_fetch_retry(&dev->stats.syncp, start)); } static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { @@ -2181,12 +2359,12 @@ static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = { }; static void -airoha_ethtool_get_rmon_stats(struct net_device *dev, +airoha_ethtool_get_rmon_stats(struct net_device *netdev, struct ethtool_rmon_stats *stats, const struct ethtool_rmon_hist_range **ranges) { - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_hw_stats *hw_stats = &port->stats; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_hw_stats *hw_stats = &dev->stats; unsigned int start; BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) != @@ -2195,11 +2373,11 @@ airoha_ethtool_get_rmon_stats(struct net_device *dev, ARRAY_SIZE(hw_stats->rx_len) + 1); *ranges = airoha_ethtool_rmon_ranges; - airoha_update_hw_stats(port); + airoha_update_hw_stats(dev); do { int i; - start = u64_stats_fetch_begin(&port->stats.syncp); + start = u64_stats_fetch_begin(&dev->stats.syncp); stats->fragments = hw_stats->rx_fragment; stats->jabbers = hw_stats->rx_jabber; for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1; @@ -2207,25 +2385,25 @@ airoha_ethtool_get_rmon_stats(struct net_device *dev, stats->hist[i] = hw_stats->rx_len[i]; stats->hist_tx[i] = hw_stats->tx_len[i]; } - } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } while (u64_stats_fetch_retry(&dev->stats.syncp, start)); } -static int airoha_qdma_set_chan_tx_sched(struct net_device *dev, +static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, int channel, enum tx_sched_mode mode, const u16 *weights, u8 n_weights) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); int i; for (i = 0; i < AIROHA_NUM_TX_RING; i++) - airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel), + airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel), TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); for (i = 0; i < n_weights; i++) { u32 status; int err; - airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG, + airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG, TWRR_RW_CMD_MASK | FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | @@ -2233,13 +2411,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *dev, err = read_poll_timeout(airoha_qdma_rr, status, status & TWRR_RW_CMD_DONE, USEC_PER_MSEC, 10 * USEC_PER_MSEC, - true, port->qdma, - REG_TXWRR_WEIGHT_CFG); + true, dev->qdma, REG_TXWRR_WEIGHT_CFG); if (err) return err; } - airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3), + airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3), CHAN_QOS_MODE_MASK(channel), __field_prep(CHAN_QOS_MODE_MASK(channel), mode)); @@ -2299,21 +2476,21 @@ static int airoha_qdma_set_tx_ets_sched(struct net_device *dev, int channel, ARRAY_SIZE(w)); } -static int airoha_qdma_get_tx_ets_stats(struct net_device *dev, int channel, +static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel, struct tc_ets_qopt_offload *opt) { - struct airoha_gdm_port *port = netdev_priv(dev); - u64 cpu_tx_packets = airoha_qdma_rr(port->qdma, - REG_CNTR_VAL(channel << 1)); - u64 fwd_tx_packets = airoha_qdma_rr(port->qdma, + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma = dev->qdma; + + u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1)); + u64 fwd_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL((channel << 1) + 1)); - u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) + - (fwd_tx_packets - port->fwd_tx_packets); + u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) + + (fwd_tx_packets - dev->fwd_tx_packets); _bstats_update(opt->stats.bstats, 0, tx_packets); - - port->cpu_tx_packets = cpu_tx_packets; - port->fwd_tx_packets = fwd_tx_packets; + dev->cpu_tx_packets = cpu_tx_packets; + dev->fwd_tx_packets = fwd_tx_packets; return 0; } @@ -2564,21 +2741,21 @@ static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma, mode, val); } -static int airoha_qdma_set_tx_rate_limit(struct net_device *dev, +static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev, int channel, u32 rate, u32 bucket_size) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); int i, err; for (i = 0; i <= TRTCM_PEAK_MODE; i++) { - err = airoha_qdma_set_trtcm_config(port->qdma, channel, + err = airoha_qdma_set_trtcm_config(dev->qdma, channel, REG_EGRESS_TRTCM_CFG, i, !!rate, TRTCM_METER_MODE); if (err) return err; - err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel, + err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel, REG_EGRESS_TRTCM_CFG, i, rate, bucket_size); if (err) @@ -2588,13 +2765,12 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *dev, return 0; } -static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev, - struct tc_htb_qopt_offload *opt) +static int airoha_tc_htb_modify_queue(struct net_device *dev, + struct tc_htb_qopt_offload *opt) { u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */ - int err, num_tx_queues = dev->real_num_tx_queues; - struct airoha_gdm_port *port = netdev_priv(dev); + int err; if (opt->parent_classid != TC_HTB_CLASSID_ROOT) { NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid"); @@ -2602,34 +2778,58 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev, } err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum); - if (err) { + if (err) NL_SET_ERR_MSG_MOD(opt->extack, "failed configuring htb offload"); - return err; + + return err; +} + +static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev, + struct tc_htb_qopt_offload *opt) +{ + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; + int err, num_tx_queues = netdev->real_num_tx_queues; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma = dev->qdma; + + /* Here we need to check the requested QDMA channel is not already + * in use by another net_device running on the same QDMA block. + */ + if (test_and_set_bit(channel, qdma->qos_channel_map)) { + NL_SET_ERR_MSG_MOD(opt->extack, + "qdma qos channel already in use"); + return -EBUSY; } - if (opt->command == TC_HTB_NODE_MODIFY) - return 0; + err = airoha_tc_htb_modify_queue(netdev, opt); + if (err) + goto error; - err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1); + err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1); if (err) { - airoha_qdma_set_tx_rate_limit(dev, channel, 0, opt->quantum); + airoha_qdma_set_tx_rate_limit(netdev, channel, 0, + opt->quantum); NL_SET_ERR_MSG_MOD(opt->extack, "failed setting real_num_tx_queues"); - return err; + goto error; } - set_bit(channel, port->qos_sq_bmap); + set_bit(channel, dev->qos_sq_bmap); opt->qid = AIROHA_NUM_TX_RING + channel; return 0; +error: + clear_bit(channel, qdma->qos_channel_map); + + return err; } -static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port, +static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev, u32 rate, u32 bucket_size, enum trtcm_unit_type unit_type) { - struct airoha_qdma *qdma = port->qdma; + struct airoha_qdma *qdma = dev->qdma; int i; for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { @@ -2703,11 +2903,11 @@ static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f) return 0; } -static int airoha_dev_tc_matchall(struct net_device *dev, +static int airoha_dev_tc_matchall(struct net_device *netdev, struct tc_cls_matchall_offload *f) { enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); u32 rate = 0, bucket_size = 0; switch (f->command) { @@ -2732,7 +2932,7 @@ static int airoha_dev_tc_matchall(struct net_device *dev, fallthrough; } case TC_CLSMATCHALL_DESTROY: - return airoha_qdma_set_rx_meter(port, rate, bucket_size, + return airoha_qdma_set_rx_meter(dev, rate, bucket_size, unit_type); default: return -EOPNOTSUPP; @@ -2742,18 +2942,18 @@ static int airoha_dev_tc_matchall(struct net_device *dev, static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { - struct net_device *dev = cb_priv; - struct airoha_gdm_port *port = netdev_priv(dev); - struct airoha_eth *eth = port->qdma->eth; + struct net_device *netdev = cb_priv; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_eth *eth = dev->eth; - if (!tc_can_offload(dev)) + if (!tc_can_offload(netdev)) return -EOPNOTSUPP; switch (type) { case TC_SETUP_CLSFLOWER: return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data); case TC_SETUP_CLSMATCHALL: - return airoha_dev_tc_matchall(dev, type_data); + return airoha_dev_tc_matchall(netdev, type_data); default: return -EOPNOTSUPP; } @@ -2800,49 +3000,52 @@ static int airoha_dev_setup_tc_block(struct net_device *dev, } } -static void airoha_tc_remove_htb_queue(struct net_device *dev, int queue) +static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma = dev->qdma; - netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1); - airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0); - clear_bit(queue, port->qos_sq_bmap); + netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1); + airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0); + + clear_bit(queue, qdma->qos_channel_map); + clear_bit(queue, dev->qos_sq_bmap); } -static int airoha_tc_htb_delete_leaf_queue(struct net_device *dev, +static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev, struct tc_htb_qopt_offload *opt) { u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); - if (!test_bit(channel, port->qos_sq_bmap)) { + if (!test_bit(channel, dev->qos_sq_bmap)) { NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); return -EINVAL; } - airoha_tc_remove_htb_queue(dev, channel); + airoha_tc_remove_htb_queue(netdev, channel); return 0; } -static int airoha_tc_htb_destroy(struct net_device *dev) +static int airoha_tc_htb_destroy(struct net_device *netdev) { - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); int q; - for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) - airoha_tc_remove_htb_queue(dev, q); + for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) + airoha_tc_remove_htb_queue(netdev, q); return 0; } -static int airoha_tc_get_htb_get_leaf_queue(struct net_device *dev, +static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev, struct tc_htb_qopt_offload *opt) { u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; - struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_gdm_dev *dev = netdev_priv(netdev); - if (!test_bit(channel, port->qos_sq_bmap)) { + if (!test_bit(channel, dev->qos_sq_bmap)) { NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id"); return -EINVAL; } @@ -2861,6 +3064,7 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev, case TC_HTB_DESTROY: return airoha_tc_htb_destroy(dev); case TC_HTB_NODE_MODIFY: + return airoha_tc_htb_modify_queue(dev, opt); case TC_HTB_LEAF_ALLOC_QUEUE: return airoha_tc_htb_alloc_leaf_queue(dev, opt); case TC_HTB_LEAF_DEL: @@ -2876,8 +3080,8 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev, return 0; } -static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type, - void *type_data) +static int airoha_dev_tc_setup(struct net_device *dev, + enum tc_setup_type type, void *type_data) { switch (type) { case TC_SETUP_QDISC_ETS: @@ -2943,26 +3147,100 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port) } } -bool airoha_is_valid_gdm_port(struct airoha_eth *eth, - struct airoha_gdm_port *port) +bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, + struct airoha_gdm_dev *dev) { int i; for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { - if (eth->ports[i] == port) - return true; + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + if (port->devs[j] == dev) + return true; + } } return false; } +static int airoha_alloc_gdm_device(struct airoha_eth *eth, + struct airoha_gdm_port *port, + int nbq, struct device_node *np) +{ + struct net_device *netdev; + struct airoha_gdm_dev *dev; + u8 index; + int err; + + netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev), + AIROHA_NUM_NETDEV_TX_RINGS, + AIROHA_NUM_RX_RING); + if (!netdev) { + dev_err(eth->dev, "alloc_etherdev failed\n"); + return -ENOMEM; + } + + netdev->netdev_ops = &airoha_netdev_ops; + netdev->ethtool_ops = &airoha_ethtool_ops; + netdev->max_mtu = AIROHA_MAX_MTU; + netdev->watchdog_timeo = 5 * HZ; + netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 | + NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | + NETIF_F_HW_TC; + netdev->features |= netdev->hw_features; + netdev->vlan_features = netdev->hw_features; + SET_NETDEV_DEV(netdev, eth->dev); + + /* reserve hw queues for HTB offloading */ + err = netif_set_real_num_tx_queues(netdev, AIROHA_NUM_TX_RING); + if (err) + return err; + + err = of_get_ethdev_address(np, netdev); + if (err) { + if (err == -EPROBE_DEFER) + return err; + + eth_hw_addr_random(netdev); + dev_info(eth->dev, "generated random MAC address %pM\n", + netdev->dev_addr); + } + + /* Allowed nbq for EN7581 on GDM3 port are 4 and 5 for PCIE0 + * and PCIE1 respectively. + */ + index = nbq; + if (index && airoha_is_7581(eth) && port->id == AIROHA_GDM3_IDX) + index -= 4; + + if (index >= ARRAY_SIZE(port->devs) || port->devs[index]) { + dev_err(eth->dev, "invalid nbq id: %d\n", nbq); + return -EINVAL; + } + + netdev->dev.of_node = of_node_get(np); + dev = netdev_priv(netdev); + u64_stats_init(&dev->stats.syncp); + dev->port = port; + dev->eth = eth; + dev->nbq = nbq; + port->devs[index] = dev; + + return 0; +} + static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np) { const __be32 *id_ptr = of_get_property(np, "reg", NULL); struct airoha_gdm_port *port; - struct net_device *dev; - int err, p; + struct device_node *node; + int err, nbq, p, d = 0; u32 id; if (!id_ptr) { @@ -2983,53 +3261,57 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, return -EINVAL; } - dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port), - AIROHA_NUM_NETDEV_TX_RINGS, - AIROHA_NUM_RX_RING); - if (!dev) { - dev_err(eth->dev, "alloc_etherdev failed\n"); + port = devm_kzalloc(eth->dev, sizeof(*port), GFP_KERNEL); + if (!port) return -ENOMEM; - } - dev->netdev_ops = &airoha_netdev_ops; - dev->ethtool_ops = &airoha_ethtool_ops; - dev->max_mtu = AIROHA_MAX_MTU; - dev->watchdog_timeo = 5 * HZ; - dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | - NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | - NETIF_F_SG | NETIF_F_TSO | - NETIF_F_HW_TC; - dev->features |= dev->hw_features; - dev->vlan_features = dev->hw_features; - dev->dev.of_node = np; - SET_NETDEV_DEV(dev, eth->dev); + port->id = id; + spin_lock_init(&port->stats_lock); + eth->ports[p] = port; - /* reserve hw queues for HTB offloading */ - err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING); + err = airoha_metadata_dst_alloc(port); if (err) return err; - err = of_get_ethdev_address(np, dev); - if (err) { - if (err == -EPROBE_DEFER) - return err; + /* Default nbq value to ensure backward compatibility */ + nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; - eth_hw_addr_random(dev); - dev_info(eth->dev, "generated random MAC address %pM\n", - dev->dev_addr); + for_each_child_of_node(np, node) { + /* Multiple external serdes connected to the FE GDM port via an + * external arbiter. + */ + const __be32 *nbq_ptr; + + if (!of_device_is_compatible(node, "airoha,eth-port")) + continue; + + d++; + if (!of_device_is_available(node)) + continue; + + nbq_ptr = of_get_property(node, "reg", NULL); + if (!nbq_ptr) { + dev_err(eth->dev, "missing nbq id\n"); + of_node_put(node); + return -EINVAL; + } + + /* Verify the provided nbq parameter is valid */ + nbq = be32_to_cpup(nbq_ptr); + err = eth->soc->ops.get_sport(port, nbq); + if (err < 0) { + of_node_put(node); + return err; + } + + err = airoha_alloc_gdm_device(eth, port, nbq, node); + if (err) { + of_node_put(node); + return err; + } } - port = netdev_priv(dev); - u64_stats_init(&port->stats.syncp); - spin_lock_init(&port->stats.lock); - port->eth = eth; - port->dev = dev; - port->id = id; - /* XXX: Read nbq from DTS */ - port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; - eth->ports[p] = port; - - return airoha_metadata_dst_alloc(port); + return !d ? airoha_alloc_gdm_device(eth, port, nbq, np) : 0; } static int airoha_register_gdm_devices(struct airoha_eth *eth) @@ -3038,14 +3320,22 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - int err; + int j; if (!port) continue; - err = register_netdev(port->dev); - if (err) - return err; + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + int err; + + if (!dev) + continue; + + err = register_netdev(netdev_from_priv(dev)); + if (err) + return err; + } } set_bit(DEV_STATE_REGISTERED, ð->state); @@ -3152,12 +3442,23 @@ static int airoha_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; + int j; if (!port) continue; - if (port->dev->reg_state == NETREG_REGISTERED) - unregister_netdev(port->dev); + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + struct net_device *netdev; + + if (!dev) + continue; + + netdev = netdev_from_priv(dev); + if (netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(netdev); + of_node_put(netdev->dev.of_node); + } airoha_metadata_dst_free(port); } airoha_hw_cleanup(eth); @@ -3178,11 +3479,22 @@ static void airoha_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; + int j; if (!port) continue; - unregister_netdev(port->dev); + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + struct net_device *netdev; + + if (!dev) + continue; + + netdev = netdev_from_priv(dev); + unregister_netdev(netdev); + of_node_put(netdev->dev.of_node); + } airoha_metadata_dst_free(port); } airoha_hw_cleanup(eth); @@ -3199,7 +3511,7 @@ static const char * const en7581_xsi_rsts_names[] = { "xfp-mac", }; -static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq) +static int airoha_en7581_get_sport(struct airoha_gdm_port *port, int nbq) { switch (port->id) { case AIROHA_GDM3_IDX: @@ -3245,6 +3557,39 @@ static u32 airoha_en7581_get_vip_port(struct airoha_gdm_port *port, int nbq) return 0; } +static int airoha_en7581_get_dev_from_sport(struct airoha_qdma_desc *desc, + u16 *port, u16 *dev) +{ + u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, + le32_to_cpu(READ_ONCE(desc->msg1))); + + *dev = 0; + switch (sport) { + case 0x10 ... 0x14: + *port = 0; /* GDM1 */ + break; + case 0x2 ... 0x4: + *port = sport - 1; + break; + case HSGMII_LAN_7581_PCIE1_SRCPORT: + *dev = 1; + fallthrough; + case HSGMII_LAN_7581_PCIE0_SRCPORT: + *port = 2; /* GDM3 */ + break; + case HSGMII_LAN_7581_USB_SRCPORT: + *dev = 1; + fallthrough; + case HSGMII_LAN_7581_ETH_SRCPORT: + *port = 3; /* GDM4 */ + break; + default: + return -EINVAL; + } + + return 0; +} + static const char * const an7583_xsi_rsts_names[] = { "xsi-mac", "hsi0-mac", @@ -3252,7 +3597,7 @@ static const char * const an7583_xsi_rsts_names[] = { "xfp-mac", }; -static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq) +static int airoha_an7583_get_sport(struct airoha_gdm_port *port, int nbq) { switch (port->id) { case AIROHA_GDM3_IDX: @@ -3294,14 +3639,45 @@ static u32 airoha_an7583_get_vip_port(struct airoha_gdm_port *port, int nbq) return 0; } +static int airoha_an7583_get_dev_from_sport(struct airoha_qdma_desc *desc, + u16 *port, u16 *dev) +{ + u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, + le32_to_cpu(READ_ONCE(desc->msg1))); + + *dev = 0; + switch (sport) { + case 0x10 ... 0x14: + *port = 0; /* GDM1 */ + break; + case 0x2 ... 0x4: + *port = sport - 1; + break; + case HSGMII_LAN_7583_ETH_SRCPORT: + *port = 2; /* GDM3 */ + break; + case HSGMII_LAN_7583_USB_SRCPORT: + *dev = 1; + fallthrough; + case HSGMII_LAN_7583_PCIE_SRCPORT: + *port = 3; /* GDM4 */ + break; + default: + return -EINVAL; + } + + return 0; +} + static const struct airoha_eth_soc_data en7581_soc_data = { .version = 0x7581, .xsi_rsts_names = en7581_xsi_rsts_names, .num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names), .num_ppe = 2, .ops = { - .get_src_port_id = airoha_en7581_get_src_port_id, + .get_sport = airoha_en7581_get_sport, .get_vip_port = airoha_en7581_get_vip_port, + .get_dev_from_sport = airoha_en7581_get_dev_from_sport, }, }; @@ -3311,8 +3687,9 @@ static const struct airoha_eth_soc_data an7583_soc_data = { .num_xsi_rsts = ARRAY_SIZE(an7583_xsi_rsts_names), .num_ppe = 1, .ops = { - .get_src_port_id = airoha_an7583_get_src_port_id, + .get_sport = airoha_an7583_get_sport, .get_vip_port = airoha_an7583_get_vip_port, + .get_dev_from_sport = airoha_an7583_get_dev_from_sport, }, }; @@ -3333,6 +3710,6 @@ static struct platform_driver airoha_driver = { }; module_platform_driver(airoha_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("Ethernet driver for Airoha SoC"); diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 4fad3acc3ccf..41d2e7a1f9fb 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -17,6 +17,7 @@ #include #define AIROHA_MAX_NUM_GDM_PORTS 4 +#define AIROHA_MAX_NUM_GDM_DEVS 2 #define AIROHA_MAX_NUM_QDMA 2 #define AIROHA_MAX_NUM_IRQ_BANKS 4 #define AIROHA_MAX_DSA_PORTS 7 @@ -32,6 +33,8 @@ #define AIROHA_FE_MC_MAX_VLAN_TABLE 64 #define AIROHA_FE_MC_MAX_VLAN_PORT 16 #define AIROHA_NUM_TX_IRQ 2 +#define AIROHA_RX_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN) +#define AIROHA_RX_LEN(_n) ((_n) - AIROHA_RX_HEADROOM) #define HW_DSCP_NUM 2048 #define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048) #define TX_DSCP_NUM 1024 @@ -212,8 +215,6 @@ struct airoha_tx_irq_queue { }; struct airoha_hw_stats { - /* protect concurrent hw_stats accesses */ - spinlock_t lock; struct u64_stats_sync syncp; /* get_stats64 */ @@ -523,7 +524,7 @@ struct airoha_qdma { struct airoha_eth *eth; void __iomem *regs; - atomic_t users; + int users; struct airoha_irq_bank irq_banks[AIROHA_MAX_NUM_IRQ_BANKS]; @@ -531,23 +532,38 @@ struct airoha_qdma { struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; + + DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS); }; -struct airoha_gdm_port { +enum airoha_priv_flags { + AIROHA_PRIV_F_WAN = BIT(0), +}; + +struct airoha_gdm_dev { + struct airoha_gdm_port *port; struct airoha_qdma *qdma; struct airoha_eth *eth; - struct net_device *dev; - int id; - int nbq; - - struct airoha_hw_stats stats; DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); - /* qos stats counters */ u64 cpu_tx_packets; u64 fwd_tx_packets; + u32 flags; + int nbq; + + struct airoha_hw_stats stats; +}; + +struct airoha_gdm_port { + struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS]; + int id; + int users; + + /* protect concurrent hw_stats accesses */ + spinlock_t stats_lock; + struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS]; }; @@ -578,8 +594,10 @@ struct airoha_eth_soc_data { int num_xsi_rsts; int num_ppe; struct { - int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq); + int (*get_sport)(struct airoha_gdm_port *port, int nbq); u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq); + int (*get_dev_from_sport)(struct airoha_qdma_desc *desc, + u16 *port, u16 *dev); } ops; }; @@ -619,6 +637,8 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val); airoha_rmw((eth)->fe_regs, (offset), 0, (val)) #define airoha_fe_clear(eth, offset, val) \ airoha_rmw((eth)->fe_regs, (offset), (val), 0) +#define airoha_fe_get(eth, offset, mask) \ + FIELD_GET((mask), airoha_fe_rr((eth), (offset))) #define airoha_qdma_rr(qdma, offset) \ airoha_rr((qdma)->regs, (offset)) @@ -630,19 +650,17 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val); airoha_rmw((qdma)->regs, (offset), 0, (val)) #define airoha_qdma_clear(qdma, offset, val) \ airoha_rmw((qdma)->regs, (offset), (val), 0) +#define airoha_qdma_get(qdma, offset, mask) \ + FIELD_GET((mask), airoha_qdma_rr((qdma), (offset))) static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid) { return qid % ARRAY_SIZE(qdma->q_tx); } -static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port) +static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev) { - /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. - * GDM{2,3,4} can be used as wan port connected to an external - * phy module. - */ - return port->id == 1; + return !(dev->flags & AIROHA_PRIV_F_WAN); } static inline bool airoha_is_7581(struct airoha_eth *eth) @@ -655,19 +673,18 @@ static inline bool airoha_is_7583(struct airoha_eth *eth) return eth->soc->version == 0x7583; } -int airoha_get_fe_port(struct airoha_gdm_port *port); -bool airoha_is_valid_gdm_port(struct airoha_eth *eth, - struct airoha_gdm_port *port); +int airoha_get_fe_port(struct airoha_gdm_dev *dev); +bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, + struct airoha_gdm_dev *dev); -void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, - u8 fport); +void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, u16 hash, bool rx_wlan); int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); int airoha_ppe_init(struct airoha_eth *eth); void airoha_ppe_deinit(struct airoha_eth *eth); -void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); +void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr); u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe); struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash); diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c index 17dbdc832533..870d61fdd9c6 100644 --- a/drivers/net/ethernet/airoha/airoha_npu.c +++ b/drivers/net/ethernet/airoha/airoha_npu.c @@ -826,6 +826,6 @@ MODULE_FIRMWARE(NPU_EN7581_7996_FIRMWARE_DATA); MODULE_FIRMWARE(NPU_EN7581_7996_FIRMWARE_RV32); MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_DATA); MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_RV32); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("Airoha Network Processor Unit driver"); diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 5c9dff6bccd1..329e7c2aae89 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -80,14 +80,13 @@ bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index) static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) { - u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS); - - return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); + return airoha_fe_get(ppe->eth, REG_FE_FOE_TS, + AIROHA_FOE_IB1_BIND_TIMESTAMP); } -void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport) +void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) { - struct airoha_qdma *qdma = port->qdma; + struct airoha_qdma *qdma = dev->qdma; struct airoha_eth *eth = qdma->eth; u8 qdma_id = qdma - ð->qdma[0]; u32 fe_cpu_port; @@ -168,9 +167,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i), PPE_FLOW_CFG_IP6_6RD_MASK); - for (p = 0; p < ARRAY_SIZE(eth->ports); p++) { - struct airoha_gdm_port *port = eth->ports[p]; - + for (p = 0; p < ARRAY_SIZE(eth->ports); p++) airoha_fe_rmw(eth, REG_PPE_MTU(i, p), FP0_EGRESS_MTU_MASK | FP1_EGRESS_MTU_MASK, @@ -178,11 +175,27 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) AIROHA_MAX_MTU) | FIELD_PREP(FP1_EGRESS_MTU_MASK, AIROHA_MAX_MTU)); - if (!port) + } + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + int ppe_id; + u8 fport; + + if (!dev) continue; - airoha_ppe_set_cpu_port(port, i, - airoha_get_fe_port(port)); + ppe_id = !airoha_is_lan_gdm_dev(dev) && + airoha_ppe_is_enabled(eth, 1); + fport = airoha_get_fe_port(dev); + airoha_ppe_set_cpu_port(dev, ppe_id, fport); } } } @@ -264,7 +277,7 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr, path = &stack.path[stack.num_paths - 1]; if (path->type != DEV_PATH_MTK_WDMA) - return -1; + return -EINVAL; info->idx = path->mtk_wdma.wdma_idx; info->bss = path->mtk_wdma.bss; @@ -299,12 +312,12 @@ static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br, static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, struct airoha_foe_entry *hwe, - struct net_device *dev, int type, + struct net_device *netdev, int type, struct airoha_flow_data *data, int l4proto) { u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val; - int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev); + int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev); struct airoha_foe_mac_info_common *l2; u8 smac_id = 0xf; @@ -320,10 +333,11 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, hwe->ib1 = val; val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f); - if (dev) { + if (netdev) { struct airoha_wdma_info info = {}; - if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) { + if (!airoha_ppe_get_wdma_info(netdev, data->eth.h_dest, + &info)) { val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) | FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, FE_PSE_PORT_CDM4); @@ -333,13 +347,15 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID, info.wcid); } else { - struct airoha_gdm_port *port = netdev_priv(dev); - u8 pse_port; + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port; + u8 pse_port, channel; - if (!airoha_is_valid_gdm_port(eth, port)) + if (!airoha_is_valid_gdm_dev(eth, dev)) return -EINVAL; - if (dsa_port >= 0 || eth->ports[1]) + port = dev->port; + if (dsa_port >= 0 || airoha_is_lan_gdm_dev(dev)) pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; else @@ -347,12 +363,20 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, * loopback */ + /* For traffic forwarded to DSA devices select QoS + * channel according to the DSA user port index, rely + * on port id otherwise. + */ + channel = dsa_port >= 0 ? dsa_port : port->id; + channel = channel % AIROHA_NUM_QOS_CHANNELS; + qdata |= FIELD_PREP(AIROHA_FOE_CHANNEL, channel); + val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port) | AIROHA_FOE_IB2_PSE_QOS; /* For downlink traffic consume SRAM memory for hw * forwarding descriptors queue. */ - if (airoha_is_lan_gdm_port(port)) + if (airoha_is_lan_gdm_dev(dev)) val |= AIROHA_FOE_IB2_FAST_PATH; if (dsa_port >= 0) val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, @@ -1330,7 +1354,7 @@ static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe) { u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe); struct airoha_foe_entry *hwe = ppe->foe; - int i, err = 0; + int i; for (i = 0; i < sram_num_entries; i++) { int err; @@ -1338,10 +1362,10 @@ static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe) memset(&hwe[i], 0, sizeof(*hwe)); err = airoha_ppe_foe_commit_sram_entry(ppe, i); if (err) - break; + return err; } - return err; + return 0; } static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) @@ -1463,11 +1487,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan); } -void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) +void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr) { - struct airoha_eth *eth = port->qdma->eth; - struct net_device *dev = port->dev; - const u8 *addr = dev->dev_addr; + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; u32 val; val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; diff --git a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c index 0112c41150bb..e46a98514486 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c @@ -121,8 +121,6 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, case PPE_PKT_TYPE_IPV4_DSLITE: src_port = &hwe->ipv4.new_tuple.src_port; dest_port = &hwe->ipv4.new_tuple.dest_port; - fallthrough; - case PPE_PKT_TYPE_IPV4_ROUTE: src_addr = &hwe->ipv4.new_tuple.src_ip; dest_addr = &hwe->ipv4.new_tuple.dest_ip; seq_puts(m, " new="); diff --git a/drivers/net/ethernet/alibaba/Kconfig b/drivers/net/ethernet/alibaba/Kconfig new file mode 100644 index 000000000000..b8fd3c92fa4c --- /dev/null +++ b/drivers/net/ethernet/alibaba/Kconfig @@ -0,0 +1,28 @@ +# +# Alibaba network device configuration +# + +config NET_VENDOR_ALIBABA + bool "Alibaba Devices" + default y + help + If you have a network (Ethernet) device 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 Alibaba devices. If you say Y, you will be asked + for your specific device in the following questions. + +if NET_VENDOR_ALIBABA + +config ALIBABA_EEA + tristate "Alibaba Elastic Ethernet Adapter support" + depends on PCI_MSI + depends on 64BIT + select PAGE_POOL + help + This driver supports Alibaba Elastic Ethernet Adapter. + + To compile this driver as a module, choose M here. + +endif #NET_VENDOR_ALIBABA diff --git a/drivers/net/ethernet/alibaba/Makefile b/drivers/net/ethernet/alibaba/Makefile new file mode 100644 index 000000000000..63a527e79ba7 --- /dev/null +++ b/drivers/net/ethernet/alibaba/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Alibaba network device drivers. +# + +obj-$(CONFIG_ALIBABA_EEA) += eea/ diff --git a/drivers/net/ethernet/alibaba/eea/Makefile b/drivers/net/ethernet/alibaba/eea/Makefile new file mode 100644 index 000000000000..c38db22cca34 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/Makefile @@ -0,0 +1,9 @@ + +obj-$(CONFIG_ALIBABA_EEA) += eea.o +eea-y := eea_ring.o \ + eea_net.o \ + eea_pci.o \ + eea_adminq.o \ + eea_ethtool.o \ + eea_tx.o \ + eea_rx.o diff --git a/drivers/net/ethernet/alibaba/eea/eea_adminq.c b/drivers/net/ethernet/alibaba/eea/eea_adminq.c new file mode 100644 index 000000000000..dfad1bdbc44d --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_adminq.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include +#include +#include + +#include "eea_adminq.h" +#include "eea_net.h" +#include "eea_pci.h" +#include "eea_ring.h" + +#define EEA_AQ_CMD_CFG_QUERY ((0 << 8) | 0) + +#define EEA_AQ_CMD_QUEUE_CREATE ((1 << 8) | 0) +#define EEA_AQ_CMD_QUEUE_DESTROY_ALL ((1 << 8) | 1) + +#define EEA_AQ_CMD_HOST_INFO ((2 << 8) | 0) + +#define EEA_AQ_CMD_DEV_STATUS ((3 << 8) | 0) + +#define EEA_RING_DESC_F_AQ_PHASE (BIT(15) | BIT(7)) + +#define EEA_QUEUE_FLAGS_HW_SPLIT_HDR BIT(0) +#define EEA_QUEUE_FLAGS_SQCQ BIT(1) +#define EEA_QUEUE_FLAGS_HWTS BIT(2) + +struct eea_aq_create { + __le32 flags; + /* queue index. + * rx: 0 == qidx % 2 + * tx: 1 == qidx % 2 + */ + __le16 qidx; + /* the depth of the queue */ + __le16 depth; + /* 0: without SPLIT HDR + * 1: 128B + * 2: 256B + * 3: 512B + */ + u8 hdr_buf_size; + u8 sq_desc_size; + u8 cq_desc_size; + u8 reserve0; + /* The vector for the irq. rx,tx share the same vector */ + __le16 msix_vector; + __le16 reserve; + /* sq ring cfg. */ + __le32 sq_addr_low; + __le32 sq_addr_high; + /* cq ring cfg. Just valid when flags include EEA_QUEUE_FLAGS_SQCQ. */ + __le32 cq_addr_low; + __le32 cq_addr_high; +}; + +struct eea_aq_queue_drv_status { + __le16 qidx; + + __le16 sq_head; + __le16 cq_head; + __le16 reserved; +}; + +#define EEA_OS_DISTRO 0 +#define EEA_DRV_TYPE 0 +#define EEA_OS_LINUX 1 +#define EEA_SPEC_VER_MAJOR 1 +#define EEA_SPEC_VER_MINOR 0 + +struct eea_aq_host_info_cfg { + __le16 os_type; + __le16 os_dist; + __le16 drv_type; + + __le16 kern_ver_major; + __le16 kern_ver_minor; + __le16 kern_ver_sub_minor; + + __le16 drv_ver_major; + __le16 drv_ver_minor; + __le16 drv_ver_sub_minor; + + __le16 spec_ver_major; + __le16 spec_ver_minor; + __le16 pci_bdf; + __le32 pci_domain; + + u8 os_ver_str[64]; + u8 isa_str[64]; +}; + +#define EEA_HINFO_MAX_REP_LEN 1024 +#define EEA_HINFO_REP_BAD 2 + +struct eea_aq_host_info_rep { + u8 op_code; + u8 has_reply; + u8 reply_str[EEA_HINFO_MAX_REP_LEN]; +}; + +static struct eea_ring *qid_to_ering(struct eea_net *enet, u32 qid) +{ + struct eea_ring *ering; + + if (qid % 2 == 0) + ering = enet->rx[qid / 2]->ering; + else + ering = enet->tx[qid / 2].ering; + + return ering; +} + +#define EEA_AQ_TIMEOUT_US (60 * 1000 * 1000) + +static void eea_device_broken(struct eea_net *enet) +{ + if (enet->adminq.broken) + return; + + eea_device_reset(enet->edev); + enet->adminq.broken = true; +} + +static int eea_adminq_submit(struct eea_net *enet, u16 cmd, + dma_addr_t req_addr, dma_addr_t res_addr, + u32 req_size, u32 res_size, u32 *reply_len) +{ + struct eea_aq_cdesc *cdesc; + struct eea_aq_desc *desc; + int ret; + + if (enet->adminq.broken) + return -EIO; + + desc = eea_ering_aq_alloc_desc(enet->adminq.ring); + + desc->classid = cmd >> 8; + desc->command = cmd & 0xff; + + desc->data_addr = cpu_to_le64(req_addr); + desc->data_len = cpu_to_le32(req_size); + + desc->reply_addr = cpu_to_le64(res_addr); + desc->reply_len = cpu_to_le32(res_size); + + /* for update flags */ + dma_wmb(); + + desc->flags = cpu_to_le16(enet->adminq.phase); + + eea_ering_sq_commit_desc(enet->adminq.ring); + + eea_ering_kick(enet->adminq.ring); + + ++enet->adminq.num; + + if ((enet->adminq.num % enet->adminq.ring->num) == 0) + enet->adminq.phase ^= EEA_RING_DESC_F_AQ_PHASE; + + ret = read_poll_timeout(eea_ering_cq_get_desc, cdesc, cdesc, 10, + EEA_AQ_TIMEOUT_US, false, enet->adminq.ring); + if (ret) { + netdev_err(enet->netdev, + "adminq exec timeout. cmd: %d reset device.\n", + cmd); + /* The device must be reset before unmapping buffers to avoid + * potential DMA writes after the memory is freed. + */ + eea_device_broken(enet); + return ret; + } + + /* Returns 0 on success, or a negative error code on failure. */ + ret = le32_to_cpu(cdesc->status); + + eea_ering_cq_ack_desc(enet->adminq.ring, 1); + + if (ret) + netdev_err(enet->netdev, + "adminq exec failed. cmd: %d ret %d\n", cmd, ret); + else + *reply_len = le32_to_cpu(cdesc->reply_len); + + return ret; +} + +static int eea_adminq_exec(struct eea_net *enet, u16 cmd, + void *req, u32 req_size, + void *res, u32 res_size, + u32 *reply) +{ + dma_addr_t req_addr = 0, res_addr = 0; + struct device *dma; + u32 reply_len = 0; + int ret; + + if (reply) + *reply = 0; + + dma = enet->edev->dma_dev; + + if (req) { + req_addr = dma_map_single(dma, req, req_size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dma, req_addr))) + return -ENOMEM; + } + + if (res) { + res_addr = dma_map_single(dma, res, res_size, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dma, res_addr))) { + ret = -ENOMEM; + goto err_unmap_req; + } + } + + mutex_lock(&enet->adminq.lock); + ret = eea_adminq_submit(enet, cmd, req_addr, res_addr, + req_size, res_size, &reply_len); + mutex_unlock(&enet->adminq.lock); + if (res) { + dma_unmap_single(dma, res_addr, res_size, DMA_FROM_DEVICE); + + if (ret) + memset(res, 0, res_size); + else if (res_size > reply_len) + memset(res + reply_len, 0, res_size - reply_len); + + if (reply) + *reply = reply_len; + } + +err_unmap_req: + if (req) + dma_unmap_single(dma, req_addr, req_size, DMA_TO_DEVICE); + + return ret; +} + +void eea_destroy_adminq(struct eea_net *enet) +{ + struct eea_aq *aq; + + aq = &enet->adminq; + + if (aq->ring) { + eea_ering_free(aq->ring); + aq->ring = NULL; + aq->phase = 0; + } + + kfree(aq->q_req_buf); + kfree(aq->q_res_buf); + + aq->q_req_buf = NULL; + aq->q_res_buf = NULL; +} + +int eea_create_adminq(struct eea_net *enet, u32 qid) +{ + u32 db_size, q_size, num; + struct eea_ring *ering; + struct eea_aq *aq; + int err = -ENOMEM; + + num = enet->edev->rx_num + enet->edev->tx_num; + aq = &enet->adminq; + + ering = eea_ering_alloc(qid, 64, enet->edev, sizeof(struct eea_aq_desc), + sizeof(struct eea_aq_cdesc), "adminq"); + if (!ering) + return -ENOMEM; + + aq->ring = ering; + + err = eea_pci_active_aq(ering, qid / 2 + 1); + if (err) + goto err; + + aq->phase = BIT(7); + aq->num = 0; + + q_size = sizeof(*aq->q_req_buf) * num; + db_size = sizeof(*aq->q_res_buf) * num; + + aq->q_req_size = q_size; + aq->q_res_size = db_size; + + err = -ENOMEM; + + aq->q_req_buf = kzalloc(q_size, GFP_KERNEL); + if (!aq->q_req_buf) + goto err; + + aq->q_res_buf = kzalloc(db_size, GFP_KERNEL); + if (!aq->q_res_buf) + goto err; + + /* Before we set up the AQ, the device remains in an inactive state, so + * there will be no DMA operations. If the 'set up AQ' process fails, we + * can safely free the DMA-related memory. + */ + err = eea_pci_set_aq_up(enet->edev); + if (err) + goto err; + + aq->broken = false; + + mutex_init(&aq->lock); + + return 0; + +err: + eea_destroy_adminq(enet); + return err; +} + +int eea_adminq_query_cfg(struct eea_net *enet, struct eea_aq_cfg *cfg) +{ + return eea_adminq_exec(enet, EEA_AQ_CMD_CFG_QUERY, NULL, 0, cfg, + sizeof(*cfg), NULL); +} + +static void qcfg_fill(struct eea_aq_create *qcfg, struct eea_ring *ering, + u32 flags) +{ + qcfg->flags = cpu_to_le32(flags); + qcfg->qidx = cpu_to_le16(ering->index); + qcfg->depth = cpu_to_le16(ering->num); + + qcfg->hdr_buf_size = flags & EEA_QUEUE_FLAGS_HW_SPLIT_HDR ? 1 : 0; + qcfg->sq_desc_size = ering->sq.desc_size; + qcfg->cq_desc_size = ering->cq.desc_size; + qcfg->msix_vector = cpu_to_le16(ering->msix_vec); + + qcfg->sq_addr_low = cpu_to_le32(lower_32_bits(ering->sq.dma_addr)); + qcfg->sq_addr_high = cpu_to_le32(upper_32_bits(ering->sq.dma_addr)); + + qcfg->cq_addr_low = cpu_to_le32(lower_32_bits(ering->cq.dma_addr)); + qcfg->cq_addr_high = cpu_to_le32(upper_32_bits(ering->cq.dma_addr)); +} + +int eea_adminq_create_q(struct eea_net *enet, u32 num, u32 flags) +{ + int i, db_size, q_size, err = -ENOMEM; + struct eea_net_cfg *cfg; + struct eea_ring *ering; + struct eea_aq *aq; + u32 reply_len; + + cfg = &enet->cfg; + aq = &enet->adminq; + + if (cfg->split_hdr) + flags |= EEA_QUEUE_FLAGS_HW_SPLIT_HDR; + + flags |= EEA_QUEUE_FLAGS_SQCQ; + flags |= EEA_QUEUE_FLAGS_HWTS; + + q_size = sizeof(*aq->q_req_buf) * num; + db_size = sizeof(*aq->q_res_buf) * num; + + for (i = 0; i < num; i++) { + ering = qid_to_ering(enet, i); + qcfg_fill(aq->q_req_buf + i, ering, flags); + } + + err = eea_adminq_exec(enet, EEA_AQ_CMD_QUEUE_CREATE, + aq->q_req_buf, q_size, + aq->q_res_buf, db_size, + &reply_len); + if (err) + return err; + + if (reply_len != db_size) { + eea_adminq_destroy_all_q(enet); + netdev_err(enet->netdev, "invalid reply len %u\n", reply_len); + return -EINVAL; + } + + for (i = 0; i < num; i++) { + ering = qid_to_ering(enet, i); + ering->db = eea_pci_db_addr(ering->edev, + le32_to_cpu(aq->q_res_buf[i])); + if (!ering->db) { + netdev_err(enet->netdev, "invalid db off %u\n", + le32_to_cpu(aq->q_res_buf[i])); + goto err; + } + } + + return err; + +err: + eea_adminq_destroy_all_q(enet); + for (i = 0; i < num; i++) { + ering = qid_to_ering(enet, i); + ering->db = NULL; + } + + return -EIO; +} + +int eea_adminq_destroy_all_q(struct eea_net *enet) +{ + int err; + + err = eea_adminq_exec(enet, EEA_AQ_CMD_QUEUE_DESTROY_ALL, NULL, 0, + NULL, 0, NULL); + if (err) { + /* The device must be reset before unmapping buffers to avoid + * potential DMA writes after the memory is freed. + */ + mutex_lock(&enet->adminq.lock); + eea_device_broken(enet); + mutex_unlock(&enet->adminq.lock); + + netdev_err(enet->netdev, "QUEUE_DESTROY fail: reset device.\n"); + } + + return err; +} + +/* The caller must ensure that both the 'rx' and 'tx' arrays are valid. */ +int eea_adminq_dev_status(struct eea_net *enet, + struct eea_aq_dev_status *dstatus) +{ + struct eea_aq_queue_drv_status *drv_status; + struct __eea_aq_dev_status *dev_status; + int err, i, io_num, size, q_num; + struct eea_ring *ering; + void *rep, *req; + + q_num = enet->cfg.rx_ring_num + enet->cfg.tx_ring_num + 1; + io_num = enet->cfg.rx_ring_num + enet->cfg.tx_ring_num; + + req = kcalloc(q_num, sizeof(struct eea_aq_queue_drv_status), + GFP_KERNEL); + if (!req) + return -ENOMEM; + + size = struct_size(dev_status, q_status, q_num); + + rep = kzalloc(size, GFP_KERNEL); + if (!rep) { + kfree(req); + return -ENOMEM; + } + + drv_status = req; + for (i = 0; i < io_num; ++i, ++drv_status) { + ering = qid_to_ering(enet, i); + drv_status->qidx = cpu_to_le16(i); + drv_status->cq_head = cpu_to_le16(ering->cq.head); + drv_status->sq_head = cpu_to_le16(ering->sq.head); + } + + drv_status->qidx = cpu_to_le16(i); + drv_status->cq_head = cpu_to_le16(enet->adminq.ring->cq.head); + drv_status->sq_head = cpu_to_le16(enet->adminq.ring->sq.head); + + err = eea_adminq_exec(enet, EEA_AQ_CMD_DEV_STATUS, req, + q_num * sizeof(struct eea_aq_queue_drv_status), + rep, size, NULL); + kfree(req); + if (err) { + kfree(rep); + return err; + } + + dstatus->num = q_num; + dstatus->status = rep; + + return 0; +} + +void eea_adminq_config_host_info(struct eea_net *enet) +{ + struct device *dev = enet->edev->dma_dev; + struct eea_aq_host_info_cfg *cfg; + struct eea_aq_host_info_rep *rep; + int rc = -ENOMEM; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return; + + rep = kzalloc(sizeof(*rep), GFP_KERNEL); + if (!rep) + goto err_free_cfg; + + cfg->os_type = cpu_to_le16(EEA_OS_LINUX); + cfg->os_dist = cpu_to_le16(EEA_OS_DISTRO); + cfg->drv_type = cpu_to_le16(EEA_DRV_TYPE); + + cfg->kern_ver_major = cpu_to_le16(LINUX_VERSION_MAJOR); + cfg->kern_ver_minor = cpu_to_le16(LINUX_VERSION_PATCHLEVEL); + cfg->kern_ver_sub_minor = cpu_to_le16(LINUX_VERSION_SUBLEVEL); + + cfg->drv_ver_major = cpu_to_le16(EEA_VER_MAJOR); + cfg->drv_ver_minor = cpu_to_le16(EEA_VER_MINOR); + cfg->drv_ver_sub_minor = cpu_to_le16(EEA_VER_SUB_MINOR); + + cfg->spec_ver_major = cpu_to_le16(EEA_SPEC_VER_MAJOR); + cfg->spec_ver_minor = cpu_to_le16(EEA_SPEC_VER_MINOR); + + cfg->pci_bdf = cpu_to_le16(eea_pci_bdf(enet->edev)); + cfg->pci_domain = cpu_to_le32(eea_pci_domain_nr(enet->edev)); + + strscpy(cfg->os_ver_str, utsname()->release, sizeof(cfg->os_ver_str)); + strscpy(cfg->isa_str, utsname()->machine, sizeof(cfg->isa_str)); + + rc = eea_adminq_exec(enet, EEA_AQ_CMD_HOST_INFO, + cfg, sizeof(*cfg), rep, sizeof(*rep), NULL); + + if (!rc) { + if (rep->op_code == EEA_HINFO_REP_BAD) + dev_warn(dev, "The hardware-driven state validation may be abnormal.\n"); + + if (rep->has_reply) { + char buf[EEA_HINFO_MAX_REP_LEN] = {0}; + + rep->reply_str[EEA_HINFO_MAX_REP_LEN - 1] = '\0'; + + string_escape_str(rep->reply_str, buf, sizeof(buf), + ESCAPE_NP, NULL); + + buf[EEA_HINFO_MAX_REP_LEN - 1] = '\0'; + + dev_warn(dev, "Device replied: %s\n", buf); + } + } + + kfree(rep); +err_free_cfg: + kfree(cfg); +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_adminq.h b/drivers/net/ethernet/alibaba/eea/eea_adminq.h new file mode 100644 index 000000000000..0182f5641fcf --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_adminq.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_ADMINQ_H__ +#define __EEA_ADMINQ_H__ + +struct eea_aq_cfg { + __le32 rx_depth_max; + __le32 rx_depth_def; + + __le32 tx_depth_max; + __le32 tx_depth_def; + + __le32 max_tso_size; + __le32 max_tso_segs; + + u8 mac[ETH_ALEN]; + __le16 status; + + __le16 mtu; + __le16 reserved0; + __le16 reserved1; + u8 reserved2; + u8 reserved3; + + __le16 reserved4; + __le16 reserved5; + __le16 reserved6; +}; + +struct eea_aq_queue_status { + __le16 qidx; +#define EEA_QUEUE_STATUS_OK 0 +#define EEA_QUEUE_STATUS_NEED_RESET 1 + __le16 status; +}; + +struct __eea_aq_dev_status { +#define EEA_LINK_DOWN_STATUS 0 +#define EEA_LINK_UP_STATUS 1 + __le16 link_status; + __le16 reserved; + + struct eea_aq_queue_status q_status[]; +}; + +struct eea_aq_dev_status { + u32 num; + struct __eea_aq_dev_status *status; +}; + +struct eea_aq { + struct eea_ring *ring; + u32 num; + bool broken; + u16 phase; + + /* lock for adminq exec */ + struct mutex lock; + + u32 q_req_size; + u32 q_res_size; + struct eea_aq_create *q_req_buf; + __le32 *q_res_buf; +}; + +struct eea_net; + +int eea_create_adminq(struct eea_net *enet, u32 qid); +void eea_destroy_adminq(struct eea_net *enet); + +int eea_adminq_query_cfg(struct eea_net *enet, struct eea_aq_cfg *cfg); + +int eea_adminq_create_q(struct eea_net *enet, u32 num, u32 flags); +int eea_adminq_destroy_all_q(struct eea_net *enet); +int eea_adminq_dev_status(struct eea_net *enet, + struct eea_aq_dev_status *dstatus); +void eea_adminq_config_host_info(struct eea_net *enet); +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_desc.h b/drivers/net/ethernet/alibaba/eea/eea_desc.h new file mode 100644 index 000000000000..8d94a0f0f237 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_desc.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_DESC_H__ +#define __EEA_DESC_H__ + +#define EEA_DESC_TS_MASK GENMASK_ULL(47, 0) +#define EEA_DESC_TS(desc) (le64_to_cpu((desc)->ts) & EEA_DESC_TS_MASK) + +struct eea_aq_desc { + __le16 flags; + __le16 id; + __le16 reserved; + u8 classid; + u8 command; + __le64 data_addr; + __le64 reply_addr; + __le32 data_len; + __le32 reply_len; +}; + +struct eea_aq_cdesc { + __le16 flags; + __le16 id; +#define EEA_OK 0 +#define EEA_ERR 0xffffffff + __le32 status; + __le32 reply_len; + __le32 reserved1; + + __le64 reserved2; + __le64 reserved3; +}; + +struct eea_rx_desc_no_hdr { + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + __le64 addr; +}; + +struct eea_rx_desc { + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + __le64 addr; + + __le64 hdr_addr; + __le32 reserved2; + __le32 reserved3; +}; + +#define EEA_RX_CDESC_HDR_LEN_MASK GENMASK_ULL(9, 0) + +struct eea_rx_cdesc { +#define EEA_DESC_F_DATA_VALID BIT(6) +#define EEA_DESC_F_SPLIT_HDR BIT(5) + __le16 flags; + __le16 id; + __le16 len; +#define EEA_NET_PT_NONE 0 +#define EEA_NET_PT_IPv4 1 +#define EEA_NET_PT_TCPv4 2 +#define EEA_NET_PT_UDPv4 3 +#define EEA_NET_PT_IPv6 4 +#define EEA_NET_PT_TCPv6 5 +#define EEA_NET_PT_UDPv6 6 +#define EEA_NET_PT_IPv6_EX 7 +#define EEA_NET_PT_TCPv6_EX 8 +#define EEA_NET_PT_UDPv6_EX 9 + /* [9:0] is packet type. */ + __le16 type; + + /* hw timestamp [0:47]: ts */ + __le64 ts; + + __le32 hash; + + /* 0-9: hdr_len split header + * 10-15: reserved1 + */ + __le16 len_ex; + __le16 reserved2; + + __le32 reserved3; + __le32 reserved4; +}; + +#define EEA_TX_GSO_NONE 0 +#define EEA_TX_GSO_TCPV4 1 +#define EEA_TX_GSO_TCPV6 4 +#define EEA_TX_GSO_UDP_L4 5 +#define EEA_TX_GSO_ECN 0x80 + +struct eea_tx_desc { +#define EEA_DESC_F_DO_CSUM BIT(6) + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + __le64 addr; + + __le16 csum_start; + __le16 csum_offset; + u8 gso_type; + u8 reserved2; + __le16 gso_size; + __le64 reserved3; +}; + +struct eea_tx_cdesc { + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + /* hw timestamp [0:47]: ts */ + __le64 ts; +}; + +#define EEA_DB_FLAGS_OFF 0 +#define EEA_DB_IDX_OFF (2 * 8) +#define EEA_DB_TX_CQ_HEAD_OFF (4 * 8) +#define EEA_DB_RX_CQ_HEAD_OFF (6 * 8) + +#define EEA_IDX_PRESENT BIT(0) +#define EEA_IRQ_MASK BIT(1) +#define EEA_IRQ_UNMASK BIT(2) +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_ethtool.c b/drivers/net/ethernet/alibaba/eea/eea_ethtool.c new file mode 100644 index 000000000000..479779aa2dd8 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_ethtool.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include +#include + +#include "eea_adminq.h" +#include "eea_net.h" +#include "eea_pci.h" + +struct eea_stat_desc { + char desc[ETH_GSTRING_LEN]; + size_t offset; +}; + +#define EEA_TX_STAT(m) {#m, offsetof(struct eea_tx_stats, m)} +#define EEA_RX_STAT(m) {#m, offsetof(struct eea_rx_stats, m)} + +static const struct eea_stat_desc eea_rx_stats_desc[] = { + EEA_RX_STAT(descs), + EEA_RX_STAT(kicks), +}; + +static const struct eea_stat_desc eea_tx_stats_desc[] = { + EEA_TX_STAT(descs), + EEA_TX_STAT(kicks), +}; + +#define EEA_TX_STATS_LEN ARRAY_SIZE(eea_tx_stats_desc) +#define EEA_RX_STATS_LEN ARRAY_SIZE(eea_rx_stats_desc) + +static void eea_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct eea_net *enet = netdev_priv(netdev); + struct eea_device *edev = enet->edev; + + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->bus_info, eea_pci_name(edev), sizeof(info->bus_info)); +} + +static void eea_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct eea_net *enet = netdev_priv(netdev); + + ring->rx_max_pending = enet->cfg_hw.rx_ring_depth; + ring->tx_max_pending = enet->cfg_hw.tx_ring_depth; + ring->rx_pending = enet->cfg.rx_ring_depth; + ring->tx_pending = enet->cfg.tx_ring_depth; + + kernel_ring->tcp_data_split = enet->cfg.split_hdr ? + ETHTOOL_TCP_DATA_SPLIT_ENABLED : + ETHTOOL_TCP_DATA_SPLIT_DISABLED; +} + +static int eea_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct eea_net *enet = netdev_priv(netdev); + struct eea_net_init_ctx ctx; + bool need_update = false; + struct eea_net_cfg *cfg; + bool sh; + + if (ring->rx_pending < EEA_NET_IO_RING_DEPTH_MIN || + ring->tx_pending < EEA_NET_IO_RING_DEPTH_MIN) + return -EINVAL; + + if (!is_power_of_2(ring->rx_pending) || + !is_power_of_2(ring->tx_pending)) + return -EINVAL; + + eea_init_ctx(enet, &ctx); + + cfg = &ctx.cfg; + + if (ring->rx_pending != cfg->rx_ring_depth) + need_update = true; + + if (ring->tx_pending != cfg->tx_ring_depth) + need_update = true; + + sh = false; + + switch (kernel_ring->tcp_data_split) { + case ETHTOOL_TCP_DATA_SPLIT_ENABLED: + sh = true; + break; + + case ETHTOOL_TCP_DATA_SPLIT_DISABLED: + sh = false; + break; + + case ETHTOOL_TCP_DATA_SPLIT_UNKNOWN: + sh = !!cfg->split_hdr; + break; + } + + if (sh != !!(cfg->split_hdr)) + need_update = true; + + if (!need_update) + return 0; + + cfg->rx_ring_depth = ring->rx_pending; + cfg->tx_ring_depth = ring->tx_pending; + + /* By default, enet->cfg_hw.split_hdr is 128. */ + cfg->split_hdr = sh ? enet->cfg_hw.split_hdr : 0; + + return eea_reset_hw_resources(enet, &ctx); +} + +static int eea_set_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct eea_net *enet = netdev_priv(netdev); + u16 queue_pairs = channels->combined_count; + struct eea_net_init_ctx ctx; + struct eea_net_cfg *cfg; + + eea_init_ctx(enet, &ctx); + + cfg = &ctx.cfg; + + cfg->rx_ring_num = queue_pairs; + cfg->tx_ring_num = queue_pairs; + + return eea_reset_hw_resources(enet, &ctx); +} + +static void eea_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct eea_net *enet = netdev_priv(netdev); + + channels->combined_count = enet->cfg.rx_ring_num; + channels->max_combined = enet->cfg_hw.rx_ring_num; +} + +static void eea_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct eea_net *enet = netdev_priv(netdev); + u8 *p = data; + u32 i, j; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < enet->cfg.rx_ring_num; i++) { + for (j = 0; j < EEA_RX_STATS_LEN; j++) + ethtool_sprintf(&p, "rx%u_%s", i, + eea_rx_stats_desc[j].desc); + } + + for (i = 0; i < enet->cfg.tx_ring_num; i++) { + for (j = 0; j < EEA_TX_STATS_LEN; j++) + ethtool_sprintf(&p, "tx%u_%s", i, + eea_tx_stats_desc[j].desc); + } +} + +static int eea_get_sset_count(struct net_device *netdev, int sset) +{ + struct eea_net *enet = netdev_priv(netdev); + + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; + + return enet->cfg.rx_ring_num * EEA_RX_STATS_LEN + + enet->cfg.tx_ring_num * EEA_TX_STATS_LEN; +} + +static void eea_stats_fill_for_q(struct u64_stats_sync *syncp, u32 num, + const struct eea_stat_desc *desc, + u64 *data, u32 idx) +{ + void *stats_base = syncp; + u32 start, i; + + do { + start = u64_stats_fetch_begin(syncp); + for (i = 0; i < num; i++) + data[idx + i] = + u64_stats_read(stats_base + desc[i].offset); + + } while (u64_stats_fetch_retry(syncp, start)); + + BUILD_BUG_ON(offsetof(struct eea_tx_stats, syncp)); + BUILD_BUG_ON(offsetof(struct eea_rx_stats, syncp)); +} + +static void eea_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct eea_net *enet = netdev_priv(netdev); + u32 i, idx = 0; + + ASSERT_RTNL(); + + if (enet->rx) { + for (i = 0; i < enet->cfg.rx_ring_num; i++) { + struct eea_net_rx *rx = enet->rx[i]; + + eea_stats_fill_for_q(&rx->stats.syncp, EEA_RX_STATS_LEN, + eea_rx_stats_desc, data, idx); + + idx += EEA_RX_STATS_LEN; + } + } + + if (enet->tx) { + for (i = 0; i < enet->cfg.tx_ring_num; i++) { + struct eea_net_tx *tx = &enet->tx[i]; + + eea_stats_fill_for_q(&tx->stats.syncp, EEA_TX_STATS_LEN, + eea_tx_stats_desc, data, idx); + + idx += EEA_TX_STATS_LEN; + } + } +} + +void eea_update_rx_stats(struct eea_rx_stats *rx_stats, + struct eea_rx_ctx_stats *stats) +{ + u64_stats_update_begin(&rx_stats->syncp); + u64_stats_add(&rx_stats->descs, stats->descs); + u64_stats_add(&rx_stats->packets, stats->packets); + u64_stats_add(&rx_stats->bytes, stats->bytes); + u64_stats_add(&rx_stats->drops, stats->drops); + u64_stats_add(&rx_stats->split_hdr_bytes, stats->split_hdr_bytes); + u64_stats_add(&rx_stats->split_hdr_packets, stats->split_hdr_packets); + u64_stats_add(&rx_stats->length_errors, stats->length_errors); + u64_stats_add(&rx_stats->kicks, stats->kicks); + u64_stats_update_end(&rx_stats->syncp); +} + +static int eea_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct eea_net *enet = netdev_priv(netdev); + + cmd->base.speed = enet->speed; + cmd->base.duplex = enet->duplex; + cmd->base.port = PORT_OTHER; + + return 0; +} + +const struct ethtool_ops eea_ethtool_ops = { + .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT, + .get_drvinfo = eea_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ringparam = eea_get_ringparam, + .set_ringparam = eea_set_ringparam, + .set_channels = eea_set_channels, + .get_channels = eea_get_channels, + .get_strings = eea_get_strings, + .get_sset_count = eea_get_sset_count, + .get_ethtool_stats = eea_get_ethtool_stats, + .get_link_ksettings = eea_get_link_ksettings, +}; diff --git a/drivers/net/ethernet/alibaba/eea/eea_ethtool.h b/drivers/net/ethernet/alibaba/eea/eea_ethtool.h new file mode 100644 index 000000000000..08c824a116de --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_ethtool.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_ETHTOOL_H__ +#define __EEA_ETHTOOL_H__ + +struct eea_tx_stats { + struct u64_stats_sync syncp; + u64_stats_t descs; + u64_stats_t packets; + u64_stats_t bytes; + u64_stats_t drops; + u64_stats_t kicks; +}; + +struct eea_rx_ctx_stats { + u64 descs; + u64 packets; + u64 bytes; + u64 drops; + u64 split_hdr_bytes; + u64 split_hdr_packets; + u64 kicks; + u64 length_errors; +}; + +struct eea_rx_stats { + struct u64_stats_sync syncp; + u64_stats_t descs; + u64_stats_t packets; + u64_stats_t bytes; + u64_stats_t drops; + u64_stats_t kicks; + u64_stats_t split_hdr_bytes; + u64_stats_t split_hdr_packets; + + u64_stats_t length_errors; +}; + +void eea_update_rx_stats(struct eea_rx_stats *rx_stats, + struct eea_rx_ctx_stats *stats); + +extern const struct ethtool_ops eea_ethtool_ops; +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_net.c b/drivers/net/ethernet/alibaba/eea/eea_net.c new file mode 100644 index 000000000000..63e68580de94 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_net.c @@ -0,0 +1,887 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include +#include +#include +#include + +#include "eea_adminq.h" +#include "eea_net.h" +#include "eea_pci.h" +#include "eea_ring.h" + +#define EEA_SPLIT_HDR_SIZE ALIGN(128, L1_CACHE_BYTES) + +static irqreturn_t eea_irq_handler(int irq, void *data) +{ + struct eea_irq_blk *blk = data; + + napi_schedule_irqoff(&blk->napi); + + return IRQ_HANDLED; +} + +static void eea_free_irq_blk(struct eea_net *enet) +{ + struct eea_irq_blk *blk; + u32 num; + int i; + + if (!enet->irq_blks) + return; + + num = enet->edev->rx_num; + + for (i = 0; i < num; i++) { + blk = &enet->irq_blks[i]; + + if (blk->ready) + eea_pci_free_irq(blk); + + blk->ready = false; + } + + kvfree(enet->irq_blks); + enet->irq_blks = NULL; +} + +/* The driver will always attempt to allocate IRQ blocks based on the maximum + * possible queue num. + */ +static int eea_alloc_irq_blks(struct eea_net *enet) +{ + struct eea_device *edev = enet->edev; + struct eea_irq_blk *blk, *irq_blks; + int i, err, num; + + num = enet->edev->rx_num; + + irq_blks = kvcalloc(num, sizeof(*blk), GFP_KERNEL); + if (!irq_blks) + return -ENOMEM; + + enet->irq_blks = irq_blks; + + for (i = 0; i < num; i++) { + blk = &irq_blks[i]; + blk->idx = i; + + /* vec 0 is for error notify. */ + blk->msix_vec = i + 1; + + err = eea_pci_request_irq(edev, blk, eea_irq_handler); + if (err) + goto err_free_irq_blk; + + blk->ready = true; + } + + return 0; + +err_free_irq_blk: + eea_free_irq_blk(enet); + return err; +} + +static int eea_update_queues(struct eea_net *enet) +{ + return netif_set_real_num_queues(enet->netdev, enet->cfg.tx_ring_num, + enet->cfg.rx_ring_num); +} + +void eea_init_ctx(struct eea_net *enet, struct eea_net_init_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->netdev = enet->netdev; + ctx->edev = enet->edev; + ctx->cfg = enet->cfg; +} + +static void eea_bind_q_and_cfg(struct eea_net *enet, + struct eea_net_init_ctx *ctx) +{ + struct eea_irq_blk *blk; + struct eea_net_rx *rx; + struct eea_net_tx *tx; + int i; + + /* Since 'ndo_get_stats64' is not called in softirq context, there is no + * need to use 'spin_lock_bh'. + */ + spin_lock(&enet->stats_lock); + + enet->cfg = ctx->cfg; + enet->rx = ctx->rx; + enet->tx = ctx->tx; + + for (i = 0; i < ctx->cfg.rx_ring_num; i++) { + blk = &enet->irq_blks[i]; + + rx = ctx->rx[i]; + tx = &ctx->tx[i]; + + rx->enet = enet; + rx->napi = &blk->napi; + rx->ering->msix_vec = blk->msix_vec; + + tx->enet = enet; + tx->ering->msix_vec = blk->msix_vec; + + blk->rx = rx; + } + + spin_unlock(&enet->stats_lock); +} + +static void eea_unbind_q_and_cfg(struct eea_net *enet, + struct eea_net_init_ctx *ctx) +{ + struct eea_irq_blk *blk; + struct eea_net_rx *rx; + int i; + + spin_lock(&enet->stats_lock); + + ctx->cfg = enet->cfg; + ctx->rx = enet->rx; + ctx->tx = enet->tx; + + enet->rx = NULL; + enet->tx = NULL; + + for (i = 0; i < ctx->cfg.rx_ring_num; i++) { + blk = &enet->irq_blks[i]; + + rx = ctx->rx[i]; + + rx->napi = NULL; + + blk->rx = NULL; + } + + spin_unlock(&enet->stats_lock); +} + +static void eea_free_rxtx_q_mem(struct eea_net_init_ctx *ctx) +{ + struct eea_net_rx *rx; + struct eea_net_tx *tx; + int i; + + for (i = 0; i < ctx->cfg.rx_ring_num; i++) { + rx = ctx->rx[i]; + tx = &ctx->tx[i]; + + eea_free_rx(rx, &ctx->cfg); + eea_free_tx(tx, &ctx->cfg); + } + + kvfree(ctx->rx); + kvfree(ctx->tx); +} + +/* alloc tx/rx: struct, ring, meta, pp, napi */ +static int eea_alloc_rxtx_q_mem(struct eea_net_init_ctx *ctx) +{ + struct eea_net_rx *rx; + struct eea_net_tx *tx; + int err, i; + + ctx->tx = kvcalloc(ctx->cfg.tx_ring_num, sizeof(*ctx->tx), GFP_KERNEL); + if (!ctx->tx) + return -ENOMEM; + + ctx->rx = kvcalloc(ctx->cfg.rx_ring_num, sizeof(*ctx->rx), GFP_KERNEL); + if (!ctx->rx) + goto err_free_tx; + + ctx->cfg.rx_sq_desc_size = sizeof(struct eea_rx_desc); + ctx->cfg.rx_cq_desc_size = sizeof(struct eea_rx_cdesc); + ctx->cfg.tx_sq_desc_size = sizeof(struct eea_tx_desc); + ctx->cfg.tx_cq_desc_size = sizeof(struct eea_tx_cdesc); + + /* ethtool may config this. */ + if (!ctx->cfg.split_hdr) + ctx->cfg.rx_sq_desc_size = sizeof(struct eea_rx_desc_no_hdr); + + for (i = 0; i < ctx->cfg.rx_ring_num; i++) { + rx = eea_alloc_rx(ctx, i); + if (!rx) + goto err_free; + + ctx->rx[i] = rx; + + tx = ctx->tx + i; + err = eea_alloc_tx(ctx, tx, i); + if (err) + goto err_free; + } + + return 0; + +err_free: + for (i = 0; i < ctx->cfg.rx_ring_num; i++) { + rx = ctx->rx[i]; + tx = ctx->tx + i; + + eea_free_rx(rx, &ctx->cfg); + eea_free_tx(tx, &ctx->cfg); + } + + kvfree(ctx->rx); + +err_free_tx: + kvfree(ctx->tx); + return -ENOMEM; +} + +static int eea_hw_active_ring(struct eea_net *enet) +{ + return eea_adminq_create_q(enet, enet->cfg.rx_ring_num + + enet->cfg.tx_ring_num, 0); +} + +static int eea_hw_unactive_ring(struct eea_net *enet) +{ + int err; + + err = eea_adminq_destroy_all_q(enet); + if (err) + netdev_warn(enet->netdev, "unactive rxtx ring failed.\n"); + + return err; +} + +/* stop rx napi, stop tx queue. */ +static void eea_stop_rxtx(struct net_device *netdev) +{ + struct eea_net *enet = netdev_priv(netdev); + int i; + + netif_tx_disable(netdev); + + for (i = 0; i < enet->cfg.rx_ring_num; i++) + enet_rx_stop(enet->rx[i]); + + netif_carrier_off(netdev); +} + +static void eea_start_rxtx(struct eea_net *enet) +{ + int i; + + for (i = 0; i < enet->cfg.rx_ring_num; i++) + enet_rx_start(enet->rx[i]); + + netif_tx_start_all_queues(enet->netdev); + netif_carrier_on(enet->netdev); + + enet->started = true; +} + +static int eea_netdev_stop(struct net_device *netdev) +{ + struct eea_net *enet = netdev_priv(netdev); + struct eea_net_init_ctx ctx; + + /* This function can be called during device anomaly recovery. To + * prevent duplicate stop operations, the `started` flag is introduced + * for checking. + */ + + if (!enet->started) { + netdev_warn(netdev, "eea netdev stop: but dev is not started.\n"); + return 0; + } + + eea_init_ctx(enet, &ctx); + + eea_stop_rxtx(netdev); + eea_hw_unactive_ring(enet); + eea_unbind_q_and_cfg(enet, &ctx); + eea_free_rxtx_q_mem(&ctx); + + enet->started = false; + + return 0; +} + +static int eea_netdev_open(struct net_device *netdev) +{ + struct eea_net *enet = netdev_priv(netdev); + struct eea_net_init_ctx ctx; + int err; + + if (enet->link_err) { + netdev_err(netdev, "netdev open err, because link error: %d\n", + enet->link_err); + return -EBUSY; + } + + eea_init_ctx(enet, &ctx); + + err = eea_alloc_rxtx_q_mem(&ctx); + if (err) + goto err_done; + + eea_bind_q_and_cfg(enet, &ctx); + + err = eea_update_queues(enet); + if (err) + goto err_free_q; + + err = eea_hw_active_ring(enet); + if (err) + goto err_free_q; + + eea_start_rxtx(enet); + + return 0; + +err_free_q: + eea_unbind_q_and_cfg(enet, &ctx); + eea_free_rxtx_q_mem(&ctx); + +err_done: + return err; +} + +/* Statistics may be reset to zero upon device reset. This is expected behavior + * for now and will be addressed in the future. + */ +static void eea_stats(struct net_device *netdev, struct rtnl_link_stats64 *tot) +{ + struct eea_net *enet = netdev_priv(netdev); + u64 packets, bytes, drop, lerr; + u32 start; + int i; + + spin_lock(&enet->stats_lock); + + if (enet->rx) { + for (i = 0; i < enet->cfg.rx_ring_num; i++) { + struct eea_net_rx *rx = enet->rx[i]; + + do { + start = u64_stats_fetch_begin(&rx->stats.syncp); + packets = u64_stats_read(&rx->stats.packets); + bytes = u64_stats_read(&rx->stats.bytes); + drop = u64_stats_read(&rx->stats.drops); + lerr = u64_stats_read(&rx->stats.length_errors); + } while (u64_stats_fetch_retry(&rx->stats.syncp, + start)); + + tot->rx_packets += packets; + tot->rx_bytes += bytes; + tot->rx_dropped += drop; + tot->rx_length_errors += lerr; + tot->rx_errors += lerr; + } + } + + if (enet->tx) { + for (i = 0; i < enet->cfg.tx_ring_num; i++) { + struct eea_net_tx *tx = &enet->tx[i]; + + do { + start = u64_stats_fetch_begin(&tx->stats.syncp); + packets = u64_stats_read(&tx->stats.packets); + bytes = u64_stats_read(&tx->stats.bytes); + drop = u64_stats_read(&tx->stats.drops); + } while (u64_stats_fetch_retry(&tx->stats.syncp, + start)); + + tot->tx_packets += packets; + tot->tx_bytes += bytes; + tot->tx_dropped += drop; + } + } + + spin_unlock(&enet->stats_lock); +} + +/* resources: ring, buffers, irq */ +int eea_reset_hw_resources(struct eea_net *enet, struct eea_net_init_ctx *ctx) +{ + struct eea_net_init_ctx ctx_old = {0}; + int err, error; + + if (!netif_running(enet->netdev) || !enet->started) { + spin_lock(&enet->stats_lock); + enet->cfg = ctx->cfg; + spin_unlock(&enet->stats_lock); + return 0; + } + + err = eea_alloc_rxtx_q_mem(ctx); + if (err) { + netdev_warn(enet->netdev, + "eea reset: alloc q failed. stop reset. err %d\n", + err); + return err; + } + + eea_stop_rxtx(enet->netdev); + eea_hw_unactive_ring(enet); + + eea_unbind_q_and_cfg(enet, &ctx_old); + eea_bind_q_and_cfg(enet, ctx); + + err = eea_update_queues(enet); + if (err) { + netdev_err(enet->netdev, + "eea reset: set real num queues failed. err %d\n", + err); + goto err_bind_old; + } + + err = eea_hw_active_ring(enet); + if (err) { + netdev_err(enet->netdev, "eea reset: active new ring. err %d\n", + err); + eea_unbind_q_and_cfg(enet, ctx); + goto err_free_q; + } + + eea_start_rxtx(enet); + eea_free_rxtx_q_mem(&ctx_old); + return 0; + +err_bind_old: + eea_unbind_q_and_cfg(enet, ctx); + eea_bind_q_and_cfg(enet, &ctx_old); + error = eea_hw_active_ring(enet); + if (error) { + netdev_err(enet->netdev, "eea reset: active old ring. err %d\n", + error); + eea_unbind_q_and_cfg(enet, &ctx_old); + err = error; + goto err_free_q; + } + + eea_start_rxtx(enet); + eea_free_rxtx_q_mem(ctx); + return err; + +err_free_q: + + /* An exception occurred at the hardware level, and there's not much we + * can do about it -- we can only release the resources first. + */ + eea_free_rxtx_q_mem(ctx); + eea_free_rxtx_q_mem(&ctx_old); + enet->started = false; + return err; +} + +int eea_queues_check_and_reset(struct eea_device *edev) +{ + struct eea_aq_dev_status dstatus = {0}; + struct eea_aq_queue_status *qstatus; + struct eea_aq_queue_status *qs; + struct eea_net_init_ctx ctx; + bool need_reset = false; + int i, err = 0; + + rtnl_lock(); + + if (!netif_running(edev->enet->netdev)) + goto err_unlock; + + /* Maybe stopped by ha. */ + if (!edev->enet->started || edev->enet->link_err) + goto err_unlock; + + err = eea_adminq_dev_status(edev->enet, &dstatus); + if (err) { + netdev_warn(edev->enet->netdev, "query queue status failed.\n"); + goto err_unlock; + } + + if (le16_to_cpu(dstatus.status->link_status) == EEA_LINK_DOWN_STATUS) { + /* The device is broken, can not be up. */ + eea_netdev_stop(edev->enet->netdev); + edev->enet->link_err = EEA_LINK_ERR_LINK_DOWN; + netdev_warn(edev->enet->netdev, "device link is down. stop device.\n"); + goto err_free; + } + + qstatus = dstatus.status->q_status; + + for (i = 0; i < dstatus.num; ++i) { + qs = &qstatus[i]; + + if (le16_to_cpu(qs->status) == EEA_QUEUE_STATUS_NEED_RESET) { + netdev_warn(edev->enet->netdev, + "queue status: queue %u needs to reset\n", + le16_to_cpu(qs->qidx)); + need_reset = true; + } + } + + if (need_reset) { + eea_init_ctx(edev->enet, &ctx); + err = eea_reset_hw_resources(edev->enet, &ctx); + } + +err_free: + kfree(dstatus.status); + +err_unlock: + rtnl_unlock(); + return err; +} + +static int eea_update_cfg(struct eea_net *enet, + struct eea_device *edev, + struct eea_aq_cfg *hwcfg) +{ + u32 rx_max = le32_to_cpu(hwcfg->rx_depth_max); + u32 tx_max = le32_to_cpu(hwcfg->tx_depth_max); + u32 rx_def = le32_to_cpu(hwcfg->rx_depth_def); + u32 tx_def = le32_to_cpu(hwcfg->tx_depth_def); + + /* Now, we assert that the rx ring num is equal to the tx ring num. */ + if (edev->rx_num != edev->tx_num) { + dev_err(edev->dma_dev, "Inconsistent ring num: RX %u, TX %u\n", + edev->rx_num, edev->tx_num); + return -EINVAL; + } + + if (rx_max > EEA_NET_IO_HW_RING_DEPTH_MAX || + rx_max < EEA_NET_IO_HW_RING_DEPTH_MIN || + tx_max > EEA_NET_IO_HW_RING_DEPTH_MAX || + tx_max < EEA_NET_IO_HW_RING_DEPTH_MIN) { + dev_err(edev->dma_dev, "Invalid HW max depth: RX %u, TX %u\n", + rx_max, tx_max); + return -EINVAL; + } + + if (rx_def > rx_max || + tx_def > tx_max || + rx_def < EEA_NET_IO_HW_RING_DEPTH_MIN || + tx_def < EEA_NET_IO_HW_RING_DEPTH_MIN) { + dev_err(edev->dma_dev, "Invalid default depth: RX %u (max %u), TX %u (max %u)\n", + rx_def, rx_max, tx_def, tx_max); + return -EINVAL; + } + + if (!is_power_of_2(rx_max) || !is_power_of_2(tx_max) || + !is_power_of_2(rx_def) || !is_power_of_2(tx_def)) { + dev_err(edev->dma_dev, "Ring depth must be power of 2\n"); + return -EINVAL; + } + + enet->cfg_hw.rx_ring_depth = rx_max; + enet->cfg_hw.tx_ring_depth = tx_max; + enet->cfg_hw.rx_ring_num = edev->rx_num; + enet->cfg_hw.tx_ring_num = edev->tx_num; + enet->cfg_hw.split_hdr = EEA_SPLIT_HDR_SIZE; + + enet->cfg.rx_ring_depth = rx_def; + enet->cfg.tx_ring_depth = tx_def; + enet->cfg.rx_ring_num = edev->rx_num; + enet->cfg.tx_ring_num = edev->tx_num; + + return 0; +} + +static int eea_netdev_init_features(struct net_device *netdev, + struct eea_net *enet, + struct eea_device *edev) +{ + struct eea_aq_cfg *cfg; + int err; + u32 mtu; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + err = eea_adminq_query_cfg(enet, cfg); + if (err) + goto err_free; + + mtu = le16_to_cpu(cfg->mtu); + if (mtu < ETH_MIN_MTU) { + dev_err(edev->dma_dev, "The device gave us an invalid MTU. Here we can only exit the initialization. %u < %u\n", + mtu, ETH_MIN_MTU); + err = -EINVAL; + goto err_free; + } + + err = eea_update_cfg(enet, edev, cfg); + if (err) + goto err_free; + + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + + netdev->hw_features |= NETIF_F_HW_CSUM; + netdev->hw_features |= NETIF_F_GRO_HW; + netdev->hw_features |= NETIF_F_SG; + netdev->hw_features |= NETIF_F_TSO; + netdev->hw_features |= NETIF_F_TSO_ECN; + netdev->hw_features |= NETIF_F_TSO6; + netdev->hw_features |= NETIF_F_GSO_UDP_L4; + + netdev->features |= NETIF_F_HIGHDMA; + netdev->features |= NETIF_F_HW_CSUM; + netdev->features |= NETIF_F_SG; + netdev->features |= NETIF_F_GSO_ROBUST; + netdev->features |= netdev->hw_features & NETIF_F_ALL_TSO; + netdev->features |= NETIF_F_RXCSUM; + netdev->features |= NETIF_F_GRO_HW; + + netdev->vlan_features = netdev->features; + + if (!is_valid_ether_addr(cfg->mac)) { + dev_err(edev->dma_dev, "The device gave invalid mac %pM\n", + cfg->mac); + err = -EINVAL; + goto err_free; + } + + eth_hw_addr_set(netdev, cfg->mac); + + enet->speed = SPEED_UNKNOWN; + enet->duplex = DUPLEX_UNKNOWN; + + netdev->min_mtu = ETH_MIN_MTU; + + netdev->mtu = mtu; + + /* If jumbo frames are already enabled, then the returned MTU will be a + * jumbo MTU, and the driver will automatically enable jumbo frame + * support by default. + */ + netdev->max_mtu = mtu; + +err_free: + kfree(cfg); + return err; +} + +static const struct net_device_ops eea_netdev = { + .ndo_open = eea_netdev_open, + .ndo_stop = eea_netdev_stop, + .ndo_start_xmit = eea_tx_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_get_stats64 = eea_stats, + .ndo_features_check = passthru_features_check, +}; + +static struct eea_net *eea_netdev_alloc(struct eea_device *edev, u32 pairs) +{ + struct net_device *netdev; + struct eea_net *enet; + int err; + + netdev = alloc_etherdev_mq(sizeof(struct eea_net), pairs); + if (!netdev) { + dev_err(edev->dma_dev, + "alloc_etherdev_mq failed with pairs %d\n", pairs); + return NULL; + } + + netdev->netdev_ops = &eea_netdev; + netdev->ethtool_ops = &eea_ethtool_ops; + SET_NETDEV_DEV(netdev, edev->dma_dev); + + enet = netdev_priv(netdev); + enet->netdev = netdev; + enet->edev = edev; + edev->enet = enet; + + err = eea_alloc_irq_blks(enet); + if (err) { + dev_err(edev->dma_dev, + "eea_alloc_irq_blks failed with pairs %d\n", pairs); + free_netdev(netdev); + return NULL; + } + + spin_lock_init(&enet->stats_lock); + + return enet; +} + +static void eea_update_ts_off(struct eea_device *edev, struct eea_net *enet) +{ + u64 ts; + + ts = eea_pci_device_ts(edev); + + enet->hw_ts_offset = ktime_get_real() - ts; +} + +static int eea_net_reprobe(struct eea_device *edev) +{ + struct eea_net *enet = edev->enet; + int err = 0; + + enet->edev = edev; + + if (!enet->adminq.ring) { + err = eea_create_adminq(enet, edev->rx_num + edev->tx_num); + if (err) + return err; + } + + err = eea_alloc_irq_blks(enet); + if (err) + goto err_destroy_aq; + + eea_update_ts_off(edev, enet); + + rtnl_lock(); + + enet->link_err = 0; + if (edev->ha_reset_netdev_running && + netif_running(edev->enet->netdev)) { + err = eea_netdev_open(enet->netdev); + if (err) { + enet->link_err = EEA_LINK_ERR_HA_RESET_DEV; + rtnl_unlock(); + goto err_free_irq_blks; + } + } + + rtnl_unlock(); + + enet->wait_pci_ready = false; + return 0; + +err_free_irq_blks: + eea_free_irq_blk(enet); + +err_destroy_aq: + eea_destroy_adminq(enet); + + return err; +} + +int eea_net_probe(struct eea_device *edev) +{ + struct eea_net *enet; + int err = -ENOMEM; + + /* If edev->enet is not null, then this is called from ha reset worker. + * Call eea_net_reprobe() directly. + */ + if (edev->enet) + return eea_net_reprobe(edev); + + enet = eea_netdev_alloc(edev, edev->rx_num); + if (!enet) + return -ENOMEM; + + err = eea_create_adminq(enet, edev->rx_num + edev->tx_num); + if (err) + goto err_free_netdev; + + eea_adminq_config_host_info(enet); + + err = eea_netdev_init_features(enet->netdev, enet, edev); + if (err) + goto err_reset_dev; + + eea_update_ts_off(edev, enet); + + netif_carrier_off(enet->netdev); + + err = register_netdev(enet->netdev); + if (err) + goto err_reset_dev; + + netdev_dbg(enet->netdev, "eea probe success.\n"); + + return 0; + +err_reset_dev: + eea_device_reset(edev); + eea_destroy_adminq(enet); + +err_free_netdev: + eea_free_irq_blk(enet); + free_netdev(enet->netdev); + return err; +} + +static void eea_net_ha_reset_remove(struct eea_net *enet, + struct eea_device *edev) +{ + rtnl_lock(); + edev->ha_reset_netdev_running = false; + if (netif_running(enet->netdev)) { + eea_netdev_stop(enet->netdev); + edev->ha_reset_netdev_running = true; + } + + /* Prevent that the user set up the net device. */ + enet->link_err = EEA_LINK_ERR_HA_RESET_DEV; + + rtnl_unlock(); + + eea_device_reset(edev); + eea_destroy_adminq(enet); + eea_free_irq_blk(enet); + + enet->wait_pci_ready = true; +} + +void eea_net_remove(struct eea_device *edev, bool ha) +{ + struct net_device *netdev; + struct eea_net *enet; + + enet = edev->enet; + netdev = enet->netdev; + + if (ha) { + if (enet->wait_pci_ready) + return; + + eea_net_ha_reset_remove(enet, edev); + return; + } + + unregister_netdev(netdev); + + if (!enet->wait_pci_ready) { + eea_device_reset(edev); + eea_destroy_adminq(enet); + eea_free_irq_blk(enet); + } + + free_netdev(netdev); +} + +void eea_net_shutdown(struct eea_device *edev) +{ + struct net_device *netdev; + struct eea_net *enet; + + enet = edev->enet; + netdev = enet->netdev; + + rtnl_lock(); + + netif_device_detach(netdev); + dev_close(netdev); + + if (!enet->wait_pci_ready) { + eea_device_reset(edev); + eea_destroy_adminq(enet); + eea_free_irq_blk(enet); + } + + rtnl_unlock(); +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_net.h b/drivers/net/ethernet/alibaba/eea/eea_net.h new file mode 100644 index 000000000000..848bb90bccf8 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_net.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_NET_H__ +#define __EEA_NET_H__ + +#include +#include + +#include "eea_adminq.h" +#include "eea_ethtool.h" +#include "eea_ring.h" + +#define EEA_VER_MAJOR 1 +#define EEA_VER_MINOR 0 +#define EEA_VER_SUB_MINOR 0 + +struct eea_tx_meta; + +struct eea_net_tx { + struct eea_net *enet; + + struct eea_ring *ering; + + struct eea_tx_meta *meta; + struct eea_tx_meta *free; + + struct device *dma_dev; + + u32 index; + + char name[16]; + + struct eea_tx_stats stats; +}; + +struct eea_rx_meta { + struct eea_rx_meta *next; + + struct page *page; + dma_addr_t dma; + u32 offset; + u32 sync_for_cpu; + u32 frags; + + struct page *hdr_page; + void *hdr_addr; + dma_addr_t hdr_dma; + + u32 id; + + u32 truesize; + u32 headroom; + u32 tailroom; + + u32 len; + + bool in_use; +}; + +struct eea_net_rx_pkt_ctx { + u16 idx; + + bool data_valid; + bool do_drop; + + u32 recv_len; + struct sk_buff *head_skb; +}; + +struct eea_net_rx { + struct eea_net *enet; + + struct eea_ring *ering; + + struct eea_rx_meta *meta; + struct eea_rx_meta *free; + + struct device *dma_dev; + + u32 index; + + u32 flags; + + u32 headroom; + + struct napi_struct *napi; + + struct eea_rx_stats stats; + + char name[16]; + + struct eea_net_rx_pkt_ctx pkt; + + struct page_pool *pp; +}; + +struct eea_net_cfg { + u32 rx_ring_depth; + u32 tx_ring_depth; + u32 rx_ring_num; + u32 tx_ring_num; + + u8 rx_sq_desc_size; + u8 rx_cq_desc_size; + u8 tx_sq_desc_size; + u8 tx_cq_desc_size; + + u32 split_hdr; + + struct hwtstamp_config ts_cfg; +}; + +struct eea_net_init_ctx { + struct eea_net_cfg cfg; + + struct eea_net_tx *tx; + struct eea_net_rx **rx; + + struct net_device *netdev; + struct eea_device *edev; +}; + +enum { + EEA_LINK_ERR_NONE, + EEA_LINK_ERR_HA_RESET_DEV, + EEA_LINK_ERR_LINK_DOWN, +}; + +struct eea_irq_blk { + struct napi_struct napi; + u16 msix_vec; + bool ready; + struct eea_net_rx *rx; + char irq_name[32]; + int irq; + int idx; + +}; + +struct eea_net { + struct eea_device *edev; + struct net_device *netdev; + + struct eea_aq adminq; + + struct eea_net_tx *tx; + struct eea_net_rx **rx; + + struct eea_net_cfg cfg; + struct eea_net_cfg cfg_hw; + + struct eea_irq_blk *irq_blks; + + u32 link_err; + + bool started; + bool wait_pci_ready; + + u8 duplex; + u32 speed; + + u64 hw_ts_offset; + + /* Protect the tx and rx of struct eea_net, when eea_stats accesses the + * stats from rx and tx queues. + */ + spinlock_t stats_lock; +}; + +int eea_net_probe(struct eea_device *edev); +void eea_net_remove(struct eea_device *edev, bool ha); +void eea_net_shutdown(struct eea_device *edev); + +int eea_reset_hw_resources(struct eea_net *enet, struct eea_net_init_ctx *ctx); +void eea_init_ctx(struct eea_net *enet, struct eea_net_init_ctx *ctx); +int eea_queues_check_and_reset(struct eea_device *edev); + +/* rx apis */ + +void enet_rx_stop(struct eea_net_rx *rx); +void enet_rx_start(struct eea_net_rx *rx); + +void eea_free_rx(struct eea_net_rx *rx, struct eea_net_cfg *cfg); +struct eea_net_rx *eea_alloc_rx(struct eea_net_init_ctx *ctx, u32 idx); + +/* tx apis */ +int eea_poll_tx(struct eea_net_tx *tx, int budget); +netdev_tx_t eea_tx_xmit(struct sk_buff *skb, struct net_device *netdev); + +void eea_free_tx(struct eea_net_tx *tx, struct eea_net_cfg *cfg); +int eea_alloc_tx(struct eea_net_init_ctx *ctx, struct eea_net_tx *tx, u32 idx); + +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_pci.c b/drivers/net/ethernet/alibaba/eea/eea_pci.c new file mode 100644 index 000000000000..9872e360dd5d --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_pci.c @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include + +#include "eea_net.h" +#include "eea_pci.h" + +#define EEA_PCI_DB_OFFSET 4096 +#define EEA_PCI_DB_MIN_SIZE 8 +#define EEA_PCI_DB_MAX_SIZE 512 +#define EEA_PCI_Q_MAX_NUM 1000 + +#define EEA_PCI_CAP_RESET_DEVICE 0xFA +#define EEA_PCI_CAP_RESET_FLAG BIT(1) + +struct eea_pci_cfg { + __le32 reserve0; + __le32 reserve1; + __le32 drv_f_idx; + __le32 drv_f; + +#define EEA_S_INIT (BIT(0) | BIT(1)) +#define EEA_S_OK BIT(2) +#define EEA_S_FEATURE_DONE BIT(3) +#define EEA_S_FAILED BIT(7) + u8 device_status; + u8 reserved[7]; + + __le32 rx_num_max; + __le32 tx_num_max; + __le32 db_blk_size; + + /* admin queue cfg */ + __le16 aq_size; + __le16 aq_msix_vector; + __le32 aq_db_off; + + __le32 aq_sq_addr; + __le32 aq_sq_addr_hi; + __le32 aq_cq_addr; + __le32 aq_cq_addr_hi; + + __le32 reserved1; + __le64 hw_ts; +}; + +struct eea_pci_device { + struct eea_device edev; + struct pci_dev *pci_dev; + + u32 msix_vec_n; + u32 db_len; + + void __iomem *reg; + void __iomem *db_base; + void __iomem *db_end; + + int ha_irq; + + struct work_struct ha_handle_work; + char ha_irq_name[32]; + int reset_pos; + bool ha_ready; + + bool shutdown; +}; + +#define cfg_pointer(reg, item) \ + ((void __iomem *)((reg) + offsetof(struct eea_pci_cfg, item))) + +#define cfg_write8(reg, item, val) iowrite8(val, cfg_pointer(reg, item)) +#define cfg_write16(reg, item, val) iowrite16(val, cfg_pointer(reg, item)) +#define cfg_write32(reg, item, val) iowrite32(val, cfg_pointer(reg, item)) +#define cfg_write64(reg, item, val) iowrite64_lo_hi(val, cfg_pointer(reg, item)) + +#define cfg_read8(reg, item) ioread8(cfg_pointer(reg, item)) +#define cfg_read32(reg, item) ioread32(cfg_pointer(reg, item)) +#define cfg_read64(reg, item) ioread64(cfg_pointer(reg, item)) + +/* Due to circular references, we have to add function definitions here. */ +static int __eea_pci_probe(struct pci_dev *pci_dev, + struct eea_pci_device *ep_dev, bool pci_probe); +static void __eea_pci_remove(struct pci_dev *pci_dev, bool pci_remove); + +const char *eea_pci_name(struct eea_device *edev) +{ + return pci_name(edev->ep_dev->pci_dev); +} + +int eea_pci_domain_nr(struct eea_device *edev) +{ + return pci_domain_nr(edev->ep_dev->pci_dev->bus); +} + +u16 eea_pci_bdf(struct eea_device *edev) +{ + return pci_dev_id(edev->ep_dev->pci_dev); +} + +static void eea_pci_io_set_status(struct eea_device *edev, u8 status) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + + cfg_write8(ep_dev->reg, device_status, status); +} + +static u8 eea_pci_io_get_status(struct eea_device *edev) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + + return cfg_read8(ep_dev->reg, device_status); +} + +static void eea_add_status(struct eea_device *dev, u32 status) +{ + eea_pci_io_set_status(dev, eea_pci_io_get_status(dev) | status); +} + +#define EEA_RESET_TIMEOUT_US (60 * 1000 * 1000) + +int eea_device_reset(struct eea_device *edev) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + int err; + u8 val; + + eea_pci_io_set_status(edev, 0); + + /* We are no longer waiting for device ack during the shutdown flow. */ + if (ep_dev->shutdown) + return 0; + + /* A longer timeout is set here to handle edge cases, though it should + * return promptly in most scenarios. + * + * In our case, all replies are handled by the DPU software, so there is + * no race condition between the hardware processes and the register. + */ + err = read_poll_timeout(cfg_read8, val, (!val || val == 0xFF), 20, + EEA_RESET_TIMEOUT_US, + false, ep_dev->reg, device_status); + + /* Surprise PCIe Removal */ + if (val == 0xFF) + return -EINVAL; + + return err; +} + +int eea_pci_set_aq_up(struct eea_device *edev) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + u8 status = eea_pci_io_get_status(edev); + int err; + u8 val; + + eea_pci_io_set_status(edev, status | EEA_S_OK); + + /* A longer timeout is set here to handle edge cases, though it should + * return promptly in most scenarios. + * + * In our case, all replies are handled by the DPU software, so there is + * no race condition between the hardware processes and the register. + */ + err = read_poll_timeout(cfg_read8, val, + val & (EEA_S_OK | EEA_S_FAILED), + 20, EEA_RESET_TIMEOUT_US, + false, ep_dev->reg, device_status); + + /* Surprise PCIe Removal */ + if (val == 0xFF) + return -EINVAL; + + /* device fail */ + if (val & EEA_S_FAILED) + return -EINVAL; + + return err; +} + +static int eea_negotiate(struct eea_device *edev) +{ + struct eea_pci_device *ep_dev; + u32 status; + + ep_dev = edev->ep_dev; + + edev->features = 0; + + cfg_write32(ep_dev->reg, drv_f_idx, 0); + cfg_write32(ep_dev->reg, drv_f, lower_32_bits(edev->features)); + cfg_write32(ep_dev->reg, drv_f_idx, 1); + cfg_write32(ep_dev->reg, drv_f, upper_32_bits(edev->features)); + + eea_add_status(edev, EEA_S_FEATURE_DONE); + status = eea_pci_io_get_status(edev); + + /* Surprise PCIe Removal */ + if (status == 0xFF) + return -EINVAL; + + if (!(status & EEA_S_FEATURE_DONE)) + return -ENODEV; + + return 0; +} + +static void eea_pci_release_resource(struct eea_pci_device *ep_dev) +{ + struct pci_dev *pci_dev = ep_dev->pci_dev; + struct eea_device *edev; + + edev = &ep_dev->edev; + + if (edev->status < EEA_PCI_STATUS_READY) + return; + + if (ep_dev->reg) { + pci_iounmap(pci_dev, ep_dev->reg); + ep_dev->reg = NULL; + } + + if (ep_dev->msix_vec_n) { + ep_dev->msix_vec_n = 0; + pci_free_irq_vectors(ep_dev->pci_dev); + } + + pci_clear_master(pci_dev); + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); + + edev->status = EEA_PCI_STATUS_NONE; +} + +static int eea_pci_setup(struct pci_dev *pci_dev, struct eea_pci_device *ep_dev) +{ + int err, n, ret, len; + + ep_dev->edev.status = EEA_PCI_STATUS_ERR; + + ep_dev->pci_dev = pci_dev; + + err = pci_enable_device(pci_dev); + if (err) + return err; + + err = pci_request_regions(pci_dev, "EEA"); + if (err) + goto err_disable_dev; + + if (pci_resource_len(pci_dev, 0) < EEA_PCI_DB_OFFSET) { + dev_err(&pci_dev->dev, "Bar 0 is too small %llu\n", + (u64)pci_resource_len(pci_dev, 0)); + err = -EINVAL; + goto err_release_regions; + } + + ep_dev->reg = pci_iomap(pci_dev, 0, 0); + if (!ep_dev->reg) { + dev_err(&pci_dev->dev, "Failed to map pci bar!\n"); + err = -ENOMEM; + goto err_release_regions; + } + + err = eea_device_reset(&ep_dev->edev); + if (err) { + dev_err(&pci_dev->dev, "Failed to reset device for setup!\n"); + goto err_unmap_reg; + } + + err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64)); + if (err) { + dev_warn(&pci_dev->dev, "Failed to enable 64-bit DMA.\n"); + goto err_unmap_reg; + } + + pci_set_master(pci_dev); + + ep_dev->edev.rx_num = cfg_read32(ep_dev->reg, rx_num_max); + ep_dev->edev.tx_num = cfg_read32(ep_dev->reg, tx_num_max); + + if (ep_dev->edev.rx_num > EEA_PCI_Q_MAX_NUM || + ep_dev->edev.tx_num > EEA_PCI_Q_MAX_NUM) { + dev_err(&pci_dev->dev, "Invalid queue num %u %u\n", + ep_dev->edev.rx_num, + ep_dev->edev.tx_num); + err = -EINVAL; + goto err_clear_master; + } + + ep_dev->edev.db_blk_size = cfg_read32(ep_dev->reg, db_blk_size); + if (!IS_ALIGNED(ep_dev->edev.db_blk_size, 8) || + ep_dev->edev.db_blk_size > EEA_PCI_DB_MAX_SIZE || + ep_dev->edev.db_blk_size < EEA_PCI_DB_MIN_SIZE) { + dev_err(&pci_dev->dev, "Invalid db size %u\n", + ep_dev->edev.db_blk_size); + err = -EINVAL; + goto err_clear_master; + } + + ep_dev->db_len = ep_dev->edev.db_blk_size * (ep_dev->edev.rx_num + + ep_dev->edev.tx_num + 1); + ep_dev->db_base = ep_dev->reg + EEA_PCI_DB_OFFSET; + ep_dev->db_end = ep_dev->db_base + ep_dev->db_len; + + len = ep_dev->db_end - ep_dev->reg; + + if (pci_resource_len(pci_dev, 0) < len) { + dev_err(&pci_dev->dev, "Bar 0 is too small %llu\n", + (u64)pci_resource_len(pci_dev, 0)); + err = -EINVAL; + goto err_clear_master; + } + + /* In our design, the number of hardware interrupts matches the maximum + * number of queues. If pci_alloc_irq_vectors failed, return directly. + * + * 2: adminq, error handle + */ + n = ep_dev->edev.rx_num + 2; + ret = pci_alloc_irq_vectors(ep_dev->pci_dev, n, n, PCI_IRQ_MSIX); + if (ret != n) { + err = ret; + goto err_clear_master; + } + + ep_dev->msix_vec_n = ret; + + ep_dev->edev.status = EEA_PCI_STATUS_READY; + + return 0; + +err_clear_master: + pci_clear_master(pci_dev); + +err_unmap_reg: + pci_iounmap(pci_dev, ep_dev->reg); + ep_dev->reg = NULL; + +err_release_regions: + pci_release_regions(pci_dev); + +err_disable_dev: + pci_disable_device(pci_dev); + + return err; +} + +void __iomem *eea_pci_db_addr(struct eea_device *edev, u32 off) +{ + u32 max_off; + + if (!IS_ALIGNED(off, 8)) + return NULL; + + max_off = edev->ep_dev->db_len - edev->db_blk_size; + + if (off > max_off) + return NULL; + + return edev->ep_dev->db_base + off; +} + +int eea_pci_active_aq(struct eea_ring *ering, int msix_vec) +{ + struct eea_pci_device *ep_dev = ering->edev->ep_dev; + + cfg_write16(ep_dev->reg, aq_size, ering->num); + cfg_write16(ep_dev->reg, aq_msix_vector, msix_vec); + + cfg_write64(ep_dev->reg, aq_sq_addr, ering->sq.dma_addr); + cfg_write64(ep_dev->reg, aq_cq_addr, ering->cq.dma_addr); + + ering->db = eea_pci_db_addr(ering->edev, + cfg_read32(ep_dev->reg, aq_db_off)); + + if (!ering->db) + return -EIO; + + return 0; +} + +void eea_pci_free_irq(struct eea_irq_blk *blk) +{ + irq_update_affinity_hint(blk->irq, NULL); + free_irq(blk->irq, blk); +} + +int eea_pci_request_irq(struct eea_device *edev, struct eea_irq_blk *blk, + irqreturn_t (*callback)(int irq, void *data)) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + int irq; + + snprintf(blk->irq_name, sizeof(blk->irq_name), "eea-q%d@%s", blk->idx, + pci_name(ep_dev->pci_dev)); + + irq = pci_irq_vector(ep_dev->pci_dev, blk->msix_vec); + + blk->irq = irq; + + return request_irq(irq, callback, IRQF_NO_AUTOEN, blk->irq_name, blk); +} + +static void eea_ha_handle_reset(struct eea_pci_device *ep_dev) +{ + struct eea_device *edev; + struct pci_dev *pci_dev; + u16 reset; + int err; + + if (!ep_dev->reset_pos) { + eea_queues_check_and_reset(&ep_dev->edev); + return; + } + + edev = &ep_dev->edev; + + pci_read_config_word(ep_dev->pci_dev, ep_dev->reset_pos, &reset); + + /* Clear bits using 0xFFFF and ignore all previous messages. */ + pci_write_config_word(ep_dev->pci_dev, ep_dev->reset_pos, 0xFFFF); + + if (reset & EEA_PCI_CAP_RESET_FLAG) { + dev_warn(&ep_dev->pci_dev->dev, "recv device reset request.\n"); + + pci_dev = ep_dev->pci_dev; + + /* The pci remove callback may hold this lock. If the + * pci remove callback is called, then we can ignore the + * ha interrupt. + */ + if (mutex_trylock(&edev->ha_lock)) { + if (edev->status != EEA_PCI_STATUS_DONE) { + dev_err(&ep_dev->pci_dev->dev, "ha: reset device: pci status is %d. skip it.\n", + edev->status); + + mutex_unlock(&edev->ha_lock); + return; + } + + __eea_pci_remove(pci_dev, false); + err = __eea_pci_probe(pci_dev, ep_dev, false); + if (err) + /* Currently, for some reason, PCI + * initialization or network device re-probing + * has failed. Waiting for the PCI subsystem to + * call the remove callback to release the + * remaining resources. + */ + dev_err(&ep_dev->pci_dev->dev, + "ha: re-setup failed.\n"); + + mutex_unlock(&edev->ha_lock); + } else { + /* Device removal is in progress, so return directly. */ + dev_warn(&ep_dev->pci_dev->dev, + "ha device reset: trylock failed.\n"); + } + return; + } + + eea_queues_check_and_reset(&ep_dev->edev); +} + +/* ha handle code */ +static void eea_ha_handle_work(struct work_struct *work) +{ + struct eea_pci_device *ep_dev; + + ep_dev = container_of(work, struct eea_pci_device, ha_handle_work); + + /* Ha interrupt is triggered, so there maybe some error, we may need to + * reset the device or reset some queues. + */ + dev_warn(&ep_dev->pci_dev->dev, "recv ha interrupt.\n"); + + eea_ha_handle_reset(ep_dev); +} + +static irqreturn_t eea_pci_ha_handle(int irq, void *data) +{ + struct eea_device *edev = data; + + schedule_work(&edev->ep_dev->ha_handle_work); + + return IRQ_HANDLED; +} + +static void eea_pci_free_ha_irq(struct eea_device *edev) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + int irq; + + if (ep_dev->ha_ready) { + irq = pci_irq_vector(ep_dev->pci_dev, 0); + free_irq(irq, edev); + ep_dev->ha_ready = false; + } +} + +static int eea_pci_ha_init(struct eea_device *edev, struct pci_dev *pci_dev, + bool pci_probe) +{ + int pos, cfg_type_off, cfg_drv_off, cfg_dev_off; + struct eea_pci_device *ep_dev = edev->ep_dev; + int irq, err; + u8 type; + + snprintf(ep_dev->ha_irq_name, sizeof(ep_dev->ha_irq_name), "eea-ha@%s", + pci_name(ep_dev->pci_dev)); + + irq = pci_irq_vector(ep_dev->pci_dev, 0); + + if (pci_probe) + INIT_WORK(&ep_dev->ha_handle_work, eea_ha_handle_work); + + /* This irq is not only work for ha, so request it always. */ + err = request_irq(irq, eea_pci_ha_handle, IRQF_NO_AUTOEN, + ep_dev->ha_irq_name, edev); + if (err) + return err; + + ep_dev->ha_irq = irq; + + ep_dev->ha_ready = true; + ep_dev->reset_pos = 0; + + cfg_type_off = offsetof(struct eea_pci_cap, cfg_type); + cfg_drv_off = offsetof(struct eea_pci_reset_reg, driver); + cfg_dev_off = offsetof(struct eea_pci_reset_reg, device); + + for (pos = pci_find_capability(pci_dev, PCI_CAP_ID_VNDR); + pos > 0; + pos = pci_find_next_capability(pci_dev, pos, PCI_CAP_ID_VNDR)) { + pci_read_config_byte(pci_dev, pos + cfg_type_off, &type); + + if (type == EEA_PCI_CAP_RESET_DEVICE) { + /* notify device, driver support this feature. */ + pci_write_config_word(pci_dev, pos + cfg_drv_off, + EEA_PCI_CAP_RESET_FLAG); + pci_write_config_word(pci_dev, pos + cfg_dev_off, + 0xFFFF); + + edev->ep_dev->reset_pos = pos + cfg_dev_off; + return 0; + } + } + + /* irq just for event notify */ + dev_warn(&edev->ep_dev->pci_dev->dev, "Not Found reset cap.\n"); + return 0; +} + +u64 eea_pci_device_ts(struct eea_device *edev) +{ + struct eea_pci_device *ep_dev = edev->ep_dev; + + return cfg_read64(ep_dev->reg, hw_ts); +} + +static int eea_init_device(struct eea_device *edev) +{ + int err; + + err = eea_device_reset(edev); + if (err) + return err; + + eea_pci_io_set_status(edev, EEA_S_INIT); + + err = eea_negotiate(edev); + if (err) + goto err; + + err = eea_net_probe(edev); + if (err) + goto err; + + return 0; +err: + eea_add_status(edev, EEA_S_FAILED); + return err; +} + +static int __eea_pci_probe(struct pci_dev *pci_dev, + struct eea_pci_device *ep_dev, + bool pci_probe) +{ + struct eea_device *edev; + int err; + + pci_set_drvdata(pci_dev, ep_dev); + + edev = &ep_dev->edev; + + err = eea_pci_setup(pci_dev, ep_dev); + if (err) + return err; + + err = eea_init_device(&ep_dev->edev); + if (err) + goto err_pci_rel; + + err = eea_pci_ha_init(edev, pci_dev, pci_probe); + if (err) + goto err_net_rm; + + edev->status = EEA_PCI_STATUS_DONE; + + enable_irq(ep_dev->ha_irq); + + return 0; + +err_net_rm: + eea_net_remove(edev, !pci_probe); + +err_pci_rel: + eea_pci_release_resource(ep_dev); + return err; +} + +static void __eea_pci_remove(struct pci_dev *pci_dev, bool pci_remove) +{ + struct eea_pci_device *ep_dev = pci_get_drvdata(pci_dev); + struct device *dev = get_device(&ep_dev->pci_dev->dev); + struct eea_device *edev = &ep_dev->edev; + + eea_pci_free_ha_irq(edev); + + if (pci_remove) + flush_work(&ep_dev->ha_handle_work); + + eea_net_remove(edev, !pci_remove); + + eea_pci_release_resource(ep_dev); + + put_device(dev); +} + +static int eea_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct eea_pci_device *ep_dev; + struct eea_device *edev; + int err; + + ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); + if (!ep_dev) + return -ENOMEM; + + edev = &ep_dev->edev; + + edev->ep_dev = ep_dev; + edev->dma_dev = &pci_dev->dev; + + ep_dev->pci_dev = pci_dev; + + mutex_init(&edev->ha_lock); + + err = __eea_pci_probe(pci_dev, ep_dev, true); + if (err) { + mutex_destroy(&edev->ha_lock); + pci_set_drvdata(pci_dev, NULL); + kfree(ep_dev); + } + + return err; +} + +static void eea_pci_remove(struct pci_dev *pci_dev) +{ + struct eea_pci_device *ep_dev = pci_get_drvdata(pci_dev); + struct eea_device *edev; + + edev = &ep_dev->edev; + + mutex_lock(&edev->ha_lock); + __eea_pci_remove(pci_dev, true); + mutex_unlock(&edev->ha_lock); + + pci_set_drvdata(pci_dev, NULL); + + mutex_destroy(&edev->ha_lock); + kfree(ep_dev); +} + +static void eea_pci_shutdown(struct pci_dev *pci_dev) +{ + struct eea_pci_device *ep_dev = pci_get_drvdata(pci_dev); + struct eea_device *edev; + + edev = &ep_dev->edev; + + ep_dev->shutdown = true; + + mutex_lock(&edev->ha_lock); + eea_pci_free_ha_irq(edev); + flush_work(&ep_dev->ha_handle_work); + mutex_unlock(&edev->ha_lock); + + eea_net_shutdown(edev); + + pci_clear_master(pci_dev); +} + +static const struct pci_device_id eea_pci_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_ALIBABA, 0x500B) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, eea_pci_id_table); + +static struct pci_driver eea_pci_driver = { + .name = "alibaba_eea", + .id_table = eea_pci_id_table, + .probe = eea_pci_probe, + .remove = eea_pci_remove, + .shutdown = eea_pci_shutdown, + .sriov_configure = pci_sriov_configure_simple, +}; + +static __init int eea_pci_init(void) +{ + return pci_register_driver(&eea_pci_driver); +} + +static __exit void eea_pci_exit(void) +{ + pci_unregister_driver(&eea_pci_driver); +} + +module_init(eea_pci_init); +module_exit(eea_pci_exit); + +MODULE_DESCRIPTION("Driver for Alibaba Elastic Ethernet Adapter"); +MODULE_AUTHOR("Xuan Zhuo "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/alibaba/eea/eea_pci.h b/drivers/net/ethernet/alibaba/eea/eea_pci.h new file mode 100644 index 000000000000..6f6ba7d5514d --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_pci.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_PCI_H__ +#define __EEA_PCI_H__ + +#include + +#include "eea_net.h" +#include "eea_ring.h" + +enum eea_pci_status { + EEA_PCI_STATUS_NONE, + EEA_PCI_STATUS_ERR, + EEA_PCI_STATUS_READY, + EEA_PCI_STATUS_DONE, +}; + +struct eea_irq_blk; + +struct eea_pci_cap { + __u8 cap_vndr; + __u8 cap_next; + __u8 cap_len; + __u8 cfg_type; +}; + +struct eea_pci_reset_reg { + struct eea_pci_cap cap; + __le16 driver; + __le16 device; +}; + +struct eea_pci_device; + +struct eea_device { + struct eea_pci_device *ep_dev; + struct device *dma_dev; + struct eea_net *enet; + + u64 features; + + enum eea_pci_status status; + bool ha_reset_netdev_running; + + /* ha lock for the race between ha work and pci remove */ + struct mutex ha_lock; + + u32 rx_num; + u32 tx_num; + u32 db_blk_size; +}; + +const char *eea_pci_name(struct eea_device *edev); +int eea_pci_domain_nr(struct eea_device *edev); +u16 eea_pci_bdf(struct eea_device *edev); + +int eea_device_reset(struct eea_device *dev); +int eea_pci_set_aq_up(struct eea_device *dev); +int eea_pci_active_aq(struct eea_ring *ering, int msix_vec); + +int eea_pci_request_irq(struct eea_device *edev, struct eea_irq_blk *blk, + irqreturn_t (*callback)(int irq, void *data)); +void eea_pci_free_irq(struct eea_irq_blk *blk); + +u64 eea_pci_device_ts(struct eea_device *edev); + +void __iomem *eea_pci_db_addr(struct eea_device *edev, u32 off); +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_ring.c b/drivers/net/ethernet/alibaba/eea/eea_ring.c new file mode 100644 index 000000000000..99dcabd094b8 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_ring.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include "eea_pci.h" +#include "eea_ring.h" + +void eea_ering_irq_active(struct eea_ring *ering, struct eea_ring *tx_ering) +{ + u64 value = 0, rx_idx, tx_idx; + + tx_idx = (u64)tx_ering->cq.hw_idx; + rx_idx = (u64)ering->cq.hw_idx; + + value |= EEA_IRQ_UNMASK << EEA_DB_FLAGS_OFF; + value |= tx_idx << EEA_DB_TX_CQ_HEAD_OFF; + value |= rx_idx << EEA_DB_RX_CQ_HEAD_OFF; + + writeq(value, ering->db); +} + +void *eea_ering_cq_get_desc(const struct eea_ring *ering) +{ + u8 phase; + u8 *desc; + + desc = ering->cq.desc + (ering->cq.head << ering->cq.desc_size_shift); + + phase = READ_ONCE(*(u8 *)(desc + ering->cq.desc_size - 1)); + + if ((phase & EEA_RING_DESC_F_CQ_PHASE) == ering->cq.phase) { + dma_rmb(); + return desc; + } + + return NULL; +} + +/* sq api */ +void *eea_ering_sq_alloc_desc(struct eea_ring *ering, u16 id, bool is_last, + u16 flags) +{ + struct eea_ring_sq *sq = &ering->sq; + struct eea_common_desc *desc; + + if (!sq->shadow_num) { + sq->shadow_idx = sq->head; + sq->shadow_id = cpu_to_le16(id); + } + + if (!is_last) + flags |= EEA_RING_DESC_F_MORE; + + desc = sq->desc + (sq->shadow_idx << sq->desc_size_shift); + + desc->flags = cpu_to_le16(flags); + desc->id = sq->shadow_id; + + if (unlikely(++sq->shadow_idx >= ering->num)) + sq->shadow_idx = 0; + + ++sq->shadow_num; + + return desc; +} + +/* This is an allocation API for admin Q. For each call to admin Q, only one + * desc will be allocated. + */ +void *eea_ering_aq_alloc_desc(struct eea_ring *ering) +{ + struct eea_ring_sq *sq = &ering->sq; + struct eea_common_desc *desc; + + if (!sq->shadow_num) + sq->shadow_idx = sq->head; + + desc = sq->desc + (sq->shadow_idx << sq->desc_size_shift); + + if (unlikely(++sq->shadow_idx >= ering->num)) + sq->shadow_idx = 0; + + ++sq->shadow_num; + + return desc; +} + +void eea_ering_sq_commit_desc(struct eea_ring *ering) +{ + struct eea_ring_sq *sq = &ering->sq; + int num; + + num = sq->shadow_num; + + ering->num_free -= num; + + sq->head = sq->shadow_idx; + sq->hw_idx += num; + sq->shadow_num = 0; +} + +void eea_ering_sq_cancel(struct eea_ring *ering) +{ + ering->sq.shadow_num = 0; +} + +/* cq api */ +void eea_ering_cq_ack_desc(struct eea_ring *ering, u32 num) +{ + struct eea_ring_cq *cq = &ering->cq; + + cq->head += num; + cq->hw_idx += num; + + if (unlikely(cq->head >= ering->num)) { + cq->head -= ering->num; + cq->phase ^= EEA_RING_DESC_F_CQ_PHASE; + } + + ering->num_free += num; +} + +/* notify */ +void eea_ering_kick(struct eea_ring *ering) +{ + u64 value = 0, idx; + + idx = (u64)ering->sq.hw_idx; + + value |= EEA_IDX_PRESENT << EEA_DB_FLAGS_OFF; + value |= idx << EEA_DB_IDX_OFF; + + writeq(value, ering->db); +} + +/* ering alloc/free */ +static void ering_free_queue(struct eea_device *edev, size_t size, + void *queue, dma_addr_t dma_handle) +{ + dma_free_coherent(edev->dma_dev, size, queue, dma_handle); +} + +static void *ering_alloc_queue(struct eea_device *edev, size_t size, + dma_addr_t *dma_handle) +{ + gfp_t flags = GFP_KERNEL | __GFP_NOWARN; + + return dma_alloc_coherent(edev->dma_dev, size, dma_handle, flags); +} + +static int ering_alloc_queues(struct eea_ring *ering, struct eea_device *edev, + size_t num, u8 sq_desc_size, u8 cq_desc_size) +{ + dma_addr_t addr; + size_t size; + void *ring; + + size = num * sq_desc_size; + + ring = ering_alloc_queue(edev, size, &addr); + if (!ring) + return -ENOMEM; + + ering->sq.desc = ring; + ering->sq.dma_addr = addr; + ering->sq.dma_size = size; + ering->sq.desc_size = sq_desc_size; + ering->sq.desc_size_shift = fls(sq_desc_size) - 1; + + size = num * cq_desc_size; + + ring = ering_alloc_queue(edev, size, &addr); + if (!ring) + goto err_free_sq; + + ering->cq.desc = ring; + ering->cq.dma_addr = addr; + ering->cq.dma_size = size; + ering->cq.desc_size = cq_desc_size; + ering->cq.desc_size_shift = fls(cq_desc_size) - 1; + + ering->num = num; + + return 0; + +err_free_sq: + ering_free_queue(ering->edev, ering->sq.dma_size, + ering->sq.desc, ering->sq.dma_addr); + return -ENOMEM; +} + +static void ering_init(struct eea_ring *ering) +{ + ering->cq.phase = EEA_RING_DESC_F_CQ_PHASE; + ering->num_free = ering->num; +} + +struct eea_ring *eea_ering_alloc(u32 index, u32 num, struct eea_device *edev, + u8 sq_desc_size, u8 cq_desc_size, + const char *name) +{ + struct eea_ring *ering; + + if (num > EEA_NET_IO_HW_RING_DEPTH_MAX || + num < EEA_NET_IO_RING_DEPTH_MIN) + return NULL; + + if (!is_power_of_2(num)) + return NULL; + + if (!sq_desc_size || !is_power_of_2(sq_desc_size)) + return NULL; + + if (!cq_desc_size || !is_power_of_2(cq_desc_size)) + return NULL; + + ering = kzalloc(sizeof(*ering), GFP_KERNEL); + if (!ering) + return NULL; + + ering->edev = edev; + ering->name = name; + ering->index = index; + + if (ering_alloc_queues(ering, edev, num, sq_desc_size, cq_desc_size)) + goto err_free; + + ering_init(ering); + + return ering; + +err_free: + kfree(ering); + return NULL; +} + +void eea_ering_free(struct eea_ring *ering) +{ + ering_free_queue(ering->edev, ering->cq.dma_size, + ering->cq.desc, ering->cq.dma_addr); + + ering_free_queue(ering->edev, ering->sq.dma_size, + ering->sq.desc, ering->sq.dma_addr); + + kfree(ering); +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_ring.h b/drivers/net/ethernet/alibaba/eea/eea_ring.h new file mode 100644 index 000000000000..a7ce465943a5 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_ring.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_RING_H__ +#define __EEA_RING_H__ + +#include +#include "eea_desc.h" + +#define EEA_RING_DESC_F_MORE BIT(0) +#define EEA_RING_DESC_F_CQ_PHASE BIT(7) + +/* These two values define the bounds for the queue depth returned by the + * hardware. + */ +#define EEA_NET_IO_HW_RING_DEPTH_MAX (32 * 1024) +#define EEA_NET_IO_HW_RING_DEPTH_MIN 128 + +/* This value constrains the minimum queue depth that the driver configures for + * the hardware, which typically applies to user-provided settings. Naturally, + * the configured depth must also not exceed the maximum capacity supported by + * the hardware. + */ +#define EEA_NET_IO_RING_DEPTH_MIN 64 + +struct eea_common_desc { + __le16 flags; + __le16 id; +}; + +struct eea_device; + +struct eea_ring_sq { + void *desc; + + u16 head; + u16 hw_idx; + + u16 shadow_idx; + __le16 shadow_id; + u16 shadow_num; + + u8 desc_size; + u8 desc_size_shift; + + dma_addr_t dma_addr; + u32 dma_size; +}; + +struct eea_ring_cq { + void *desc; + + u16 head; + u16 hw_idx; + + u8 phase; + u8 desc_size_shift; + u8 desc_size; + + dma_addr_t dma_addr; + u32 dma_size; +}; + +struct eea_ring { + const char *name; + struct eea_device *edev; + u32 index; + void __iomem *db; + u16 msix_vec; + + u32 num; + + u32 num_free; + + struct eea_ring_sq sq; + struct eea_ring_cq cq; +}; + +struct eea_ring *eea_ering_alloc(u32 index, u32 num, struct eea_device *edev, + u8 sq_desc_size, u8 cq_desc_size, + const char *name); +void eea_ering_free(struct eea_ring *ering); +void eea_ering_kick(struct eea_ring *ering); + +void *eea_ering_sq_alloc_desc(struct eea_ring *ering, u16 id, + bool is_last, u16 flags); +void *eea_ering_aq_alloc_desc(struct eea_ring *ering); +void eea_ering_sq_commit_desc(struct eea_ring *ering); +void eea_ering_sq_cancel(struct eea_ring *ering); + +void eea_ering_cq_ack_desc(struct eea_ring *ering, u32 num); + +void eea_ering_irq_active(struct eea_ring *ering, struct eea_ring *tx_ering); +void *eea_ering_cq_get_desc(const struct eea_ring *ering); +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_rx.c b/drivers/net/ethernet/alibaba/eea/eea_rx.c new file mode 100644 index 000000000000..a3f0d2a79ad8 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_rx.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include +#include + +#include "eea_adminq.h" +#include "eea_net.h" +#include "eea_pci.h" +#include "eea_ring.h" + +#define EEA_ENABLE_F_NAPI BIT(0) + +#define EEA_PAGE_FRAGS_NUM 1024 + +#define EEA_RX_BUF_ALIGN 128 + +#define EEA_RX_BUF_MAX_LEN (10 * 1024) + +struct eea_rx_ctx { + u32 len; + u32 hdr_len; + + u16 flags; + bool more; + + struct eea_rx_meta *meta; + + struct eea_rx_ctx_stats stats; +}; + +static struct eea_rx_meta *eea_rx_meta_get(struct eea_net_rx *rx) +{ + struct eea_rx_meta *meta; + + if (!rx->free) + return NULL; + + meta = rx->free; + rx->free = meta->next; + + return meta; +} + +static void eea_rx_meta_put(struct eea_net_rx *rx, struct eea_rx_meta *meta) +{ + meta->next = rx->free; + rx->free = meta; +} + +static void eea_free_rx_buffer(struct eea_net_rx *rx, struct eea_rx_meta *meta, + bool allow_direct) +{ + u32 drain_count; + + drain_count = EEA_PAGE_FRAGS_NUM - meta->frags; + + if (page_pool_unref_page(meta->page, drain_count) == 0) + page_pool_put_unrefed_page(rx->pp, meta->page, -1, + allow_direct); + + meta->page = NULL; +} + +static void eea_rx_meta_dma_sync_for_device(struct eea_net_rx *rx, + struct eea_rx_meta *meta) +{ + u32 len; + + if (meta->sync_for_cpu <= meta->offset + rx->headroom) + return; + + len = meta->sync_for_cpu - meta->offset - rx->headroom; + + dma_sync_single_for_device(rx->enet->edev->dma_dev, + meta->dma + meta->offset + rx->headroom, + len, DMA_FROM_DEVICE); + meta->sync_for_cpu = 0; +} + +static void meta_align_offset(struct eea_net_rx *rx, struct eea_rx_meta *meta) +{ + int h, b; + + h = rx->headroom; + b = meta->offset + h; + + /* For better performance, we align the buffer address to + * EEA_RX_BUF_ALIGN, as required by the device design. + */ + b = ALIGN(b, EEA_RX_BUF_ALIGN); + + meta->offset = b - h; +} + +static int eea_alloc_rx_buffer(struct eea_net_rx *rx, struct eea_rx_meta *meta) +{ + struct page *page; + + if (meta->page) { + eea_rx_meta_dma_sync_for_device(rx, meta); + return 0; + } + + page = page_pool_dev_alloc_pages(rx->pp); + if (!page) + return -ENOMEM; + + page_pool_fragment_page(page, EEA_PAGE_FRAGS_NUM); + + meta->page = page; + meta->dma = page_pool_get_dma_addr(page); + meta->offset = 0; + meta->frags = 0; + meta->sync_for_cpu = 0; + + meta_align_offset(rx, meta); + + return 0; +} + +static u32 eea_consume_rx_buffer(struct eea_net_rx *rx, + struct eea_rx_meta *meta, + u32 consumed) +{ + u32 offset; + int min; + + offset = meta->offset; + + meta->offset += consumed; + ++meta->frags; + + min = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + min += rx->headroom; + min += SKB_DATA_ALIGN(ETH_DATA_LEN); + + meta_align_offset(rx, meta); + + if (min + meta->offset > PAGE_SIZE) { + eea_free_rx_buffer(rx, meta, true); + return PAGE_SIZE - offset; + } + + return meta->offset - offset; +} + +static void eea_free_rx_hdr(struct eea_net_rx *rx, struct eea_net_cfg *cfg) +{ + struct eea_rx_meta *meta; + int i; + + for (i = 0; i < cfg->rx_ring_depth; ++i) { + meta = &rx->meta[i]; + meta->hdr_addr = NULL; + + if (!meta->hdr_page) + continue; + + dma_unmap_page(rx->dma_dev, meta->hdr_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + put_page(meta->hdr_page); + + meta->hdr_page = NULL; + } +} + +static int eea_alloc_rx_hdr(struct eea_net_init_ctx *ctx, struct eea_net_rx *rx) +{ + struct page *hdr_page = NULL; + struct eea_rx_meta *meta; + u32 offset = 0, hdrsize; + struct device *dmadev; + dma_addr_t dma; + int i; + + dmadev = ctx->edev->dma_dev; + hdrsize = ctx->cfg.split_hdr; + + for (i = 0; i < ctx->cfg.rx_ring_depth; ++i) { + meta = &rx->meta[i]; + meta->hdr_page = NULL; + + if (!hdr_page || offset + hdrsize > PAGE_SIZE) { + hdr_page = alloc_page(GFP_KERNEL); + if (!hdr_page) + goto err; + + dma = dma_map_page(dmadev, hdr_page, 0, PAGE_SIZE, + DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(dmadev, dma))) { + put_page(hdr_page); + goto err; + } + + offset = 0; + meta->hdr_page = hdr_page; + } + + meta->hdr_dma = dma + offset; + meta->hdr_addr = page_address(hdr_page) + offset; + offset += hdrsize; + } + + return 0; + +err: + eea_free_rx_hdr(rx, &ctx->cfg); + return -ENOMEM; +} + +static void eea_rx_meta_dma_sync_for_cpu(struct eea_net_rx *rx, + struct eea_rx_meta *meta, u32 len) +{ + dma_sync_single_for_cpu(rx->enet->edev->dma_dev, + meta->dma + meta->offset + meta->headroom, + len, DMA_FROM_DEVICE); + meta->sync_for_cpu = meta->offset + meta->headroom + len; +} + +static int eea_harden_check_overflow(struct eea_rx_ctx *ctx, + struct eea_net *enet) +{ + u32 max_len; + + max_len = ctx->meta->truesize - ctx->meta->headroom - + ctx->meta->tailroom; + + if (unlikely(ctx->len > max_len)) { + pr_debug("%s: rx error: len %u exceeds truesize %u\n", + enet->netdev->name, ctx->len, max_len); + ++ctx->stats.length_errors; + return -EINVAL; + } + + return 0; +} + +static int eea_harden_check_size(struct eea_rx_ctx *ctx, struct eea_net *enet) +{ + int err; + + err = eea_harden_check_overflow(ctx, enet); + if (err) + return err; + + if (ctx->hdr_len) { + if (unlikely(ctx->hdr_len < ETH_HLEN)) { + pr_debug("%s: short hdr %u\n", enet->netdev->name, + ctx->hdr_len); + ++ctx->stats.length_errors; + return -EINVAL; + } + + if (unlikely(ctx->hdr_len > enet->cfg.split_hdr)) { + pr_debug("%s: rx error: hdr len %u exceeds hdr buffer size %u\n", + enet->netdev->name, ctx->hdr_len, + enet->cfg.split_hdr); + ++ctx->stats.length_errors; + return -EINVAL; + } + + return 0; + } + + if (unlikely(ctx->len < ETH_HLEN)) { + pr_debug("%s: short packet %u\n", enet->netdev->name, ctx->len); + ++ctx->stats.length_errors; + return -EINVAL; + } + + return 0; +} + +static struct sk_buff *eea_build_skb(void *buf, u32 buflen, u32 headroom, + u32 len) +{ + struct sk_buff *skb; + + skb = build_skb(buf, buflen); + if (unlikely(!skb)) + return NULL; + + skb_reserve(skb, headroom); + skb_put(skb, len); + + return skb; +} + +static struct sk_buff *eea_rx_build_split_hdr_skb(struct eea_net_rx *rx, + struct eea_rx_ctx *ctx) +{ + struct eea_rx_meta *meta = ctx->meta; + u32 truesize, offset; + struct sk_buff *skb; + struct page *page; + + dma_sync_single_for_cpu(rx->enet->edev->dma_dev, meta->hdr_dma, + ctx->hdr_len, DMA_FROM_DEVICE); + + skb = napi_alloc_skb(rx->napi, ctx->hdr_len); + if (unlikely(!skb)) + return NULL; + + skb_put_data(skb, ctx->meta->hdr_addr, ctx->hdr_len); + + if (ctx->len) { + page = meta->page; + offset = meta->offset + meta->headroom; + + truesize = eea_consume_rx_buffer(rx, meta, + meta->headroom + ctx->len); + + skb_add_rx_frag(skb, 0, page, offset, ctx->len, truesize); + } + + skb_mark_for_recycle(skb); + + return skb; +} + +static struct sk_buff *eea_rx_build_skb(struct eea_net_rx *rx, + struct eea_rx_ctx *ctx) +{ + struct eea_rx_meta *meta = ctx->meta; + u32 shinfo_size, bufsize, truesize; + struct sk_buff *skb; + struct page *page; + void *buf; + + page = meta->page; + + shinfo_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + buf = page_address(page) + meta->offset; + bufsize = meta->headroom + SKB_DATA_ALIGN(ctx->len) + shinfo_size; + + skb = eea_build_skb(buf, bufsize, meta->headroom, ctx->len); + if (unlikely(!skb)) + return NULL; + + truesize = eea_consume_rx_buffer(rx, meta, bufsize); + skb_mark_for_recycle(skb); + + skb->truesize += truesize - bufsize; + + return skb; +} + +static void process_remain_buf(struct eea_net_rx *rx, struct eea_rx_ctx *ctx) +{ + struct eea_net *enet = rx->enet; + struct sk_buff *head_skb; + u32 offset, truesize, nr_frags; + struct page *page; + + if (eea_harden_check_overflow(ctx, enet)) + goto err; + + head_skb = rx->pkt.head_skb; + + nr_frags = skb_shinfo(head_skb)->nr_frags; + if (unlikely(nr_frags >= MAX_SKB_FRAGS)) + goto err; + + offset = ctx->meta->offset + ctx->meta->headroom; + page = ctx->meta->page; + truesize = eea_consume_rx_buffer(rx, ctx->meta, + ctx->meta->headroom + ctx->len); + + skb_add_rx_frag(head_skb, nr_frags, page, offset, ctx->len, truesize); + + return; + +err: + dev_kfree_skb(rx->pkt.head_skb); + ++ctx->stats.drops; + rx->pkt.do_drop = true; + rx->pkt.head_skb = NULL; +} + +static void process_first_buf(struct eea_net_rx *rx, struct eea_rx_ctx *ctx) +{ + struct eea_net *enet = rx->enet; + struct sk_buff *skb = NULL; + + if (eea_harden_check_size(ctx, enet)) + goto err; + + rx->pkt.data_valid = ctx->flags & EEA_DESC_F_DATA_VALID; + + if (ctx->hdr_len) + skb = eea_rx_build_split_hdr_skb(rx, ctx); + else + skb = eea_rx_build_skb(rx, ctx); + + if (unlikely(!skb)) + goto err; + + rx->pkt.head_skb = skb; + + return; + +err: + ++ctx->stats.drops; + rx->pkt.do_drop = true; +} + +static void eea_submit_skb(struct eea_net_rx *rx, struct sk_buff *skb, + struct eea_rx_cdesc *desc) +{ + struct eea_net *enet = rx->enet; + + if (rx->pkt.data_valid) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (enet->cfg.ts_cfg.rx_filter == HWTSTAMP_FILTER_ALL) + skb_hwtstamps(skb)->hwtstamp = EEA_DESC_TS(desc) + + enet->hw_ts_offset; + + skb_record_rx_queue(skb, rx->index); + skb->protocol = eth_type_trans(skb, enet->netdev); + + napi_gro_receive(rx->napi, skb); +} + +static int eea_rx_desc_to_ctx(struct eea_net_rx *rx, + struct eea_rx_ctx *ctx, + struct eea_rx_cdesc *desc) +{ + u16 id; + + ctx->meta = NULL; + + id = le16_to_cpu(desc->id); + if (unlikely(id >= rx->ering->num)) { + if (net_ratelimit()) + netdev_err(rx->enet->netdev, "rx invalid id %d\n", id); + return -EINVAL; + } + + ctx->meta = &rx->meta[id]; + if (!ctx->meta->in_use) { + if (net_ratelimit()) + netdev_err(rx->enet->netdev, "rx invalid id %d\n", id); + ctx->meta = NULL; + return -EINVAL; + } + + ctx->meta->in_use = false; + + ctx->len = le16_to_cpu(desc->len); + if (unlikely(ctx->len > ctx->meta->len)) { + if (net_ratelimit()) + netdev_err(rx->enet->netdev, "rx invalid len(%d) id:%d\n", + ctx->len, id); + return -EINVAL; + } + + ctx->flags = le16_to_cpu(desc->flags); + + ctx->hdr_len = 0; + if (ctx->flags & EEA_DESC_F_SPLIT_HDR) { + ctx->hdr_len = le16_to_cpu(desc->len_ex) & + EEA_RX_CDESC_HDR_LEN_MASK; + ctx->stats.split_hdr_bytes += ctx->hdr_len; + ++ctx->stats.split_hdr_packets; + } + + ctx->more = ctx->flags & EEA_RING_DESC_F_MORE; + + return 0; +} + +static int eea_cleanrx(struct eea_net_rx *rx, int budget, + struct eea_rx_ctx *ctx) +{ + struct eea_rx_cdesc *desc; + struct eea_rx_meta *meta; + int recv, err; + + for (recv = 0; recv < budget; ) { + desc = eea_ering_cq_get_desc(rx->ering); + if (!desc) + break; + + err = eea_rx_desc_to_ctx(rx, ctx, desc); + if (unlikely(err)) { + if (ctx->meta) + eea_rx_meta_put(rx, ctx->meta); + + if (rx->pkt.head_skb) { + dev_kfree_skb(rx->pkt.head_skb); + ++ctx->stats.drops; + } + + /* A hardware error occurred; we are attempting to + * mitigate the impact. Subsequent packets may be + * corrupted. + */ + ctx->more = false; + goto ack; + } + + meta = ctx->meta; + + if (unlikely(rx->pkt.do_drop)) + goto skip; + + eea_rx_meta_dma_sync_for_cpu(rx, meta, ctx->len); + + rx->pkt.recv_len += ctx->len; + rx->pkt.recv_len += ctx->hdr_len; + + if (!rx->pkt.idx) + process_first_buf(rx, ctx); + else + process_remain_buf(rx, ctx); + + ++rx->pkt.idx; + + if (!ctx->more && rx->pkt.head_skb) { + eea_submit_skb(rx, rx->pkt.head_skb, desc); + ctx->stats.bytes += rx->pkt.recv_len; + ++ctx->stats.packets; + } + +skip: + eea_rx_meta_put(rx, meta); +ack: + eea_ering_cq_ack_desc(rx->ering, 1); + ++ctx->stats.descs; + + if (!ctx->more) { + memset(&rx->pkt, 0, sizeof(rx->pkt)); + ++recv; + } + } + + return recv; +} + +static void eea_rx_dma_sync_hdr(struct eea_net_rx *rx, dma_addr_t addr) +{ + dma_sync_single_for_device(rx->dma_dev, addr, + rx->enet->cfg.split_hdr, + DMA_FROM_DEVICE); +} + +/* Only be called from napi. */ +static void eea_rx_post(struct eea_net_rx *rx, struct eea_rx_ctx *ctx) +{ + u32 tailroom, headroom, room, len; + struct eea_rx_meta *meta; + struct eea_rx_desc *desc; + int err = 0, num = 0; + dma_addr_t addr; + + tailroom = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + headroom = rx->headroom; + room = headroom + tailroom; + + while (true) { + meta = eea_rx_meta_get(rx); + if (!meta) + break; + + err = eea_alloc_rx_buffer(rx, meta); + if (err) { + eea_rx_meta_put(rx, meta); + break; + } + + len = min_t(u32, PAGE_SIZE - meta->offset - room, + EEA_RX_BUF_MAX_LEN); + + len = ALIGN_DOWN(len, SMP_CACHE_BYTES); + + addr = meta->dma + meta->offset + headroom; + + desc = eea_ering_sq_alloc_desc(rx->ering, meta->id, true, 0); + desc->addr = cpu_to_le64(addr); + desc->len = cpu_to_le16(len); + + if (meta->hdr_addr) { + eea_rx_dma_sync_hdr(rx, meta->hdr_dma); + desc->hdr_addr = cpu_to_le64(meta->hdr_dma); + } + + eea_ering_sq_commit_desc(rx->ering); + + meta->truesize = len + room; + meta->headroom = headroom; + meta->tailroom = tailroom; + meta->len = len; + meta->in_use = true; + ++num; + } + + if (num) { + eea_ering_kick(rx->ering); + ++ctx->stats.kicks; + } +} + +static int eea_poll(struct napi_struct *napi, int budget) +{ + struct eea_irq_blk *blk = container_of(napi, struct eea_irq_blk, napi); + struct eea_net_rx *rx = blk->rx; + struct eea_net_tx *tx = &rx->enet->tx[rx->index]; + struct eea_rx_ctx ctx = {}; + bool busy = false; + u32 received; + + busy |= eea_poll_tx(tx, budget) >= budget; + + received = eea_cleanrx(rx, budget, &ctx); + + if (rx->ering->num_free > budget) { + /* Due to the hardware design, there is no notification when + * buffers are exhausted. Therefore, we should proactively + * pre-fill the buffers to avoid starvation. + */ + eea_rx_post(rx, &ctx); + + if (rx->ering->num - rx->ering->num_free < budget) + busy = true; + } + + eea_update_rx_stats(&rx->stats, &ctx.stats); + + busy |= received >= budget; + + if (busy) + return budget; + + if (napi_complete_done(napi, received)) + eea_ering_irq_active(rx->ering, tx->ering); + + return received; +} + +static void eea_free_rx_buffers(struct eea_net_rx *rx, struct eea_net_cfg *cfg) +{ + struct eea_rx_meta *meta; + u32 i; + + if (rx->pkt.head_skb) { + dev_kfree_skb(rx->pkt.head_skb); + rx->pkt.head_skb = NULL; + } + + for (i = 0; i < cfg->rx_ring_depth; ++i) { + meta = &rx->meta[i]; + if (!meta->page) + continue; + + eea_free_rx_buffer(rx, meta, false); + } +} + +static struct page_pool *eea_create_pp(struct eea_net_init_ctx *ctx, u32 idx) +{ + struct page_pool_params pp_params = {0}; + + pp_params.order = 0; + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; + pp_params.pool_size = ctx->cfg.rx_ring_depth; + pp_params.nid = dev_to_node(ctx->edev->dma_dev); + pp_params.dev = ctx->edev->dma_dev; + pp_params.netdev = ctx->netdev; + pp_params.dma_dir = DMA_FROM_DEVICE; + pp_params.max_len = PAGE_SIZE; + pp_params.queue_idx = idx; + + return page_pool_create(&pp_params); +} + +static void eea_destroy_page_pool(struct eea_net_rx *rx) +{ + if (rx->pp) + page_pool_destroy(rx->pp); +} + +void enet_rx_stop(struct eea_net_rx *rx) +{ + if (rx->flags & EEA_ENABLE_F_NAPI) { + rx->flags &= ~EEA_ENABLE_F_NAPI; + + disable_irq(rx->enet->irq_blks[rx->index].irq); + napi_disable(rx->napi); + + page_pool_disable_direct_recycling(rx->pp); + netif_napi_del(rx->napi); + } +} + +void enet_rx_start(struct eea_net_rx *rx) +{ + netif_napi_add(rx->enet->netdev, rx->napi, eea_poll); + + page_pool_enable_direct_recycling(rx->pp, rx->napi); + + napi_enable(rx->napi); + + rx->flags |= EEA_ENABLE_F_NAPI; + + local_bh_disable(); + napi_schedule(rx->napi); + local_bh_enable(); + + enable_irq(rx->enet->irq_blks[rx->index].irq); +} + +/* Maybe called before eea_bind_q_and_cfg. So the cfg must be passed. */ +void eea_free_rx(struct eea_net_rx *rx, struct eea_net_cfg *cfg) +{ + if (!rx) + return; + + if (rx->ering) { + eea_ering_free(rx->ering); + rx->ering = NULL; + } + + if (rx->meta) { + eea_free_rx_buffers(rx, cfg); + eea_free_rx_hdr(rx, cfg); + kvfree(rx->meta); + rx->meta = NULL; + } + + if (rx->pp) { + eea_destroy_page_pool(rx); + rx->pp = NULL; + } + + kfree(rx); +} + +static void eea_rx_meta_init(struct eea_net_rx *rx, u32 num) +{ + struct eea_rx_meta *meta; + int i; + + rx->free = NULL; + + for (i = 0; i < num; ++i) { + meta = &rx->meta[i]; + meta->id = i; + meta->next = rx->free; + rx->free = meta; + } +} + +struct eea_net_rx *eea_alloc_rx(struct eea_net_init_ctx *ctx, u32 idx) +{ + struct eea_ring *ering; + struct eea_net_rx *rx; + int err; + + rx = kzalloc(sizeof(*rx), GFP_KERNEL); + if (!rx) + return rx; + + rx->index = idx; + snprintf(rx->name, sizeof(rx->name), "rx.%u", idx); + + u64_stats_init(&rx->stats.syncp); + + /* ering */ + ering = eea_ering_alloc(idx * 2, ctx->cfg.rx_ring_depth, ctx->edev, + ctx->cfg.rx_sq_desc_size, + ctx->cfg.rx_cq_desc_size, + rx->name); + if (!ering) + goto err_free_rx; + + rx->ering = ering; + + rx->dma_dev = ctx->edev->dma_dev; + + /* meta */ + rx->meta = kvcalloc(ctx->cfg.rx_ring_depth, + sizeof(*rx->meta), GFP_KERNEL); + if (!rx->meta) + goto err_free_rx; + + eea_rx_meta_init(rx, ctx->cfg.rx_ring_depth); + + if (ctx->cfg.split_hdr) { + err = eea_alloc_rx_hdr(ctx, rx); + if (err) + goto err_free_rx; + } + + rx->pp = eea_create_pp(ctx, idx); + if (IS_ERR(rx->pp)) { + err = PTR_ERR(rx->pp); + rx->pp = NULL; + goto err_free_rx; + } + + return rx; + +err_free_rx: + eea_free_rx(rx, &ctx->cfg); + return NULL; +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_tx.c b/drivers/net/ethernet/alibaba/eea/eea_tx.c new file mode 100644 index 000000000000..85fb0e9ca5ba --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_tx.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include + +#include "eea_net.h" +#include "eea_pci.h" +#include "eea_ring.h" + +struct eea_sq_free_stats { + u64 packets; + u64 bytes; +}; + +struct eea_tx_meta { + struct eea_tx_meta *next; + + u32 id; + + union { + struct sk_buff *skb; + void *data; + }; + + u32 num; + + dma_addr_t dma_addr; + struct eea_tx_desc *desc; + u32 dma_len; + bool unmap; + bool unmap_single; +}; + +static struct eea_tx_meta *eea_tx_meta_get(struct eea_net_tx *tx) +{ + struct eea_tx_meta *meta; + + if (!tx->free) + return NULL; + + meta = tx->free; + tx->free = meta->next; + + return meta; +} + +static void eea_tx_meta_put_and_unmap(struct eea_net_tx *tx, + struct eea_tx_meta *meta) +{ + struct eea_tx_meta *head; + + head = meta; + + while (true) { + if (meta->unmap) { + if (meta->unmap_single) + dma_unmap_single(tx->dma_dev, meta->dma_addr, + meta->dma_len, DMA_TO_DEVICE); + else + dma_unmap_page(tx->dma_dev, meta->dma_addr, + meta->dma_len, DMA_TO_DEVICE); + } + + if (meta->next) { + meta = meta->next; + continue; + } + + break; + } + + meta->next = tx->free; + tx->free = head; +} + +static void eea_meta_free_xmit(struct eea_net_tx *tx, + struct eea_tx_meta *meta, + int budget, + struct eea_tx_cdesc *desc, + struct eea_sq_free_stats *stats) +{ + struct sk_buff *skb = meta->skb; + + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && desc)) { + struct skb_shared_hwtstamps ts = {}; + + ts.hwtstamp = EEA_DESC_TS(desc) + tx->enet->hw_ts_offset; + skb_tstamp_tx(skb, &ts); + } + + ++stats->packets; + stats->bytes += meta->skb->len; + napi_consume_skb(meta->skb, budget); + + meta->data = NULL; +} + +static int eea_clean_tx(struct eea_net_tx *tx, int budget) +{ + struct eea_sq_free_stats stats = {0}; + struct eea_tx_cdesc *desc; + struct eea_tx_meta *meta; + int desc_n; + u16 id; + + while (stats.packets < budget) { + desc = eea_ering_cq_get_desc(tx->ering); + if (!desc) + break; + + id = le16_to_cpu(desc->id); + if (unlikely(id >= tx->ering->num)) { + if (net_ratelimit()) + netdev_err(tx->enet->netdev, "tx invalid id %d\n", + id); + eea_ering_cq_ack_desc(tx->ering, 1); + continue; + } + + meta = &tx->meta[id]; + + if (meta->data) { + eea_tx_meta_put_and_unmap(tx, meta); + eea_meta_free_xmit(tx, meta, budget, desc, &stats); + desc_n = meta->num; + } else { + if (net_ratelimit()) + netdev_err(tx->enet->netdev, + "tx meta->data is null. id %d num: %d\n", + meta->id, meta->num); + desc_n = 1; + } + + eea_ering_cq_ack_desc(tx->ering, desc_n); + } + + if (stats.packets) { + u64_stats_update_begin(&tx->stats.syncp); + u64_stats_add(&tx->stats.bytes, stats.bytes); + u64_stats_add(&tx->stats.packets, stats.packets); + u64_stats_update_end(&tx->stats.syncp); + } + + return stats.packets; +} + +int eea_poll_tx(struct eea_net_tx *tx, int budget) +{ + struct eea_net *enet = tx->enet; + u32 index = tx - enet->tx; + struct netdev_queue *txq; + int num; + + txq = netdev_get_tx_queue(enet->netdev, index); + + __netif_tx_lock(txq, smp_processor_id()); + + num = eea_clean_tx(tx, budget); + + if (netif_tx_queue_stopped(txq) && + tx->ering->num_free >= MAX_SKB_FRAGS + 2) + netif_tx_wake_queue(txq); + + __netif_tx_unlock(txq); + + return num; +} + +static int eea_fill_desc_from_skb(const struct sk_buff *skb, + struct eea_tx_desc *desc) +{ + if (skb_is_gso(skb)) { + struct skb_shared_info *sinfo = skb_shinfo(skb); + + desc->gso_size = cpu_to_le16(sinfo->gso_size); + if (sinfo->gso_type & SKB_GSO_TCPV4) + desc->gso_type = EEA_TX_GSO_TCPV4; + + else if (sinfo->gso_type & SKB_GSO_TCPV6) + desc->gso_type = EEA_TX_GSO_TCPV6; + + else if (sinfo->gso_type & SKB_GSO_UDP_L4) + desc->gso_type = EEA_TX_GSO_UDP_L4; + + else + return -EINVAL; + + if (sinfo->gso_type & SKB_GSO_TCP_ECN) + desc->gso_type |= EEA_TX_GSO_ECN; + } else { + desc->gso_type = EEA_TX_GSO_NONE; + } + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + desc->csum_start = cpu_to_le16(skb_checksum_start_offset(skb)); + desc->csum_offset = cpu_to_le16(skb->csum_offset); + } + + return 0; +} + +static struct eea_tx_meta *__eea_tx_desc_fill(struct eea_net_tx *tx, + struct eea_tx_meta *head_meta, + dma_addr_t addr, u32 data_len, + u32 dma_len, bool last, + void *data, u16 flags, + bool unmap) +{ + struct eea_tx_meta *meta; + struct eea_tx_desc *desc; + + meta = eea_tx_meta_get(tx); + + desc = eea_ering_sq_alloc_desc(tx->ering, meta->id, last, flags); + desc->addr = cpu_to_le64(addr); + desc->len = cpu_to_le16(data_len); + + meta->next = NULL; + meta->dma_len = dma_len; + meta->dma_addr = addr; + meta->data = data; + meta->num = 1; + meta->desc = desc; + meta->unmap = unmap; + meta->unmap_single = false; + + if (head_meta) { + meta->next = head_meta->next; + head_meta->next = meta; + ++head_meta->num; + } + + return meta; +} + +static struct eea_tx_meta *eea_tx_desc_fill(struct eea_net_tx *tx, + struct eea_tx_meta *head_meta, + dma_addr_t addr, u32 length, + bool is_last, void *data, u16 flags) +{ + struct eea_tx_meta *meta; + u16 len, last; + + WARN_ON_ONCE(length >= 2 * USHRT_MAX); + + /* Since eea does not support BIG TCP, the maximum GSO size is capped at + * 64KB. Consequently, a single skb buffer (head or fragment) will not + * require more than two descriptors + */ + if (length > USHRT_MAX) { + len = USHRT_MAX; + last = false; + } else { + len = length; + last = is_last; + } + + meta = __eea_tx_desc_fill(tx, head_meta, addr, len, length, + last, data, flags, true); + + if (length > USHRT_MAX) { + if (!head_meta) + head_meta = meta; + + addr += USHRT_MAX; + len = length - USHRT_MAX; + + __eea_tx_desc_fill(tx, head_meta, addr, len, 0, is_last, + NULL, 0, false); + } + + return meta; +} + +static int eea_tx_add_skb_frag(struct eea_net_tx *tx, + struct eea_tx_meta *head_meta, + const skb_frag_t *frag, bool is_last) +{ + u32 len = skb_frag_size(frag); + dma_addr_t addr; + + addr = skb_frag_dma_map(tx->dma_dev, frag, 0, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(tx->dma_dev, addr))) + return -ENOMEM; + + eea_tx_desc_fill(tx, head_meta, addr, len, is_last, NULL, 0); + + return 0; +} + +static int eea_tx_post_skb(struct eea_net_tx *tx, struct sk_buff *skb) +{ + const struct skb_shared_info *shinfo = skb_shinfo(skb); + u32 hlen = skb_headlen(skb); + struct eea_tx_meta *meta; + const skb_frag_t *frag; + dma_addr_t addr; + u32 len = hlen; + int i, err; + u16 flags; + bool last; + + if (len) { + addr = dma_map_single(tx->dma_dev, skb->data, len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(tx->dma_dev, addr))) + return -ENOMEM; + + last = !shinfo->nr_frags; + i = 0; + } else { + /* The net stack will never submit an skb with an skb->len of + * 0. If the head len is 0, the number of frags must be greater + * than 0. + */ + frag = &shinfo->frags[0]; + len = skb_frag_size(frag); + + addr = skb_frag_dma_map(tx->dma_dev, frag, 0, len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(tx->dma_dev, addr))) + return -ENOMEM; + + last = shinfo->nr_frags == 1; + i = 1; + } + + flags = skb->ip_summed == CHECKSUM_PARTIAL ? EEA_DESC_F_DO_CSUM : 0; + + meta = eea_tx_desc_fill(tx, NULL, addr, len, last, skb, flags); + meta->unmap_single = !!hlen; + + err = eea_fill_desc_from_skb(skb, meta->desc); + if (err) + goto err_cancel; + + for (; i < shinfo->nr_frags; i++) { + frag = &shinfo->frags[i]; + bool is_last = i == (shinfo->nr_frags - 1); + + err = eea_tx_add_skb_frag(tx, meta, frag, is_last); + if (err) + goto err_cancel; + } + + eea_ering_sq_commit_desc(tx->ering); + + u64_stats_update_begin(&tx->stats.syncp); + u64_stats_add(&tx->stats.descs, meta->num); + u64_stats_update_end(&tx->stats.syncp); + + return 0; + +err_cancel: + eea_ering_sq_cancel(tx->ering); + eea_tx_meta_put_and_unmap(tx, meta); + meta->data = NULL; + return err; +} + +static void eea_tx_kick(struct eea_net_tx *tx) +{ + eea_ering_kick(tx->ering); + + u64_stats_update_begin(&tx->stats.syncp); + u64_stats_inc(&tx->stats.kicks); + u64_stats_update_end(&tx->stats.syncp); +} + +static int eea_tx_check_free_num(struct eea_net_tx *tx, + struct netdev_queue *txq) +{ + int n; + + /* MAX_SKB_FRAGS + 1: Covers the skb linear head and all paged fragments + * 1: Extra slot for a head or fragment that exceeds 64KB. + */ + n = MAX_SKB_FRAGS + 2; + return netif_txq_maybe_stop(txq, tx->ering->num_free, n, n); +} + +netdev_tx_t eea_tx_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct eea_net *enet = netdev_priv(netdev); + int qnum = skb_get_queue_mapping(skb); + struct eea_net_tx *tx = &enet->tx[qnum]; + struct netdev_queue *txq; + int err, enable; + + txq = netdev_get_tx_queue(netdev, qnum); + + enable = eea_tx_check_free_num(tx, txq); + if (!enable) + return NETDEV_TX_BUSY; + + err = eea_tx_post_skb(tx, skb); + if (unlikely(err)) { + u64_stats_update_begin(&tx->stats.syncp); + u64_stats_inc(&tx->stats.drops); + u64_stats_update_end(&tx->stats.syncp); + + dev_kfree_skb_any(skb); + } else { + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + skb_tx_timestamp(skb); + } + + /* NETDEV_TX_BUSY is expensive. So stop advancing the TX queue. */ + eea_tx_check_free_num(tx, txq); + + if (!netdev_xmit_more() || netif_xmit_stopped(txq)) + eea_tx_kick(tx); + + return NETDEV_TX_OK; +} + +static void eea_free_meta(struct eea_net_tx *tx, struct eea_net_cfg *cfg) +{ + struct eea_sq_free_stats stats = {0}; + struct eea_tx_meta *meta; + int i; + + while ((meta = eea_tx_meta_get(tx))) + meta->skb = NULL; + + for (i = 0; i < cfg->tx_ring_depth; i++) { + meta = &tx->meta[i]; + + if (!meta->skb) + continue; + + eea_tx_meta_put_and_unmap(tx, meta); + + eea_meta_free_xmit(tx, meta, 0, NULL, &stats); + } + + kvfree(tx->meta); + tx->meta = NULL; +} + +/* Maybe called before eea_bind_q_and_cfg. So the cfg must be passed. */ +void eea_free_tx(struct eea_net_tx *tx, struct eea_net_cfg *cfg) +{ + if (!tx) + return; + + if (tx->ering) { + eea_ering_free(tx->ering); + tx->ering = NULL; + } + + if (tx->meta) + eea_free_meta(tx, cfg); +} + +int eea_alloc_tx(struct eea_net_init_ctx *ctx, struct eea_net_tx *tx, u32 idx) +{ + struct eea_tx_meta *meta; + struct eea_ring *ering; + u32 i; + + u64_stats_init(&tx->stats.syncp); + + snprintf(tx->name, sizeof(tx->name), "tx.%u", idx); + + ering = eea_ering_alloc(idx * 2 + 1, ctx->cfg.tx_ring_depth, ctx->edev, + ctx->cfg.tx_sq_desc_size, + ctx->cfg.tx_cq_desc_size, + tx->name); + if (!ering) + goto err_free_tx; + + tx->ering = ering; + tx->index = idx; + tx->dma_dev = ctx->edev->dma_dev; + + /* meta */ + tx->meta = kvcalloc(ctx->cfg.tx_ring_depth, + sizeof(*tx->meta), GFP_KERNEL); + if (!tx->meta) + goto err_free_tx; + + for (i = 0; i < ctx->cfg.tx_ring_depth; ++i) { + meta = &tx->meta[i]; + meta->id = i; + meta->next = tx->free; + tx->free = meta; + } + + return 0; + +err_free_tx: + eea_free_tx(tx, &ctx->cfg); + return -ENOMEM; +} diff --git a/drivers/net/ethernet/amazon/ena/ena_devlink.c b/drivers/net/ethernet/amazon/ena/ena_devlink.c index 4772185e669d..5ea9fef149aa 100644 --- a/drivers/net/ethernet/amazon/ena/ena_devlink.c +++ b/drivers/net/ethernet/amazon/ena/ena_devlink.c @@ -8,12 +8,12 @@ #include "ena_phc.h" static int ena_devlink_enable_phc_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); - if (!val.vbool) + if (!val->vbool) return 0; if (!ena_com_phc_supported(adapter->ena_dev)) { @@ -57,7 +57,7 @@ void ena_devlink_disable_phc_param(struct devlink *devlink) value.vbool = false; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC, - value); + &value); devl_unlock(devlink); } @@ -151,7 +151,7 @@ static int ena_devlink_configure_params(struct devlink *devlink) value.vbool = ena_phc_is_enabled(adapter); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC, - value); + &value); devl_unlock(devlink); return 0; diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c index ce9445425045..672c4996b30e 100644 --- a/drivers/net/ethernet/amd/a2065.c +++ b/drivers/net/ethernet/amd/a2065.c @@ -647,10 +647,10 @@ static void a2065_remove_one(struct zorro_dev *z); static const struct zorro_device_id a2065_zorro_tbl[] = { - { ZORRO_PROD_CBM_A2065_1 }, - { ZORRO_PROD_CBM_A2065_2 }, - { ZORRO_PROD_AMERISTAR_A2065 }, - { 0 } + { .id = ZORRO_PROD_CBM_A2065_1 }, + { .id = ZORRO_PROD_CBM_A2065_2 }, + { .id = ZORRO_PROD_AMERISTAR_A2065 }, + { } }; MODULE_DEVICE_TABLE(zorro, a2065_zorro_tbl); diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c index fa201da567ed..ec6e7e8c14fe 100644 --- a/drivers/net/ethernet/amd/ariadne.c +++ b/drivers/net/ethernet/amd/ariadne.c @@ -693,8 +693,8 @@ static void ariadne_remove_one(struct zorro_dev *z) } static const struct zorro_device_id ariadne_zorro_tbl[] = { - { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, - { 0 } + { .id = ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, + { } }; MODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl); diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 705cab7b0727..38a2446571af 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -446,6 +446,8 @@ int pdsc_setup(struct pdsc *pdsc, bool init) { int err; + pci_set_master(pdsc->pdev); + err = pdsc_dev_init(pdsc); if (err) return err; @@ -480,6 +482,8 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) if (pdsc->adminqcq.work.func) cancel_work_sync(&pdsc->adminqcq.work); + pci_clear_master(pdsc->pdev); + pdsc_core_uninit(pdsc); if (removing) { diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h index 4a6b35c84dab..b7fe9ad73349 100644 --- a/drivers/net/ethernet/amd/pds_core/core.h +++ b/drivers/net/ethernet/amd/pds_core/core.h @@ -261,7 +261,7 @@ int pdsc_dl_enable_set(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx, struct netlink_ext_ack *extack); int pdsc_dl_enable_validate(struct devlink *dl, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack); void __iomem *pdsc_map_dbpage(struct pdsc *pdsc, int page_num); diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index 3f0e56b951bf..2ea97e1c5939 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -68,7 +68,7 @@ int pdsc_dl_enable_set(struct devlink *dl, u32 id, } int pdsc_dl_enable_validate(struct devlink *dl, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct pdsc *pdsc = devlink_priv(dl); diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c index e419c73b32ce..4be7b629bfac 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c @@ -181,6 +181,20 @@ aq_check_approve_fvlan(struct aq_nic_s *aq_nic, return 0; } +static bool aq_is_ipv6_flow_type(const struct ethtool_rx_flow_spec *fsp) +{ + switch (fsp->flow_type & ~FLOW_EXT) { + case TCP_V6_FLOW: + case UDP_V6_FLOW: + case SCTP_V6_FLOW: + case IPV6_FLOW: + case IPV6_USER_FLOW: + return true; + default: + return false; + } +} + static int __must_check aq_check_filter(struct aq_nic_s *aq_nic, struct ethtool_rx_flow_spec *fsp) @@ -466,31 +480,23 @@ static int aq_add_del_fvlan(struct aq_nic_s *aq_nic, return aq_filters_vlans_update(aq_nic); } -static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic, - struct aq_rx_filter *aq_rx_fltr, - struct aq_rx_filter_l3l4 *data, bool add) +int aq_set_data_fl3l4(const struct ethtool_rx_flow_spec *fsp, + struct aq_rx_filter_l3l4 *data, + int location, bool add) { - struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); - const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp; + u32 flow = fsp->flow_type & ~FLOW_EXT; memset(data, 0, sizeof(*data)); - data->is_ipv6 = rx_fltrs->fl3l4.is_ipv6; - data->location = HW_ATL_GET_REG_LOCATION_FL3L4(fsp->location); - - if (!add) { - if (!data->is_ipv6) - rx_fltrs->fl3l4.active_ipv4 &= ~BIT(data->location); - else - rx_fltrs->fl3l4.active_ipv6 &= - ~BIT((data->location) / 4); + data->is_ipv6 = aq_is_ipv6_flow_type(fsp); + data->location = location; + if (!add) return 0; - } data->cmd |= HW_ATL_RX_ENABLE_FLTR_L3L4; - switch (fsp->flow_type) { + switch (flow) { case TCP_V4_FLOW: case TCP_V6_FLOW: data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4; @@ -514,11 +520,9 @@ static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic, ntohl(fsp->h_u.tcp_ip4_spec.ip4src); data->ip_dst[0] = ntohl(fsp->h_u.tcp_ip4_spec.ip4dst); - rx_fltrs->fl3l4.active_ipv4 |= BIT(data->location); } else { int i; - rx_fltrs->fl3l4.active_ipv6 |= BIT((data->location) / 4); for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) { data->ip_dst[i] = ntohl(fsp->h_u.tcp_ip6_spec.ip6dst[i]); @@ -527,23 +531,23 @@ static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic, } data->cmd |= HW_ATL_RX_ENABLE_L3_IPV6; } - if (fsp->flow_type != IP_USER_FLOW && - fsp->flow_type != IPV6_USER_FLOW) { - if (!data->is_ipv6) { - data->p_dst = - ntohs(fsp->h_u.tcp_ip4_spec.pdst); - data->p_src = - ntohs(fsp->h_u.tcp_ip4_spec.psrc); - } else { - data->p_dst = - ntohs(fsp->h_u.tcp_ip6_spec.pdst); - data->p_src = - ntohs(fsp->h_u.tcp_ip6_spec.psrc); - } + if (flow == TCP_V4_FLOW || flow == UDP_V4_FLOW || + flow == SCTP_V4_FLOW) { + data->p_dst = ntohs(fsp->h_u.tcp_ip4_spec.pdst); + data->p_src = ntohs(fsp->h_u.tcp_ip4_spec.psrc); } - if (data->ip_src[0] && !data->is_ipv6) + if (flow == TCP_V6_FLOW || flow == UDP_V6_FLOW || + flow == SCTP_V6_FLOW) { + data->p_dst = ntohs(fsp->h_u.tcp_ip6_spec.pdst); + data->p_src = ntohs(fsp->h_u.tcp_ip6_spec.psrc); + } + if (data->ip_src[0] || + (data->is_ipv6 && (data->ip_src[1] || data->ip_src[2] || + data->ip_src[3]))) data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3; - if (data->ip_dst[0] && !data->is_ipv6) + if (data->ip_dst[0] || + (data->is_ipv6 && (data->ip_dst[1] || data->ip_dst[2] || + data->ip_dst[3]))) data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3; if (data->p_dst) data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4; @@ -573,16 +577,38 @@ static int aq_set_fl3l4(struct aq_hw_s *aq_hw, static int aq_add_del_fl3l4(struct aq_nic_s *aq_nic, struct aq_rx_filter *aq_rx_fltr, bool add) { + struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic); const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops; struct aq_hw_s *aq_hw = aq_nic->aq_hw; struct aq_rx_filter_l3l4 data; + int location; + int err; if (unlikely(aq_rx_fltr->aq_fsp.location < AQ_RX_FIRST_LOC_FL3L4 || - aq_rx_fltr->aq_fsp.location > AQ_RX_LAST_LOC_FL3L4 || - aq_set_data_fl3l4(aq_nic, aq_rx_fltr, &data, add))) + aq_rx_fltr->aq_fsp.location > AQ_RX_LAST_LOC_FL3L4)) return -EINVAL; - return aq_set_fl3l4(aq_hw, aq_hw_ops, &data); + location = HW_ATL_GET_REG_LOCATION_FL3L4(aq_rx_fltr->aq_fsp.location); + + aq_set_data_fl3l4(&aq_rx_fltr->aq_fsp, &data, location, add); + + err = aq_set_fl3l4(aq_hw, aq_hw_ops, &data); + if (err) + return err; + + if (add) { + if (!data.is_ipv6) + rx_fltrs->fl3l4.active_ipv4 |= BIT(data.location); + else + rx_fltrs->fl3l4.active_ipv6 |= BIT(data.location / 4); + } else { + if (!data.is_ipv6) + rx_fltrs->fl3l4.active_ipv4 &= ~BIT(data.location); + else + rx_fltrs->fl3l4.active_ipv6 &= ~BIT(data.location / 4); + } + + return 0; } static int aq_add_del_rule(struct aq_nic_s *aq_nic, diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.h b/drivers/net/ethernet/aquantia/atlantic/aq_filters.h index 122e06c88a33..96e89c8e52d0 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_filters.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_filters.h @@ -32,5 +32,8 @@ int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic); int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic); int aq_filters_vlans_update(struct aq_nic_s *aq_nic); int aq_filters_vlan_offload_off(struct aq_nic_s *aq_nic); +int aq_set_data_fl3l4(const struct ethtool_rx_flow_spec *fsp, + struct aq_rx_filter_l3l4 *data, + int location, bool add); #endif /* AQ_FILTERS_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h index 57ea59026a2c..338c956ad1f7 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h @@ -15,10 +15,14 @@ #include "aq_common.h" #include "aq_rss.h" #include "hw_atl/hw_atl_utils.h" +#include "hw_atl2/hw_atl2.h" #define AQ_HW_MAC_COUNTER_HZ 312500000ll #define AQ_HW_PHY_COUNTER_HZ 160000000ll +#define AQ_HW_TXD_CTL_TS_EN 0x40000000U +#define AQ_HW_TXD_CTL_TS_TSG0 0x80000000U + enum aq_tc_mode { AQ_TC_MODE_INVALID = -1, AQ_TC_MODE_8TCS, @@ -38,6 +42,8 @@ enum aq_tc_mode { #define AQ_FRAC_PER_NS 0x100000000LL +#define AQ2_HW_PTP_COUNTER_HZ 156250000ll + /* Used for rate to Mbps conversion */ #define AQ_MBPS_DIVISOR 125000 /* 1000000 / 8 */ @@ -109,6 +115,7 @@ struct aq_stats_s { #define AQ_HW_IRQ_MSIX 3U #define AQ_HW_SERVICE_IRQS 1U +#define AQ_HW_PTP_IRQS 1U #define AQ_HW_POWER_STATE_D0 0U #define AQ_HW_POWER_STATE_D3 3U @@ -157,6 +164,15 @@ enum aq_priv_flags { AQ_HW_LOOPBACK_PHYEXT_SYS, }; +enum { + AQ_HW_PTP_DISABLE = 0, + AQ_HW_PTP_L2_ENABLE = BIT(1), + AQ_HW_PTP_L4_ENABLE = BIT(2), +}; + +#define ATL_TSG_CLOCK_SEL_0 0 +#define ATL_TSG_CLOCK_SEL_1 1 + #define AQ_HW_LOOPBACK_MASK (BIT(AQ_HW_LOOPBACK_DMA_SYS) |\ BIT(AQ_HW_LOOPBACK_PKT_SYS) |\ BIT(AQ_HW_LOOPBACK_DMA_NET) |\ @@ -198,6 +214,7 @@ struct aq_hw_s { u32 rpc_tid; struct hw_atl_utils_fw_rpc rpc; s64 ptp_clk_offset; + s8 clk_select; u16 phy_id; void *priv; }; @@ -323,11 +340,15 @@ struct aq_hw_ops { int (*hw_ts_to_sys_clock)(struct aq_hw_s *self, u64 ts, u64 *time); - int (*hw_gpio_pulse)(struct aq_hw_s *self, u32 index, u64 start, - u32 period); + int (*hw_gpio_pulse)(struct aq_hw_s *self, u32 index, + u32 clk_sel, u64 start, + u32 period, u32 hightime); int (*hw_extts_gpio_enable)(struct aq_hw_s *self, u32 index, - u32 enable); + u32 channel, int enable); + + void (*enable_ptp)(struct aq_hw_s *self, unsigned int param, + int enable); int (*hw_get_sync_ts)(struct aq_hw_s *self, u64 *ts); @@ -337,6 +358,14 @@ struct aq_hw_ops { int (*extract_hwts)(struct aq_hw_s *self, u8 *p, unsigned int len, u64 *timestamp); + u64 (*hw_ring_tx_ptp_get_ts)(struct aq_ring_s *ring); + + int (*hw_tx_ptp_ring_init)(struct aq_hw_s *self, + struct aq_ring_s *aq_ring); + int (*hw_rx_ptp_ring_init)(struct aq_hw_s *self, + struct aq_ring_s *aq_ring); + u32 (*hw_get_clk_sel)(struct aq_hw_s *self); + int (*hw_set_fc)(struct aq_hw_s *self, u32 fc, u32 tc); int (*hw_set_loopback)(struct aq_hw_s *self, u32 mode, bool enable); diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c index 4ef4fe64b8ac..1da14786fe5c 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -68,14 +70,6 @@ int aq_ndev_open(struct net_device *ndev) if (err < 0) goto err_exit; - err = aq_reapply_rxnfc_all_rules(aq_nic); - if (err < 0) - goto err_exit; - - err = aq_filters_vlans_update(aq_nic); - if (err < 0) - goto err_exit; - err = aq_nic_start(aq_nic); if (err < 0) { aq_nic_stop(aq_nic); @@ -113,12 +107,20 @@ static netdev_tx_t aq_ndev_start_xmit(struct sk_buff *skb, struct net_device *nd * and hardware PTP design of the chip. Otherwise ptp stream * will fail to sync */ - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) || - unlikely((ip_hdr(skb)->version == 4) && - (ip_hdr(skb)->protocol == IPPROTO_UDP) && - ((udp_hdr(skb)->dest == htons(319)) || - (udp_hdr(skb)->dest == htons(320)))) || - unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588))) + if (unlikely(skb->protocol == htons(ETH_P_IP) && + ip_hdr(skb)->protocol == IPPROTO_UDP && + (udp_hdr(skb)->dest == htons(PTP_EV_PORT) || + udp_hdr(skb)->dest == htons(PTP_GEN_PORT)))) + return aq_ptp_xmit(aq_nic, skb); + + /* PTP over IPv6 does not use extension headers */ + if (unlikely(skb->protocol == htons(ETH_P_IPV6) && + ipv6_hdr(skb)->nexthdr == IPPROTO_UDP && + (udp_hdr(skb)->dest == htons(PTP_EV_PORT) || + udp_hdr(skb)->dest == htons(PTP_GEN_PORT)))) + return aq_ptp_xmit(aq_nic, skb); + + if (unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588))) return aq_ptp_xmit(aq_nic, skb); } #endif diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index ef9447810071..01792f7abf96 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -72,8 +72,15 @@ static void aq_nic_cfg_update_num_vecs(struct aq_nic_s *self) cfg->vecs = min(cfg->aq_hw_caps->vecs, AQ_CFG_VECS_DEF); cfg->vecs = min(cfg->vecs, num_online_cpus()); - if (self->irqvecs > AQ_HW_SERVICE_IRQS) - cfg->vecs = min(cfg->vecs, self->irqvecs - AQ_HW_SERVICE_IRQS); + if (self->irqvecs > AQ_HW_SERVICE_IRQS + AQ_HW_PTP_IRQS) + cfg->vecs = min(cfg->vecs, + self->irqvecs - AQ_HW_SERVICE_IRQS - AQ_HW_PTP_IRQS); + else if (self->irqvecs > AQ_HW_PTP_IRQS) + cfg->vecs = min(cfg->vecs, + self->irqvecs - AQ_HW_PTP_IRQS); + else + cfg->vecs = 1U; + /* cfg->vecs should be power of 2 for RSS */ cfg->vecs = rounddown_pow_of_two(cfg->vecs); @@ -138,7 +145,8 @@ void aq_nic_cfg_start(struct aq_nic_s *self) * link status IRQ. If no - we'll know link state from * slower service task. */ - if (AQ_HW_SERVICE_IRQS > 0 && cfg->vecs + 1 <= self->irqvecs) + if (AQ_HW_SERVICE_IRQS > 0 && + self->irqvecs > AQ_HW_PTP_IRQS + AQ_HW_SERVICE_IRQS) cfg->link_irq_vec = cfg->vecs; else cfg->link_irq_vec = 0; @@ -172,7 +180,14 @@ static int aq_nic_update_link_status(struct aq_nic_s *self) aq_nic_update_interrupt_moderation_settings(self); if (self->aq_ptp) { - aq_ptp_clock_init(self); + /* AQC113 TSG requires >= 100 Mbps full-duplex: below 100 Mbps + * timestamp resolution is insufficient for IEEE 1588, and + * half-duplex collision domains make TX/RX correlation unreliable. + */ + bool ptp_link_good = (self->aq_hw->aq_link_status.mbps >= 100 && + self->aq_hw->aq_link_status.full_duplex); + + aq_ptp_clock_init(self, ptp_link_good ? AQ_PTP_LINK_UP : AQ_PTP_NO_LINK); aq_ptp_tm_offset_set(self, self->aq_hw->aq_link_status.mbps); aq_ptp_link_change(self); @@ -279,6 +294,7 @@ static int aq_nic_hw_prepare(struct aq_nic_s *self) int err = 0; err = self->aq_hw_ops->hw_soft_reset(self->aq_hw); + if (err) goto exit; @@ -450,7 +466,14 @@ int aq_nic_init(struct aq_nic_s *self) } if (aq_nic_get_cfg(self)->is_ptp) { - err = aq_ptp_init(self, self->irqvecs - 1); + u32 ptp_isr_vec; + + if (self->irqvecs > AQ_HW_PTP_IRQS) + ptp_isr_vec = self->irqvecs - AQ_HW_PTP_IRQS; + else + ptp_isr_vec = 0; + + err = aq_ptp_init(self, ptp_isr_vec); if (err < 0) goto err_exit; @@ -496,6 +519,14 @@ int aq_nic_start(struct aq_nic_s *self) goto err_exit; } + err = aq_reapply_rxnfc_all_rules(self); + if (err < 0) + goto err_exit; + + err = aq_filters_vlans_update(self); + if (err < 0) + goto err_exit; + err = aq_ptp_ring_start(self); if (err < 0) goto err_exit; @@ -683,6 +714,7 @@ unsigned int aq_nic_map_skb(struct aq_nic_s *self, struct sk_buff *skb, unsigned int ret = 0U; unsigned int dx; u8 l4proto = 0; + s32 clk_sel; if (ipver == 4) l4proto = ip_hdr(skb)->protocol; @@ -795,6 +827,17 @@ unsigned int aq_nic_map_skb(struct aq_nic_s *self, struct sk_buff *skb, dx_buff->is_eop = 1U; dx_buff->skb = skb; dx_buff->xdpf = NULL; + if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && + self->aq_hw_ops->enable_ptp && + self->aq_hw_ops->hw_get_clk_sel && + aq_ptp_ring(self, ring)) { + clk_sel = (s32)self->aq_hw_ops->hw_get_clk_sel(self->aq_hw); + if (clk_sel < 0) + goto exit; + dx_buff->request_ts = 1U; + dx_buff->clk_sel = (u32)clk_sel; + ring->ptp_ts_deadline = jiffies + HZ; + } goto exit; mapping_error: diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c index 39e1b606a75a..005d6ef4fc36 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c @@ -250,6 +250,7 @@ static int aq_pci_probe(struct pci_dev *pdev, goto err_ioremap; } self->aq_hw->aq_nic_cfg = aq_nic_get_cfg(self); + self->aq_hw->clk_select = -1; if (self->aq_hw->aq_nic_cfg->aq_hw_caps->priv_data_len) { int len = self->aq_hw->aq_nic_cfg->aq_hw_caps->priv_data_len; @@ -293,8 +294,8 @@ static int aq_pci_probe(struct pci_dev *pdev, numvecs = min((u8)AQ_CFG_VECS_DEF, aq_nic_get_cfg(self)->aq_hw_caps->msix_irqs); numvecs = min(numvecs, num_online_cpus()); - /* Request IRQ vector for PTP */ - numvecs += 1; + /* Request IRQ lines for PTP */ + numvecs += AQ_HW_PTP_IRQS; numvecs += AQ_HW_SERVICE_IRQS; /*enable interrupts */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c index 9df8918216f6..558ac9237f75 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c @@ -26,6 +26,18 @@ #define POLL_SYNC_TIMER_MS 15 +#define PTP_UDP_FILTERS_CNT 4 + +#define PTP_IPV4_MC_ADDR1 0xE0000181 +#define PTP_IPV4_MC_ADDR2 0xE000006B + +#define PTP_IPV6_MC_ADDR10 0xFF0E +#define PTP_IPV6_MC_ADDR14 0x0181 +#define PTP_IPV6_MC_ADDR20 0xFF02 +#define PTP_IPV6_MC_ADDR24 0x006B + +#define PTP_GPIO_HIGHTIME 100000 + enum ptp_speed_offsets { ptp_offset_idx_10 = 0, ptp_offset_idx_100, @@ -49,11 +61,18 @@ struct ptp_tx_timeout { unsigned long tx_start; }; +struct ptp_tm_offset { + unsigned int mbps; + int egress; + int ingress; +}; + struct aq_ptp_s { struct aq_nic_s *aq_nic; struct kernel_hwtstamp_config hwtstamp_config; spinlock_t ptp_lock; spinlock_t ptp_ring_lock; + struct mutex ptp_filter_lock; /* serializes aq_ptp_dpath_enable() */ struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_info; @@ -64,7 +83,7 @@ struct aq_ptp_s { struct ptp_tx_timeout ptp_tx_timeout; - unsigned int idx_vector; + unsigned int idx_ptp_vector; struct napi_struct napi; struct aq_ring_s ptp_tx; @@ -73,7 +92,7 @@ struct aq_ptp_s { struct ptp_skb_ring skb_ring; - struct aq_rx_filter_l3l4 udp_filter; + struct aq_rx_filter_l3l4 udp_filter[PTP_UDP_FILTERS_CNT]; struct aq_rx_filter_l2 eth_type_filter; struct delayed_work poll_sync; @@ -81,18 +100,15 @@ struct aq_ptp_s { bool extts_pin_enabled; u64 last_sync1588_ts; + /* TSG clock selection: 0 - PTP, 1 - PTM */ + u32 ptp_clock_sel; bool a1_ptp; -}; + bool a2_ptp; -struct ptp_tm_offset { - unsigned int mbps; - int egress; - int ingress; + struct ptp_tm_offset ptp_offset[6]; }; -static struct ptp_tm_offset ptp_offset[6]; - void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps) { struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; @@ -104,10 +120,10 @@ void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps) egress = 0; ingress = 0; - for (i = 0; i < ARRAY_SIZE(ptp_offset); i++) { - if (mbps == ptp_offset[i].mbps) { - egress = ptp_offset[i].egress; - ingress = ptp_offset[i].ingress; + for (i = 0; i < ARRAY_SIZE(aq_ptp->ptp_offset); i++) { + if (mbps == aq_ptp->ptp_offset[i].mbps) { + egress = aq_ptp->ptp_offset[i].egress; + ingress = aq_ptp->ptp_offset[i].ingress; break; } } @@ -268,6 +284,18 @@ static void aq_ptp_tx_timeout_check(struct aq_ptp_s *aq_ptp) } } +void aq_ptp_tx_skb_drop_head(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + struct sk_buff *skb; + + if (!aq_ptp) + return; + skb = aq_ptp_skb_get(&aq_ptp->skb_ring); + if (skb) + dev_kfree_skb_any(skb); +} + /* aq_ptp_adjfine * @ptp: the ptp clock structure * @ppb: parts per billion adjustment from base @@ -366,6 +394,8 @@ static void aq_ptp_convert_to_hwtstamp(struct aq_ptp_s *aq_ptp, static int aq_ptp_hw_pin_conf(struct aq_nic_s *aq_nic, u32 pin_index, u64 start, u64 period) { + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + if (period) netdev_dbg(aq_nic->ndev, "Enable GPIO %d pulsing, start time %llu, period %u\n", @@ -380,7 +410,8 @@ static int aq_ptp_hw_pin_conf(struct aq_nic_s *aq_nic, u32 pin_index, u64 start, */ mutex_lock(&aq_nic->fwreq_mutex); aq_nic->aq_hw_ops->hw_gpio_pulse(aq_nic->aq_hw, pin_index, - start, (u32)period); + aq_ptp->ptp_clock_sel, start, + (u32)period, PTP_GPIO_HIGHTIME); mutex_unlock(&aq_nic->fwreq_mutex); return 0; @@ -454,6 +485,7 @@ static void aq_ptp_extts_pin_ctrl(struct aq_ptp_s *aq_ptp) if (aq_nic->aq_hw_ops->hw_extts_gpio_enable) aq_nic->aq_hw_ops->hw_extts_gpio_enable(aq_nic->aq_hw, 0, + aq_ptp->ptp_clock_sel, enable); } @@ -543,14 +575,191 @@ void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp) return; } - timestamp += atomic_read(&aq_ptp->offset_egress); - aq_ptp_convert_to_hwtstamp(aq_ptp, &hwtstamp, timestamp); - skb_tstamp_tx(skb, &hwtstamp); + if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { + timestamp += atomic_read(&aq_ptp->offset_egress); + aq_ptp_convert_to_hwtstamp(aq_ptp, &hwtstamp, timestamp); + skb_tstamp_tx(skb, &hwtstamp); + } + dev_kfree_skb_any(skb); aq_ptp_tx_timeout_update(aq_ptp); } +static void aq_ptp_fill_udpv4_mc(struct ethtool_rx_flow_spec *fsp, + u16 rx_queue, __be32 mc_addr) +{ + memset(fsp, 0, sizeof(*fsp)); + fsp->ring_cookie = rx_queue; + fsp->flow_type = UDP_V4_FLOW; + fsp->h_u.udp_ip4_spec.pdst = cpu_to_be16(PTP_EV_PORT); + fsp->m_u.udp_ip4_spec.pdst = cpu_to_be16(0xffff); + fsp->h_u.udp_ip4_spec.ip4dst = mc_addr; + fsp->m_u.udp_ip4_spec.ip4dst = cpu_to_be32(0xffffffff); +} + +static void aq_ptp_fill_udpv6_mc(struct ethtool_rx_flow_spec *fsp, + u16 rx_queue, + __be32 ip6dst_hi, __be32 ip6dst_hi_mask, + __be32 ip6dst_lo, __be32 ip6dst_lo_mask) +{ + memset(fsp, 0, sizeof(*fsp)); + fsp->ring_cookie = rx_queue; + fsp->flow_type = UDP_V6_FLOW; + fsp->h_u.udp_ip6_spec.pdst = cpu_to_be16(PTP_EV_PORT); + fsp->m_u.udp_ip6_spec.pdst = cpu_to_be16(0xffff); + fsp->h_u.udp_ip6_spec.ip6dst[0] = ip6dst_hi; + fsp->m_u.udp_ip6_spec.ip6dst[0] = ip6dst_hi_mask; + fsp->h_u.udp_ip6_spec.ip6dst[3] = ip6dst_lo; + fsp->m_u.udp_ip6_spec.ip6dst[3] = ip6dst_lo_mask; +} + +static void aq_ptp_add_a2_filter(struct aq_ptp_s *aq_ptp, + struct ethtool_rx_flow_spec *fsp, + int *flt_idx) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + + aq_set_data_fl3l4(fsp, + &aq_ptp->udp_filter[*flt_idx], + aq_ptp->udp_filter[*flt_idx].location, + true); + + netdev_dbg(aq_nic->ndev, + "PTP MC filter prepared. Loc: %x\n", + aq_ptp->udp_filter[*flt_idx].location); + (*flt_idx)++; +} + +static int aq_ptp_dpath_enable(struct aq_ptp_s *aq_ptp, + int enable_flags, u16 rx_queue) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + struct ethtool_rxnfc cmd = { 0 }; + const struct aq_hw_ops *hw_ops = aq_nic->aq_hw_ops; + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd.fs; + int err = 0, i = 0; + int flt_idx = 0; + + netdev_dbg(aq_nic->ndev, + "%sable ptp filters: %x.\n", + enable_flags ? "En" : "Dis", enable_flags); + + if (enable_flags) { + /* Clear all existing L4 filters before applying new config */ + for (i = 0; i < PTP_UDP_FILTERS_CNT; i++) { + aq_ptp->udp_filter[i].cmd &= + ~HW_ATL_RX_ENABLE_FLTR_L3L4; + if (hw_ops->hw_filter_l3l4_set) { + err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, + &aq_ptp->udp_filter[i]); + if (err) + netdev_dbg(aq_nic->ndev, + "Set UDP filter failed\n"); + } + } + if (enable_flags & (AQ_HW_PTP_L4_ENABLE)) { + if (aq_ptp->a1_ptp) { + fsp->ring_cookie = rx_queue; + fsp->flow_type = UDP_V4_FLOW; + fsp->h_u.udp_ip4_spec.pdst = + cpu_to_be16(PTP_EV_PORT); + fsp->m_u.udp_ip4_spec.pdst = + cpu_to_be16(0xffff); + err = aq_set_data_fl3l4(fsp, + &aq_ptp->udp_filter[flt_idx], + aq_ptp->udp_filter[flt_idx].location, + true); + if (!err) { + netdev_dbg(aq_nic->ndev, + "Set UDPv4, location: %x\n", + aq_ptp->udp_filter[flt_idx] + .location); + flt_idx++; + } + } else { + aq_ptp_fill_udpv4_mc(fsp, rx_queue, + cpu_to_be32(PTP_IPV4_MC_ADDR1)); + aq_ptp_add_a2_filter(aq_ptp, fsp, + &flt_idx); + + aq_ptp_fill_udpv6_mc(fsp, rx_queue, + cpu_to_be32(PTP_IPV6_MC_ADDR20 << 16), + cpu_to_be32(0xffff0000), + cpu_to_be32(PTP_IPV6_MC_ADDR24), + cpu_to_be32(0x0000ffff)); + aq_ptp_add_a2_filter(aq_ptp, fsp, + &flt_idx); + + aq_ptp_fill_udpv6_mc(fsp, rx_queue, + cpu_to_be32(PTP_IPV6_MC_ADDR10 << 16), + cpu_to_be32(0xffff0000), + cpu_to_be32(PTP_IPV6_MC_ADDR14), + cpu_to_be32(0x0000ffff)); + aq_ptp_add_a2_filter(aq_ptp, fsp, + &flt_idx); + + aq_ptp_fill_udpv4_mc(fsp, rx_queue, + cpu_to_be32(PTP_IPV4_MC_ADDR2)); + aq_ptp_add_a2_filter(aq_ptp, fsp, + &flt_idx); + } + } + + if (enable_flags & AQ_HW_PTP_L2_ENABLE) { + aq_ptp->eth_type_filter.ethertype = ETH_P_1588; + aq_ptp->eth_type_filter.queue = rx_queue; + } + + if (hw_ops->hw_filter_l3l4_set) { + for (i = 0; i < flt_idx; i++) { + err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, + &aq_ptp->udp_filter[i]); + + if (!err) { + netdev_dbg(aq_nic->ndev, + "Set UDP filter complete. Location: %x\n", + aq_ptp->udp_filter[i].location); + } else { + netdev_dbg(aq_nic->ndev, "Set UDP filter failed\n"); + break; + } + } + } + + if (!err && (enable_flags & AQ_HW_PTP_L2_ENABLE) && + hw_ops->hw_filter_l2_set) { + err = hw_ops->hw_filter_l2_set(aq_nic->aq_hw, + &aq_ptp->eth_type_filter); + + if (!err) + netdev_dbg(aq_nic->ndev, + "Set L2 filter complete. Location: %d\n", + aq_ptp->eth_type_filter.location); + } + } else { + /* PTP disabled, clear all UDP/L2 filters */ + for (i = 0; i < PTP_UDP_FILTERS_CNT; i++) { + aq_ptp->udp_filter[i].cmd &= + ~HW_ATL_RX_ENABLE_FLTR_L3L4; + if (hw_ops->hw_filter_l3l4_set) { + err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, + &aq_ptp->udp_filter[i]); + if (err) + netdev_dbg(aq_nic->ndev, + "Set UDP filter failed\n"); + } + } + + if (!err && hw_ops->hw_filter_l2_clear) + err = hw_ops->hw_filter_l2_clear(aq_nic->aq_hw, + &aq_ptp->eth_type_filter); + } + + return err; +} + /* aq_ptp_rx_hwtstamp - utility function which checks for RX time stamp * @adapter: pointer to adapter struct * @shhwtstamps: particular skb_shared_hwtstamps to save timestamp @@ -572,59 +781,61 @@ void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp, *config = aq_ptp->hwtstamp_config; } -static void aq_ptp_prepare_filters(struct aq_ptp_s *aq_ptp) +static unsigned int aq_ptp_parse_rx_filters(enum hwtstamp_rx_filters rx_filter) { - aq_ptp->udp_filter.cmd = HW_ATL_RX_ENABLE_FLTR_L3L4 | - HW_ATL_RX_ENABLE_CMP_PROT_L4 | - HW_ATL_RX_UDP | - HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4 | - HW_ATL_RX_HOST << HW_ATL_RX_ACTION_FL3F4_SHIFT | - HW_ATL_RX_ENABLE_QUEUE_L3L4 | - aq_ptp->ptp_rx.idx << HW_ATL_RX_QUEUE_FL3L4_SHIFT; - aq_ptp->udp_filter.p_dst = PTP_EV_PORT; + unsigned int ptp_en_flags = AQ_HW_PTP_DISABLE; - aq_ptp->eth_type_filter.ethertype = ETH_P_1588; - aq_ptp->eth_type_filter.queue = aq_ptp->ptp_rx.idx; + switch (rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + ptp_en_flags = AQ_HW_PTP_L2_ENABLE; + break; + 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_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + ptp_en_flags = AQ_HW_PTP_L4_ENABLE; + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_ALL: + default: + ptp_en_flags = AQ_HW_PTP_L4_ENABLE | AQ_HW_PTP_L2_ENABLE; + break; + } + return ptp_en_flags; } int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp, struct kernel_hwtstamp_config *config) { + unsigned int ptp_en_flags = aq_ptp_parse_rx_filters(config->rx_filter); struct aq_nic_s *aq_nic = aq_ptp->aq_nic; - const struct aq_hw_ops *hw_ops; int err = 0; - hw_ops = aq_nic->aq_hw_ops; - if (config->tx_type == HWTSTAMP_TX_ON || - config->rx_filter == HWTSTAMP_FILTER_PTP_V2_EVENT) { - aq_ptp_prepare_filters(aq_ptp); - if (hw_ops->hw_filter_l3l4_set) { - err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, - &aq_ptp->udp_filter); - } - if (!err && hw_ops->hw_filter_l2_set) { - err = hw_ops->hw_filter_l2_set(aq_nic->aq_hw, - &aq_ptp->eth_type_filter); - } - aq_utils_obj_set(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP); - } else { - aq_ptp->udp_filter.cmd &= ~HW_ATL_RX_ENABLE_FLTR_L3L4; - if (hw_ops->hw_filter_l3l4_set) { - err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, - &aq_ptp->udp_filter); - } - if (!err && hw_ops->hw_filter_l2_clear) { - err = hw_ops->hw_filter_l2_clear(aq_nic->aq_hw, - &aq_ptp->eth_type_filter); - } - aq_utils_obj_clear(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP); + mutex_lock(&aq_ptp->ptp_filter_lock); + if (aq_ptp->hwtstamp_config.rx_filter != config->rx_filter) { + err = aq_ptp_dpath_enable(aq_ptp, + ptp_en_flags, + aq_ptp->ptp_rx.idx); } + mutex_unlock(&aq_ptp->ptp_filter_lock); if (err) return -EREMOTEIO; - aq_ptp->hwtstamp_config = *config; + if (ptp_en_flags != AQ_HW_PTP_DISABLE) + aq_utils_obj_set(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP); + else + aq_utils_obj_clear(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP); + aq_ptp->hwtstamp_config = *config; return 0; } @@ -673,21 +884,23 @@ static int aq_ptp_poll(struct napi_struct *napi, int budget) was_cleaned = true; } - /* Processing HW_TIMESTAMP RX traffic */ - err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_receive(aq_nic->aq_hw, - &aq_ptp->hwts_rx); - if (err < 0) - goto err_exit; - - if (aq_ptp->hwts_rx.sw_head != aq_ptp->hwts_rx.hw_head) { - aq_ring_hwts_rx_clean(&aq_ptp->hwts_rx, aq_nic); - - err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_fill(aq_nic->aq_hw, - &aq_ptp->hwts_rx); + if (aq_ptp->a1_ptp) { + /* Processing HW_TIMESTAMP RX traffic */ + err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_receive(aq_nic->aq_hw, + &aq_ptp->hwts_rx); if (err < 0) goto err_exit; - was_cleaned = true; + if (aq_ptp->hwts_rx.sw_head != aq_ptp->hwts_rx.hw_head) { + aq_ring_hwts_rx_clean(&aq_ptp->hwts_rx, aq_nic); + + err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_fill(aq_nic->aq_hw, + &aq_ptp->hwts_rx); + if (err < 0) + goto err_exit; + + was_cleaned = true; + } } /* Processing PTP RX traffic */ @@ -818,7 +1031,7 @@ int aq_ptp_irq_alloc(struct aq_nic_s *aq_nic) return 0; if (pdev->msix_enabled || pdev->msi_enabled) { - err = request_irq(pci_irq_vector(pdev, aq_ptp->idx_vector), + err = request_irq(pci_irq_vector(pdev, aq_ptp->idx_ptp_vector), aq_ptp_isr, 0, aq_nic->ndev->name, aq_ptp); } else { err = -EINVAL; @@ -837,7 +1050,7 @@ void aq_ptp_irq_free(struct aq_nic_s *aq_nic) if (!aq_ptp) return; - free_irq(pci_irq_vector(pdev, aq_ptp->idx_vector), aq_ptp); + free_irq(pci_irq_vector(pdev, aq_ptp->idx_ptp_vector), aq_ptp); } int aq_ptp_ring_init(struct aq_nic_s *aq_nic) @@ -875,6 +1088,9 @@ int aq_ptp_ring_init(struct aq_nic_s *aq_nic) if (err < 0) goto err_rx_free; + if (aq_ptp->a2_ptp) + return 0; + err = aq_ring_init(&aq_ptp->hwts_rx, ATL_RING_RX); if (err < 0) goto err_rx_free; @@ -912,10 +1128,12 @@ int aq_ptp_ring_start(struct aq_nic_s *aq_nic) if (err < 0) goto err_exit; - err = aq_nic->aq_hw_ops->hw_ring_rx_start(aq_nic->aq_hw, - &aq_ptp->hwts_rx); - if (err < 0) - goto err_exit; + if (aq_ptp->a1_ptp) { + err = aq_nic->aq_hw_ops->hw_ring_rx_start(aq_nic->aq_hw, + &aq_ptp->hwts_rx); + if (err < 0) + goto err_exit; + } napi_enable(&aq_ptp->napi); @@ -933,7 +1151,9 @@ void aq_ptp_ring_stop(struct aq_nic_s *aq_nic) aq_nic->aq_hw_ops->hw_ring_tx_stop(aq_nic->aq_hw, &aq_ptp->ptp_tx); aq_nic->aq_hw_ops->hw_ring_rx_stop(aq_nic->aq_hw, &aq_ptp->ptp_rx); - aq_nic->aq_hw_ops->hw_ring_rx_stop(aq_nic->aq_hw, &aq_ptp->hwts_rx); + if (aq_ptp->a1_ptp) + aq_nic->aq_hw_ops->hw_ring_rx_stop(aq_nic->aq_hw, + &aq_ptp->hwts_rx); napi_disable(&aq_ptp->napi); } @@ -972,11 +1192,13 @@ int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) if (err) goto err_exit_ptp_tx; - err = aq_ring_hwts_rx_alloc(&aq_ptp->hwts_rx, aq_nic, PTP_HWST_RING_IDX, - aq_nic->aq_nic_cfg.rxds, - aq_nic->aq_nic_cfg.aq_hw_caps->rxd_size); - if (err) - goto err_exit_ptp_rx; + if (aq_ptp->a1_ptp) { + err = aq_ring_hwts_rx_alloc(&aq_ptp->hwts_rx, aq_nic, PTP_HWST_RING_IDX, + aq_nic->aq_nic_cfg.rxds, + aq_nic->aq_nic_cfg.aq_hw_caps->rxd_size); + if (err) + goto err_exit_ptp_rx; + } err = aq_ptp_skb_ring_init(&aq_ptp->skb_ring, aq_nic->aq_nic_cfg.rxds); if (err != 0) { @@ -984,7 +1206,7 @@ int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) goto err_exit_hwts_rx; } - aq_ptp->ptp_ring_param.vec_idx = aq_ptp->idx_vector; + aq_ptp->ptp_ring_param.vec_idx = aq_ptp->idx_ptp_vector; aq_ptp->ptp_ring_param.cpu = aq_ptp->ptp_ring_param.vec_idx + aq_nic_get_cfg(aq_nic)->aq_rss.base_cpu_number; cpumask_set_cpu(aq_ptp->ptp_ring_param.cpu, @@ -993,7 +1215,8 @@ int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) return 0; err_exit_hwts_rx: - aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); + if (aq_ptp->a1_ptp) + aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); err_exit_ptp_rx: aq_ring_free(&aq_ptp->ptp_rx); err_exit_ptp_tx: @@ -1011,7 +1234,8 @@ void aq_ptp_ring_free(struct aq_nic_s *aq_nic) aq_ring_free(&aq_ptp->ptp_tx); aq_ring_free(&aq_ptp->ptp_rx); - aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); + if (aq_ptp->a1_ptp) + aq_ring_hwts_rx_free(&aq_ptp->hwts_rx); aq_ptp_skb_ring_release(&aq_ptp->skb_ring); } @@ -1035,46 +1259,49 @@ static struct ptp_clock_info aq_ptp_clock = { .pin_config = NULL, }; -#define ptp_offset_init(__idx, __mbps, __egress, __ingress) do { \ - ptp_offset[__idx].mbps = (__mbps); \ - ptp_offset[__idx].egress = (__egress); \ - ptp_offset[__idx].ingress = (__ingress); } \ - while (0) +static void ptp_offset_init(struct aq_ptp_s *aq_ptp, int idx, + unsigned int mbps, int egress, int ingress) +{ + aq_ptp->ptp_offset[idx].mbps = mbps; + aq_ptp->ptp_offset[idx].egress = egress; + aq_ptp->ptp_offset[idx].ingress = ingress; +} -static void aq_ptp_offset_init_from_fw(const struct hw_atl_ptp_offset *offsets) +static void aq_ptp_offset_init_from_fw(struct aq_ptp_s *aq_ptp, + const struct hw_atl_ptp_offset *offsets) { int i; /* Load offsets for PTP */ - for (i = 0; i < ARRAY_SIZE(ptp_offset); i++) { + for (i = 0; i < ARRAY_SIZE(aq_ptp->ptp_offset); i++) { switch (i) { /* 100M */ case ptp_offset_idx_100: - ptp_offset_init(i, 100, + ptp_offset_init(aq_ptp, i, 100, offsets->egress_100, offsets->ingress_100); break; /* 1G */ case ptp_offset_idx_1000: - ptp_offset_init(i, 1000, + ptp_offset_init(aq_ptp, i, 1000, offsets->egress_1000, offsets->ingress_1000); break; /* 2.5G */ case ptp_offset_idx_2500: - ptp_offset_init(i, 2500, + ptp_offset_init(aq_ptp, i, 2500, offsets->egress_2500, offsets->ingress_2500); break; /* 5G */ case ptp_offset_idx_5000: - ptp_offset_init(i, 5000, + ptp_offset_init(aq_ptp, i, 5000, offsets->egress_5000, offsets->ingress_5000); break; /* 10G */ case ptp_offset_idx_10000: - ptp_offset_init(i, 10000, + ptp_offset_init(aq_ptp, i, 10000, offsets->egress_10000, offsets->ingress_10000); break; @@ -1082,11 +1309,12 @@ static void aq_ptp_offset_init_from_fw(const struct hw_atl_ptp_offset *offsets) } } -static void aq_ptp_offset_init(const struct hw_atl_ptp_offset *offsets) +static void aq_ptp_offset_init(struct aq_ptp_s *aq_ptp, + const struct hw_atl_ptp_offset *offsets) { - memset(ptp_offset, 0, sizeof(ptp_offset)); + memset(aq_ptp->ptp_offset, 0, sizeof(aq_ptp->ptp_offset)); - aq_ptp_offset_init_from_fw(offsets); + aq_ptp_offset_init_from_fw(aq_ptp, offsets); } static void aq_ptp_gpio_init(struct ptp_clock_info *info, @@ -1139,26 +1367,46 @@ static void aq_ptp_gpio_init(struct ptp_clock_info *info, sizeof(struct ptp_pin_desc) * info->n_pins); } -void aq_ptp_clock_init(struct aq_nic_s *aq_nic) +void aq_ptp_clock_init(struct aq_nic_s *aq_nic, enum aq_ptp_state state) { struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; - struct timespec64 ts; - ktime_get_real_ts64(&ts); - aq_ptp_settime(&aq_ptp->ptp_info, &ts); + if (!aq_ptp) + return; + + if (aq_ptp->a1_ptp || state == AQ_PTP_FIRST_INIT) { + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + aq_ptp_settime(&aq_ptp->ptp_info, &ts); + } + + if (!aq_ptp->a1_ptp && state != AQ_PTP_FIRST_INIT) { + mutex_lock(&aq_ptp->ptp_filter_lock); + unsigned int ptp_en_flags = + aq_ptp_parse_rx_filters(state == AQ_PTP_LINK_UP ? + aq_ptp->hwtstamp_config.rx_filter : + HWTSTAMP_FILTER_NONE); + if (aq_ptp_dpath_enable(aq_ptp, ptp_en_flags, aq_ptp->ptp_rx.idx)) + netdev_warn(aq_nic->ndev, + "PTP RX filter restore failed in link change\n"); + mutex_unlock(&aq_ptp->ptp_filter_lock); + } } static void aq_ptp_poll_sync_work_cb(struct work_struct *w); -int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) +int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_ptp_vec) { bool a1_ptp = ATL_HW_IS_CHIP_FEATURE(aq_nic->aq_hw, ATLANTIC); + bool a2_ptp = ATL_HW_IS_CHIP_FEATURE(aq_nic->aq_hw, ANTIGUA); struct hw_atl_utils_mbox mbox; struct ptp_clock *clock; - struct aq_ptp_s *aq_ptp; + struct aq_ptp_s *aq_ptp = NULL; int err = 0; + int i; - if (!a1_ptp) { + if (!a1_ptp && !a2_ptp) { aq_nic->aq_ptp = NULL; return 0; } @@ -1168,20 +1416,44 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) return 0; } - if (!aq_nic->aq_fw_ops->enable_ptp) { + if (a1_ptp) { + if (!aq_nic->aq_fw_ops->enable_ptp) { + aq_nic->aq_ptp = NULL; + return 0; + } + } + + /* PTP requires at least 1 free irq vector for itself */ + if (aq_nic->irqvecs <= AQ_HW_PTP_IRQS) { + netdev_warn(aq_nic->ndev, + "Disabling PTP due to insufficient number of available IRQ vectors.\n"); aq_nic->aq_ptp = NULL; return 0; } - hw_atl_utils_mpi_read_stats(aq_nic->aq_hw, &mbox); + if (a1_ptp) { + hw_atl_utils_mpi_read_stats(aq_nic->aq_hw, &mbox); + if (!(mbox.info.caps_ex & BIT(CAPS_EX_PHY_PTP_EN))) { + aq_nic->aq_ptp = NULL; + return 0; + } + } else { + memset(&mbox, 0, sizeof(mbox)); - if (!(mbox.info.caps_ex & BIT(CAPS_EX_PHY_PTP_EN))) { - aq_nic->aq_ptp = NULL; - return 0; + if (a2_ptp) { + mbox.info.ptp_offset.ingress_100 = HW_ATL2_PTP_OFFSET_INGRESS_100; + mbox.info.ptp_offset.egress_100 = HW_ATL2_PTP_OFFSET_EGRESS_100; + mbox.info.ptp_offset.ingress_1000 = HW_ATL2_PTP_OFFSET_INGRESS_1000; + mbox.info.ptp_offset.egress_1000 = HW_ATL2_PTP_OFFSET_EGRESS_1000; + mbox.info.ptp_offset.ingress_2500 = HW_ATL2_PTP_OFFSET_INGRESS_2500; + mbox.info.ptp_offset.egress_2500 = HW_ATL2_PTP_OFFSET_EGRESS_2500; + mbox.info.ptp_offset.ingress_5000 = HW_ATL2_PTP_OFFSET_INGRESS_5000; + mbox.info.ptp_offset.egress_5000 = HW_ATL2_PTP_OFFSET_EGRESS_5000; + mbox.info.ptp_offset.ingress_10000 = HW_ATL2_PTP_OFFSET_INGRESS_10000; + mbox.info.ptp_offset.egress_10000 = HW_ATL2_PTP_OFFSET_EGRESS_10000; + } } - aq_ptp_offset_init(&mbox.info.ptp_offset); - aq_ptp = kzalloc_obj(*aq_ptp); if (!aq_ptp) { err = -ENOMEM; @@ -1190,10 +1462,13 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) aq_ptp->aq_nic = aq_nic; aq_ptp->a1_ptp = a1_ptp; + aq_ptp->a2_ptp = a2_ptp; spin_lock_init(&aq_ptp->ptp_lock); spin_lock_init(&aq_ptp->ptp_ring_lock); + mutex_init(&aq_ptp->ptp_filter_lock); + aq_ptp_offset_init(aq_ptp, &mbox.info.ptp_offset); aq_ptp->ptp_info = aq_ptp_clock; aq_ptp_gpio_init(&aq_ptp->ptp_info, &mbox.info); clock = ptp_clock_register(&aq_ptp->ptp_info, &aq_nic->ndev->dev); @@ -1210,22 +1485,34 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) netif_napi_add(aq_nic_get_ndev(aq_nic), &aq_ptp->napi, aq_ptp_poll); - aq_ptp->idx_vector = idx_vec; + aq_ptp->idx_ptp_vector = idx_ptp_vec; aq_nic->aq_ptp = aq_ptp; /* enable ptp counter */ + aq_ptp->ptp_clock_sel = ATL_TSG_CLOCK_SEL_0; aq_utils_obj_set(&aq_nic->aq_hw->flags, AQ_HW_PTP_AVAILABLE); - mutex_lock(&aq_nic->fwreq_mutex); - aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 1); - aq_ptp_clock_init(aq_nic); - mutex_unlock(&aq_nic->fwreq_mutex); + if (a1_ptp) { + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 1); + mutex_unlock(&aq_nic->fwreq_mutex); + } + if (a2_ptp) + aq_nic->aq_hw_ops->enable_ptp(aq_nic->aq_hw, aq_ptp->ptp_clock_sel, 1); INIT_DELAYED_WORK(&aq_ptp->poll_sync, &aq_ptp_poll_sync_work_cb); aq_ptp->eth_type_filter.location = - aq_nic_reserve_filter(aq_nic, aq_rx_filter_ethertype); - aq_ptp->udp_filter.location = + aq_nic_reserve_filter(aq_nic, aq_rx_filter_ethertype); + + for (i = 0; i < PTP_UDP_FILTERS_CNT; i++) { + aq_ptp->udp_filter[i].location = aq_nic_reserve_filter(aq_nic, aq_rx_filter_l3l4); + } + + aq_ptp_clock_init(aq_nic, AQ_PTP_FIRST_INIT); + netdev_info(aq_nic->ndev, + "Enable PTP Support. %d GPIO(s)\n", + aq_ptp->ptp_info.n_pins); return 0; @@ -1244,31 +1531,47 @@ void aq_ptp_unregister(struct aq_nic_s *aq_nic) if (!aq_ptp) return; - ptp_clock_unregister(aq_ptp->ptp_clock); + if (aq_ptp->ptp_clock) { + ptp_clock_unregister(aq_ptp->ptp_clock); + aq_ptp->ptp_clock = NULL; + } } void aq_ptp_free(struct aq_nic_s *aq_nic) { struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + int i; if (!aq_ptp) return; + cancel_delayed_work_sync(&aq_ptp->poll_sync); + + /* disable ptp */ + if (aq_ptp->a1_ptp) { + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 0); + mutex_unlock(&aq_nic->fwreq_mutex); + } + + if (aq_ptp->a2_ptp) + aq_nic->aq_hw_ops->enable_ptp(aq_nic->aq_hw, + aq_ptp->ptp_clock_sel, 0); + aq_nic_release_filter(aq_nic, aq_rx_filter_ethertype, aq_ptp->eth_type_filter.location); - aq_nic_release_filter(aq_nic, aq_rx_filter_l3l4, - aq_ptp->udp_filter.location); - cancel_delayed_work_sync(&aq_ptp->poll_sync); - /* disable ptp */ - mutex_lock(&aq_nic->fwreq_mutex); - aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 0); - mutex_unlock(&aq_nic->fwreq_mutex); + for (i = 0; i < PTP_UDP_FILTERS_CNT; i++) + aq_nic_release_filter(aq_nic, aq_rx_filter_l3l4, + aq_ptp->udp_filter[i].location); + mutex_destroy(&aq_ptp->ptp_filter_lock); kfree(aq_ptp->ptp_info.pin_config); + aq_ptp->ptp_info.pin_config = NULL; netif_napi_del(&aq_ptp->napi); - kfree(aq_ptp); + aq_utils_obj_clear(&aq_nic->aq_hw->flags, AQ_HW_PTP_AVAILABLE); aq_nic->aq_ptp = NULL; + kfree(aq_ptp); } struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h index 5e643ec7cc06..96cbfa59ac75 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h @@ -14,6 +14,12 @@ #include "aq_ring.h" +enum aq_ptp_state { + AQ_PTP_NO_LINK = 0, + AQ_PTP_FIRST_INIT = 1, + AQ_PTP_LINK_UP = 2, +}; + #define PTP_8TC_RING_IDX 8 #define PTP_4TC_RING_IDX 16 #define PTP_HWST_RING_IDX 31 @@ -32,7 +38,7 @@ static inline unsigned int aq_ptp_ring_idx(const enum aq_tc_mode tc_mode) #if IS_REACHABLE(CONFIG_PTP_1588_CLOCK) /* Common functions */ -int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec); +int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_ptp_vec); void aq_ptp_unregister(struct aq_nic_s *aq_nic); void aq_ptp_free(struct aq_nic_s *aq_nic); @@ -52,7 +58,8 @@ void aq_ptp_service_task(struct aq_nic_s *aq_nic); void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps); -void aq_ptp_clock_init(struct aq_nic_s *aq_nic); +void aq_ptp_clock_init(struct aq_nic_s *aq_nic, enum aq_ptp_state state); +void aq_ptp_tx_skb_drop_head(struct aq_nic_s *aq_nic); /* Traffic processing functions */ int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb); @@ -80,7 +87,7 @@ u64 *aq_ptp_get_stats(struct aq_nic_s *aq_nic, u64 *data); #else -static inline int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) +static inline int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_ptp_vec) { return 0; } @@ -122,7 +129,9 @@ static inline void aq_ptp_ring_deinit(struct aq_nic_s *aq_nic) {} static inline void aq_ptp_service_task(struct aq_nic_s *aq_nic) {} static inline void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps) {} -static inline void aq_ptp_clock_init(struct aq_nic_s *aq_nic) {} +static inline void aq_ptp_clock_init(struct aq_nic_s *aq_nic, + enum aq_ptp_state state) {} +static inline void aq_ptp_tx_skb_drop_head(struct aq_nic_s *aq_nic) {} static inline int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb) { return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index e270327e47fd..8ff07de2bd52 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -311,6 +311,30 @@ bool aq_ring_tx_clean(struct aq_ring_s *self) if (likely(!buff->is_eop)) goto out; + if (unlikely(buff->request_ts) && + self->aq_nic->aq_hw_ops->hw_ring_tx_ptp_get_ts) { + u64 ts = self->aq_nic->aq_hw_ops->hw_ring_tx_ptp_get_ts(self); + + if (!ts) { + if (time_after(jiffies, + self->ptp_ts_deadline)) { + /* Timeout: drain skb_ring head to + * keep in sync with buff_ring + */ + aq_ptp_tx_skb_drop_head(self->aq_nic); + buff->request_ts = 0; + dev_kfree_skb_any(buff->skb); + buff->skb = NULL; + goto out; + } else { + buff->is_mapped = 0; + buff->pa = 0U; + break; + } + } + + aq_ptp_tx_hwtstamp(self->aq_nic, ts); + } if (buff->skb) { u64_stats_update_begin(&self->stats.tx.syncp); ++self->stats.tx.packets; @@ -570,7 +594,7 @@ static int __aq_ring_rx_clean(struct aq_ring_s *self, struct napi_struct *napi, self->hw_head); if (unlikely(!is_rsc_completed) || - frag_cnt > MAX_SKB_FRAGS) { + frag_cnt > MAX_SKB_FRAGS) { err = 0; goto err_exit; } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index d627ace850ff..a70b880ada67 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -85,7 +85,9 @@ struct __packed aq_ring_buff_s { u32 is_error:1; u32 is_vlan:1; u32 is_lro:1; - u32 rsvd3:3; + u32 request_ts:1; + u32 clk_sel:1; + u32 rsvd3:1; u16 eop_index; u16 rsvd4; }; @@ -152,6 +154,7 @@ struct aq_ring_s { struct bpf_prog *xdp_prog; enum atl_ring_type ring_type; struct xdp_rxq_info xdp_rxq; + unsigned long ptp_ts_deadline; }; struct aq_ring_param_s { diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c index c7895bfb2ecf..6c25ad264b19 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c @@ -736,6 +736,15 @@ int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, struct aq_ring_s *ring, txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_WB; is_gso = false; is_vlan = false; + + if (ATL_HW_IS_CHIP_FEATURE(self, ANTIGUA) && + unlikely(buff->request_ts)) { + txd->ctl |= AQ_HW_TXD_CTL_TS_EN; + if (buff->clk_sel != ATL_TSG_CLOCK_SEL_1) + txd->ctl |= AQ_HW_TXD_CTL_TS_TSG0; + /* The only DD+TS is required */ + txd->ctl &= ~HW_ATL_B0_TXD_CTL_CMD_WB; + } } } ring->sw_tail = aq_ring_next_dx(ring, ring->sw_tail); @@ -1323,8 +1332,8 @@ static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb) return self->aq_fw_ops->send_fw_request(self, &fwreq, size); } -static int hw_atl_b0_gpio_pulse(struct aq_hw_s *self, u32 index, - u64 start, u32 period) +static int hw_atl_b0_gpio_pulse(struct aq_hw_s *self, u32 index, u32 clk_sel, + u64 start, u32 period, u32 hightime) { struct hw_fw_request_iface fwreq; size_t size; @@ -1342,7 +1351,7 @@ static int hw_atl_b0_gpio_pulse(struct aq_hw_s *self, u32 index, } static int hw_atl_b0_extts_gpio_enable(struct aq_hw_s *self, u32 index, - u32 enable) + u32 channel, int enable) { /* Enable/disable Sync1588 GPIO Timestamping */ aq_phy_write_reg(self, MDIO_MMD_PCS, 0xc611, enable ? 0x71 : 0); diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c index 0ce9caae8799..25dd7150aaaf 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c @@ -7,10 +7,12 @@ #include "aq_hw_utils.h" #include "aq_ring.h" #include "aq_nic.h" +#include "aq_ptp.h" #include "hw_atl/hw_atl_b0.h" #include "hw_atl/hw_atl_utils.h" #include "hw_atl/hw_atl_llh.h" #include "hw_atl/hw_atl_llh_internal.h" +#include "hw_atl2.h" #include "hw_atl2_utils.h" #include "hw_atl2_llh.h" #include "hw_atl2_internal.h" @@ -19,6 +21,15 @@ static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location, u32 tag, u32 mask, u32 action); +static void hw_atl2_enable_ptp(struct aq_hw_s *self, + unsigned int param, int enable); +static int hw_atl2_hw_tx_ptp_ring_init(struct aq_hw_s *self, + struct aq_ring_s *aq_ring); +static int hw_atl2_hw_rx_ptp_ring_init(struct aq_hw_s *self, + struct aq_ring_s *aq_ring); +static void aq_get_ptp_ts(struct aq_hw_s *self, u64 *stamp); +static int hw_atl2_adj_clock_freq(struct aq_hw_s *self, s32 ppb); + #define DEFAULT_BOARD_BASIC_CAPABILITIES \ .is_64_dma = true, \ .op64bit = true, \ @@ -86,6 +97,38 @@ const struct aq_hw_caps_s hw_atl2_caps_aqc116c = { AQ_NIC_RATE_10M, }; +/* Find tag with the same action or new free tag + * top - top inclusive tag value + * action - action for ActionResolverTable + */ +static int hw_atl2_filter_tag_get(struct hw_atl2_tag_policy *tags, + int top, u16 action) +{ + int i; + + for (i = 1; i <= top; i++) + if (tags[i].usage > 0 && tags[i].action == action) { + tags[i].usage++; + return i; + } + + for (i = 1; i <= top; i++) + if (tags[i].usage == 0) { + tags[i].usage = 1; + tags[i].action = action; + return i; + } + + return -ENOSPC; +} + +static void hw_atl2_filter_tag_put(struct hw_atl2_tag_policy *tags, + int tag) +{ + if (tags[tag].usage > 0) + tags[tag].usage--; +} + static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self) { return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL2_FW_SM_ACT_RSLVR); @@ -94,13 +137,30 @@ static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self) static int hw_atl2_hw_reset(struct aq_hw_s *self) { struct hw_atl2_priv *priv = self->priv; + s8 clk_sel; int err; + int i; err = hw_atl2_utils_soft_reset(self); if (err) return err; - memset(priv, 0, sizeof(*priv)); + memset(&priv->last_stats, 0, sizeof(priv->last_stats)); + memset(priv->l3_v4_filters, 0, sizeof(priv->l3_v4_filters)); + memset(priv->l3_v6_filters, 0, sizeof(priv->l3_v6_filters)); + memset(priv->l4_filters, 0, sizeof(priv->l4_filters)); + memset(priv->etype_policy, 0, sizeof(priv->etype_policy)); + for (i = 0; i < HW_ATL2_RPF_L3L4_FILTERS; i++) { + priv->l3l4_filters[i].l3_index = -1; + priv->l3l4_filters[i].l4_index = -1; + } + + clk_sel = READ_ONCE(self->clk_select); + if (clk_sel != -1) + hw_atl2_enable_ptp(self, + clk_sel, + aq_utils_obj_test(&self->flags, AQ_HW_PTP_AVAILABLE) ? + 1 : 0); self->aq_fw_ops->set_state(self, MPI_RESET); @@ -109,6 +169,24 @@ static int hw_atl2_hw_reset(struct aq_hw_s *self) return err; } +static int hw_atl2_tc_ptp_set(struct aq_hw_s *self) +{ + /* Init TC2 for PTP_TX */ + hw_atl_tpb_tx_pkt_buff_size_per_tc_set(self, HW_ATL2_PTP_TXBUF_SIZE, + AQ_HW_PTP_TC); + + /* Init TC2 for PTP_RX */ + hw_atl_rpb_rx_pkt_buff_size_per_tc_set(self, HW_ATL2_PTP_RXBUF_SIZE, + AQ_HW_PTP_TC); + + /* No flow control for PTP */ + hw_atl_rpb_rx_xoff_en_per_tc_set(self, 0U, AQ_HW_PTP_TC); + + hw_atl2_tpb_tps_highest_priority_tc_set(self, AQ_HW_PTP_TC); + + return aq_hw_err_from_flags(self); +} + static int hw_atl2_hw_queue_to_tc_map_set(struct aq_hw_s *self) { struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; @@ -167,6 +245,11 @@ static int hw_atl2_hw_qos_set(struct aq_hw_s *self) unsigned int prio = 0U; u32 tc = 0U; + if (cfg->is_ptp) { + tx_buff_size -= HW_ATL2_PTP_TXBUF_SIZE; + rx_buff_size -= HW_ATL2_PTP_RXBUF_SIZE; + } + /* TPS Descriptor rate init */ hw_atl_tps_tx_pkt_shed_desc_rate_curr_time_res_set(self, 0x0U); hw_atl_tps_tx_pkt_shed_desc_rate_lim_set(self, 0xA); @@ -200,6 +283,9 @@ static int hw_atl2_hw_qos_set(struct aq_hw_s *self) hw_atl_b0_set_fc(self, self->aq_nic_cfg->fc.req, tc); } + if (cfg->is_ptp) + hw_atl2_tc_ptp_set(self); + /* QoS 802.1p priority -> TC mapping */ for (prio = 0; prio < 8; ++prio) hw_atl_rpf_rpb_user_priority_tc_map_set(self, prio, @@ -217,8 +303,9 @@ static int hw_atl2_hw_rss_set(struct aq_hw_s *self, u8 *indirection_table = rss_params->indirection_table; const u32 num_tcs = aq_hw_num_tcs(self); u32 rpf_redir2_enable; - int tc; - int i; + u32 queue; + u32 tc; + u32 i; rpf_redir2_enable = num_tcs > 4 ? 1 : 0; @@ -226,10 +313,9 @@ static int hw_atl2_hw_rss_set(struct aq_hw_s *self, for (i = HW_ATL2_RSS_REDIRECTION_MAX; i--;) { for (tc = 0; tc != num_tcs; tc++) { - hw_atl2_new_rpf_rss_redir_set(self, tc, i, - tc * - aq_hw_q_per_tc(self) + - indirection_table[i]); + queue = tc * aq_hw_q_per_tc(self) + + indirection_table[i]; + hw_atl2_new_rpf_rss_redir_set(self, tc, i, queue); } } @@ -373,13 +459,27 @@ static int hw_atl2_hw_init_tx_path(struct aq_hw_s *self) hw_atl2_tpb_tx_buf_clk_gate_en_set(self, 0U); + if (hw_atl2_phi_ext_tag_get(self)) { + hw_atl2_tdm_tx_data_read_req_limit_set(self, 0x7F); + hw_atl2_tdm_tx_desc_read_req_limit_set(self, 0x0F); + } + return aq_hw_err_from_flags(self); } +/* Initialise new rx filters + * L2 promisc OFF + * VLAN promisc OFF + * + * User priority to TC + */ static void hw_atl2_hw_init_new_rx_filters(struct aq_hw_s *self) { u8 *prio_tc_map = self->aq_nic_cfg->prio_tc_map; struct hw_atl2_priv *priv = self->priv; + u32 art_first_sec, art_last_sec; + u32 art_sections; + u32 art_mask; u16 action; u8 index; int i; @@ -394,9 +494,20 @@ static void hw_atl2_hw_init_new_rx_filters(struct aq_hw_s *self) * REC entry is used for further processing. If multiple entries match, * the lowest REC entry, Action field will be selected. */ - hw_atl2_rpf_act_rslvr_section_en_set(self, 0xFFFF); + art_last_sec = min_t(u32, + priv->art_base_index / 8 + priv->art_count / 8, + 16U); + art_first_sec = min_t(u32, priv->art_base_index / 8, 16U); + art_mask = (BIT(art_last_sec) - 1) - (BIT(art_first_sec) - 1); + if (!art_mask) { + /* ART base index is out of range, enabling all sections */ + art_mask = 0xFFFF; + } + art_sections = hw_atl2_rpf_act_rslvr_section_en_get(self) | art_mask; + hw_atl2_rpf_act_rslvr_section_en_set(self, art_sections); + hw_atl2_rpf_l3_v6_v4_select_set(self, 1); hw_atl2_rpfl2_uc_flr_tag_set(self, HW_ATL2_RPF_TAG_BASE_UC, - HW_ATL2_MAC_UC); + priv->l2_filters_base_index); hw_atl2_rpfl2_bc_flr_tag_set(self, HW_ATL2_RPF_TAG_BASE_UC); /* FW reserves the beginning of ART, thus all driver entries must @@ -484,6 +595,7 @@ static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location, static int hw_atl2_hw_init_rx_path(struct aq_hw_s *self) { struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; + struct hw_atl2_priv *priv = self->priv; int i; /* Rx TC/RSS number config */ @@ -499,7 +611,9 @@ static int hw_atl2_hw_init_rx_path(struct aq_hw_s *self) /* Multicast filters */ for (i = HW_ATL2_MAC_MAX; i--;) { - hw_atl_rpfl2_uc_flr_en_set(self, (i == 0U) ? 1U : 0U, i); + hw_atl_rpfl2_uc_flr_en_set(self, + (i == priv->l2_filters_base_index) ? + 1U : 0U, i); hw_atl_rpfl2unicast_flr_act_set(self, 1U, i); } @@ -530,6 +644,35 @@ static int hw_atl2_hw_init_rx_path(struct aq_hw_s *self) return aq_hw_err_from_flags(self); } +static int hw_atl2_hw_mac_addr_set(struct aq_hw_s *self, const u8 *mac_addr) +{ + struct hw_atl2_priv *priv = self->priv; + u32 location = priv->l2_filters_base_index; + unsigned int h; + unsigned int l; + int err; + + if (!mac_addr) { + err = -EINVAL; + goto err_exit; + } + h = (mac_addr[0] << 8) | (mac_addr[1]); + l = (mac_addr[2] << 24) | (mac_addr[3] << 16) | + (mac_addr[4] << 8) | mac_addr[5]; + + hw_atl_rpfl2_uc_flr_en_set(self, 0U, location); + hw_atl_rpfl2unicast_dest_addresslsw_set(self, l, location); + hw_atl_rpfl2unicast_dest_addressmsw_set(self, h, location); + hw_atl_rpfl2unicast_flr_act_set(self, 1U, location); + hw_atl2_rpfl2_uc_flr_tag_set(self, HW_ATL2_RPF_TAG_BASE_UC, location); + hw_atl_rpfl2_uc_flr_en_set(self, 1U, location); + + err = aq_hw_err_from_flags(self); + +err_exit: + return err; +} + static int hw_atl2_hw_init(struct aq_hw_s *self, const u8 *mac_addr) { static u32 aq_hw_atl2_igcr_table_[4][2] = { @@ -540,23 +683,14 @@ static int hw_atl2_hw_init(struct aq_hw_s *self, const u8 *mac_addr) }; struct aq_nic_cfg_s *aq_nic_cfg = self->aq_nic_cfg; - struct hw_atl2_priv *priv = self->priv; - u8 base_index, count; int err; - err = hw_atl2_utils_get_action_resolve_table_caps(self, &base_index, - &count); - if (err) - return err; - - priv->art_base_index = 8 * base_index; - hw_atl2_init_launchtime(self); hw_atl2_hw_init_tx_path(self); hw_atl2_hw_init_rx_path(self); - hw_atl_b0_hw_mac_addr_set(self, mac_addr); + hw_atl2_hw_mac_addr_set(self, mac_addr); self->aq_fw_ops->set_link_speed(self, aq_nic_cfg->link_speed_msk); self->aq_fw_ops->set_state(self, MPI_INIT); @@ -600,14 +734,24 @@ static int hw_atl2_hw_ring_rx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring, struct aq_ring_param_s *aq_ring_param) { - return hw_atl_b0_hw_ring_rx_init(self, aq_ring, aq_ring_param); + int res = hw_atl_b0_hw_ring_rx_init(self, aq_ring, aq_ring_param); + + if (!res && aq_ptp_ring(aq_ring->aq_nic, aq_ring)) + res = hw_atl2_hw_rx_ptp_ring_init(self, aq_ring); + + return res; } static int hw_atl2_hw_ring_tx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring, struct aq_ring_param_s *aq_ring_param) { - return hw_atl_b0_hw_ring_tx_init(self, aq_ring, aq_ring_param); + int res = hw_atl_b0_hw_ring_tx_init(self, aq_ring, aq_ring_param); + + if (!res && aq_ptp_ring(aq_ring->aq_nic, aq_ring)) + res = hw_atl2_hw_tx_ptp_ring_init(self, aq_ring); + + return res; } #define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U) @@ -628,6 +772,8 @@ static int hw_atl2_hw_multicast_list_set(struct aq_hw_s *self, [ETH_ALEN], u32 count) { + struct hw_atl2_priv *priv = self->priv; + u32 base_location = priv->l2_filters_base_index + 1; struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; int err = 0; @@ -643,18 +789,18 @@ static int hw_atl2_hw_multicast_list_set(struct aq_hw_s *self, u32 l = (ar_mac[i][2] << 24) | (ar_mac[i][3] << 16) | (ar_mac[i][4] << 8) | ar_mac[i][5]; - hw_atl_rpfl2_uc_flr_en_set(self, 0U, HW_ATL2_MAC_MIN + i); + hw_atl_rpfl2_uc_flr_en_set(self, 0U, base_location + i); hw_atl_rpfl2unicast_dest_addresslsw_set(self, l, - HW_ATL2_MAC_MIN + i); + base_location + i); hw_atl_rpfl2unicast_dest_addressmsw_set(self, h, - HW_ATL2_MAC_MIN + i); + base_location + i); - hw_atl2_rpfl2_uc_flr_tag_set(self, 1, HW_ATL2_MAC_MIN + i); + hw_atl2_rpfl2_uc_flr_tag_set(self, 1, base_location + i); hw_atl_rpfl2_uc_flr_en_set(self, (cfg->is_mc_list_enabled), - HW_ATL2_MAC_MIN + i); + base_location + i); } err = aq_hw_err_from_flags(self); @@ -767,6 +913,640 @@ static struct aq_stats_s *hw_atl2_utils_get_hw_stats(struct aq_hw_s *self) return &self->curr_stats; } +static void hw_atl2_enable_ptp(struct aq_hw_s *self, + unsigned int param, int enable) +{ + s8 sel = (s8)param; + + WRITE_ONCE(self->clk_select, sel); + /* enable tsg counter */ + hw_atl2_tsg_clock_reset(self, sel); + hw_atl2_tsg_clock_en(self, !sel, enable); + hw_atl2_tsg_clock_en(self, sel, enable); + + if (enable) + hw_atl2_adj_clock_freq(self, 0); + + hw_atl2_tpb_tps_highest_priority_tc_enable_set(self, enable); +} + +static void aq_get_ptp_ts(struct aq_hw_s *self, u64 *stamp) +{ + s8 clk_sel = READ_ONCE(self->clk_select); + + if (clk_sel < 0) { + if (stamp) + *stamp = 0; + return; + } + if (stamp) + *stamp = hw_atl2_tsg_clock_read(self, clk_sel); +} + +static u64 hw_atl2_hw_ring_tx_ptp_get_ts(struct aq_ring_s *ring) +{ + struct hw_atl2_txts_s *txts; + u32 ctrl; + + txts = (struct hw_atl2_txts_s *)&ring->dx_ring[ring->sw_head * + HW_ATL2_TXD_SIZE]; + /* DD + TS_VALID */ + ctrl = le32_to_cpu(READ_ONCE(txts->ctrl)); + if ((ctrl & HW_ATL2_TXTS_DD) && (ctrl & HW_ATL2_TXTS_TS_VALID)) { + dma_rmb(); + return le64_to_cpu(txts->ts); + } + + return 0; +} + +static u16 hw_atl2_hw_rx_extract_ts(struct aq_hw_s *self, u8 *p, + unsigned int len, u64 *timestamp) +{ + unsigned int offset = HW_ATL2_RX_TS_SIZE; + __le64 ts; + u8 *ptr; + + if (len <= offset || !timestamp) + return 0; + + ptr = p + (len - offset); + memcpy(&ts, ptr, sizeof(ts)); + *timestamp = le64_to_cpu(ts); + + return HW_ATL2_RX_TS_SIZE; +} + +static int hw_atl2_adj_sys_clock(struct aq_hw_s *self, s64 delta) +{ + s8 clk_sel = READ_ONCE(self->clk_select); + + if (clk_sel < 0) + return -ENODEV; + if (delta >= 0) + hw_atl2_tsg_clock_add(self, clk_sel, (u64)delta); + else + hw_atl2_tsg_clock_sub(self, clk_sel, (u64)(-delta)); + + return 0; +} + +static int hw_atl2_adj_clock_freq(struct aq_hw_s *self, s32 ppb) +{ + u32 freq = AQ2_HW_PTP_COUNTER_HZ; + u64 divisor = 0, base_ns; + u32 nsi_frac = 0, nsi; + u32 nsi_rem; + s8 clk_sel; + + base_ns = div_u64((u64)((s64)ppb + NSEC_PER_SEC) * NSEC_PER_SEC, freq); + nsi = (u32)div_u64_rem(base_ns, NSEC_PER_SEC, &nsi_rem); + if (nsi_rem != 0) { + divisor = div_u64(mul_u32_u32(NSEC_PER_SEC, NSEC_PER_SEC), + nsi_rem); + nsi_frac = (u32)div64_u64(AQ_FRAC_PER_NS * NSEC_PER_SEC, + divisor); + } + + clk_sel = READ_ONCE(self->clk_select); + if (clk_sel < 0) + return -ENODEV; + hw_atl2_tsg_clock_increment_set(self, clk_sel, nsi, nsi_frac); + + return 0; +} + +static int hw_atl2_hw_tx_ptp_ring_init(struct aq_hw_s *self, + struct aq_ring_s *aq_ring) +{ + hw_atl2_tdm_tx_desc_timestamp_writeback_en_set(self, true, + aq_ring->idx); + hw_atl2_tdm_tx_desc_timestamp_en_set(self, true, aq_ring->idx); + hw_atl2_tdm_tx_desc_avb_en_set(self, true, aq_ring->idx); + + return aq_hw_err_from_flags(self); +} + +static int hw_atl2_hw_rx_ptp_ring_init(struct aq_hw_s *self, + struct aq_ring_s *aq_ring) +{ + s8 clk_sel = READ_ONCE(self->clk_select); + + hw_atl2_rpf_rx_desc_timestamp_req_set(self, + clk_sel == ATL_TSG_CLOCK_SEL_1 ? 2 : 1, + aq_ring->idx); + return aq_hw_err_from_flags(self); +} + +static u32 hw_atl2_hw_get_clk_sel(struct aq_hw_s *self) +{ + return READ_ONCE(self->clk_select); +} + +static int hw_atl2_gpio_pulse(struct aq_hw_s *self, u32 index, u32 clk_sel, + u64 start, u32 period, u32 hightime) +{ + u32 mode; + + if (index != 1 && index != 3) + return -EINVAL; + + if (start == 0) + mode = HW_ATL2_GPIO_PIN_SPEC_MODE_GPIO; + else if (clk_sel == ATL_TSG_CLOCK_SEL_0) + mode = HW_ATL2_GPIO_PIN_SPEC_MODE_TSG0_EVENT_OUTPUT; + else + mode = HW_ATL2_GPIO_PIN_SPEC_MODE_TSG1_EVENT_OUTPUT; + + hw_atl2_gpio_special_mode_set(self, mode, index); + hw_atl2_tsg_ptp_gpio_gen_pulse(self, clk_sel, start, period, hightime); + + return 0; +} + +static bool hw_atl2_rxf_l3_is_equal(struct hw_atl2_l3_filter *f1, + struct hw_atl2_l3_filter *f2) +{ + if (f1->cmd != f2->cmd) + return false; + + if (f1->cmd & HW_ATL2_RPF_L3_CMD_SA_EN) + if (f1->srcip[0] != f2->srcip[0]) + return false; + + if (f1->cmd & HW_ATL2_RPF_L3_CMD_DA_EN) + if (f1->dstip[0] != f2->dstip[0]) + return false; + + if (f1->cmd & (HW_ATL2_RPF_L3_CMD_PROTO_EN | + HW_ATL2_RPF_L3_V6_CMD_PROTO_EN)) + if (f1->proto != f2->proto) + return false; + + if (f1->cmd & HW_ATL2_RPF_L3_V6_CMD_SA_EN) + if (memcmp(f1->srcip, f2->srcip, 16)) + return false; + + if (f1->cmd & HW_ATL2_RPF_L3_V6_CMD_DA_EN) + if (memcmp(f1->dstip, f2->dstip, 16)) + return false; + + return true; +} + +static int hw_atl2_new_fl3l4_find_l3(struct aq_hw_s *self, + struct hw_atl2_l3_filter *l3) +{ + struct hw_atl2_priv *priv = self->priv; + struct hw_atl2_l3_filter *l3_filters; + int i, first, last; + + if (l3->cmd & HW_ATL2_RPF_L3_V6_CMD_EN) { + l3_filters = priv->l3_v6_filters; + first = priv->l3_v6_filter_base_index; + last = priv->l3_v6_filter_base_index + + priv->l3_v6_filter_count; + } else { + l3_filters = priv->l3_v4_filters; + first = priv->l3_v4_filter_base_index; + last = priv->l3_v4_filter_base_index + + priv->l3_v4_filter_count; + } + for (i = first; i < last; i++) { + if (hw_atl2_rxf_l3_is_equal(&l3_filters[i], l3)) + return i; + } + + for (i = first; i < last; i++) { + u32 l3_enable_mask = HW_ATL2_RPF_L3_CMD_EN | + HW_ATL2_RPF_L3_V6_CMD_EN; + + if (!(l3_filters[i].cmd & l3_enable_mask)) + return i; + } + + return -ENOSPC; +} + +static void hw_atl2_rxf_l3_get(struct aq_hw_s *self, + struct hw_atl2_l3_filter *l3, int idx, + const struct hw_atl2_l3_filter *_l3) +{ + int i; + + l3->usage++; + if (l3->usage == 1) { + l3->cmd = _l3->cmd; + for (i = 0; i < 4; i++) { + l3->srcip[i] = _l3->srcip[i]; + l3->dstip[i] = _l3->dstip[i]; + } + l3->proto = _l3->proto; + + if (l3->cmd & HW_ATL2_RPF_L3_CMD_EN) { + hw_atl2_rpf_l3_v4_cmd_set(self, l3->cmd, idx); + hw_atl2_rpf_l3_v4_tag_set(self, idx + 1, idx); + hw_atl2_rpf_l3_v4_dest_addr_set(self, + idx, + l3->dstip[0]); + hw_atl2_rpf_l3_v4_src_addr_set(self, + idx, + l3->srcip[0]); + } else { + hw_atl2_rpf_l3_v6_cmd_set(self, l3->cmd, idx); + hw_atl2_rpf_l3_v6_tag_set(self, idx + 1, idx); + hw_atl2_rpf_l3_v6_dest_addr_set(self, + idx, + l3->dstip); + hw_atl2_rpf_l3_v6_src_addr_set(self, + idx, + l3->srcip); + } + } +} + +static void hw_atl2_rxf_l3_put(struct aq_hw_s *self, + struct hw_atl2_l3_filter *l3, int idx) +{ + if (l3->usage) + l3->usage--; + + if (!l3->usage) { + if (l3->cmd & HW_ATL2_RPF_L3_V6_CMD_EN) + hw_atl2_rpf_l3_v6_cmd_set(self, 0, idx); + else + hw_atl2_rpf_l3_v4_cmd_set(self, 0, idx); + l3->cmd = 0; + } +} + +static bool hw_atl2_rxf_l4_is_equal(struct hw_atl2_l4_filter *f1, + struct hw_atl2_l4_filter *f2) +{ + if (f1->cmd != f2->cmd) + return false; + + if (f1->cmd & HW_ATL2_RPF_L4_CMD_SP_EN) + if (f1->sport != f2->sport) + return false; + + if (f1->cmd & HW_ATL2_RPF_L4_CMD_DP_EN) + if (f1->dport != f2->dport) + return false; + + return true; +} + +static int hw_atl2_new_fl3l4_find_l4(struct aq_hw_s *self, + struct hw_atl2_l4_filter *l4) +{ + struct hw_atl2_priv *priv = self->priv; + int i, first, last; + + first = priv->l4_filter_base_index; + last = priv->l4_filter_base_index + priv->l4_filter_count; + + for (i = first; i < last; i++) + if (hw_atl2_rxf_l4_is_equal(&priv->l4_filters[i], l4)) + return i; + + for (i = first; i < last; i++) + if ((priv->l4_filters[i].cmd & HW_ATL2_RPF_L4_CMD_EN) == 0) + return i; + + return -ENOSPC; +} + +static void hw_atl2_rxf_l4_put(struct aq_hw_s *self, + struct hw_atl2_l4_filter *l4, int idx) +{ + if (l4->usage) + l4->usage--; + + if (!l4->usage) { + l4->cmd = 0; + hw_atl2_rpf_l4_cmd_set(self, l4->cmd, idx); + } +} + +static void hw_atl2_rxf_l4_get(struct aq_hw_s *self, + struct hw_atl2_l4_filter *l4, int idx, + const struct hw_atl2_l4_filter *_l4) +{ + l4->usage++; + if (l4->usage == 1) { + l4->cmd = _l4->cmd; + l4->sport = _l4->sport; + l4->dport = _l4->dport; + + hw_atl2_rpf_l4_cmd_set(self, l4->cmd, idx); + hw_atl2_rpf_l4_tag_set(self, idx + 1, idx); + hw_atl_rpf_l4_spd_set(self, l4->sport, idx); + hw_atl_rpf_l4_dpd_set(self, l4->dport, idx); + } +} + +static int hw_atl2_new_fl3l4_configure(struct aq_hw_s *self, + struct aq_rx_filter_l3l4 *data) +{ + struct hw_atl2_priv *priv = self->priv; + s8 old_l3_index = priv->l3l4_filters[data->location].l3_index; + s8 old_l4_index = priv->l3l4_filters[data->location].l4_index; + u8 old_ipv6 = priv->l3l4_filters[data->location].ipv6; + struct hw_atl2_l3_filter *l3_filters; + struct hw_atl2_l3_filter l3; + struct hw_atl2_l4_filter l4; + s8 l3_idx = -1; + s8 l4_idx = -1; + + if (!(data->cmd & HW_ATL_RX_ENABLE_FLTR_L3L4)) + return 0; + + memset(&l3, 0, sizeof(l3)); + memset(&l4, 0, sizeof(l4)); + + /* convert legacy filter to new */ + if (data->cmd & HW_ATL_RX_ENABLE_CMP_PROT_L4) { + l3.cmd |= data->is_ipv6 ? HW_ATL2_RPF_L3_V6_CMD_PROTO_EN : + HW_ATL2_RPF_L3_CMD_PROTO_EN; + l3.cmd |= data->is_ipv6 ? HW_ATL2_RPF_L3_V6_CMD_EN : + HW_ATL2_RPF_L3_CMD_EN; + switch (data->cmd & 0x7) { + case HW_ATL_RX_TCP: + l3.cmd |= IPPROTO_TCP << (data->is_ipv6 ? 0x18 : 8); + break; + case HW_ATL_RX_UDP: + l3.cmd |= IPPROTO_UDP << (data->is_ipv6 ? 0x18 : 8); + break; + case HW_ATL_RX_SCTP: + l3.cmd |= IPPROTO_SCTP << (data->is_ipv6 ? 0x18 : 8); + break; + case HW_ATL_RX_ICMP: + l3.cmd |= IPPROTO_ICMP << (data->is_ipv6 ? 0x18 : 8); + break; + } + } + + if (data->cmd & HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3) { + if (data->is_ipv6) { + l3.cmd |= HW_ATL2_RPF_L3_V6_CMD_SA_EN | + HW_ATL2_RPF_L3_V6_CMD_EN; + memcpy(l3.srcip, data->ip_src, sizeof(l3.srcip)); + } else { + l3.cmd |= HW_ATL2_RPF_L3_CMD_SA_EN | + HW_ATL2_RPF_L3_CMD_EN; + l3.srcip[0] = data->ip_src[0]; + } + } + if (data->cmd & HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3) { + if (data->is_ipv6) { + l3.cmd |= HW_ATL2_RPF_L3_V6_CMD_DA_EN | + HW_ATL2_RPF_L3_V6_CMD_EN; + memcpy(l3.dstip, data->ip_dst, sizeof(l3.dstip)); + } else { + l3.cmd |= HW_ATL2_RPF_L3_CMD_DA_EN | + HW_ATL2_RPF_L3_CMD_EN; + l3.dstip[0] = data->ip_dst[0]; + } + } + + if (data->cmd & HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4) { + l4.cmd |= HW_ATL2_RPF_L4_CMD_DP_EN | HW_ATL2_RPF_L4_CMD_EN; + l4.dport = data->p_dst; + } + if (data->cmd & HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4) { + l4.cmd |= HW_ATL2_RPF_L4_CMD_SP_EN | HW_ATL2_RPF_L4_CMD_EN; + l4.sport = data->p_src; + } + + /* find L3 and L4 filters */ + if (l3.cmd & (HW_ATL2_RPF_L3_CMD_EN | HW_ATL2_RPF_L3_V6_CMD_EN)) { + l3_idx = hw_atl2_new_fl3l4_find_l3(self, &l3); + if (l3_idx < 0) + return l3_idx; + + if (l3.cmd & HW_ATL2_RPF_L3_V6_CMD_EN) + l3_filters = priv->l3_v6_filters; + else + l3_filters = priv->l3_v4_filters; + + if (priv->l3l4_filters[data->location].l3_index != l3_idx || + old_ipv6 != !!(l3.cmd & HW_ATL2_RPF_L3_V6_CMD_EN)) + hw_atl2_rxf_l3_get(self, &l3_filters[l3_idx], + l3_idx, &l3); + } + + if (old_l3_index != -1) { + if (old_ipv6) + l3_filters = priv->l3_v6_filters; + else + l3_filters = priv->l3_v4_filters; + + if (!(hw_atl2_rxf_l3_is_equal(&l3, + &l3_filters[old_l3_index]))) { + hw_atl2_rxf_l3_put(self, + &l3_filters[old_l3_index], + old_l3_index); + } + } + if (l3.cmd & HW_ATL2_RPF_L3_V6_CMD_EN) + priv->l3l4_filters[data->location].ipv6 = 1; + else + priv->l3l4_filters[data->location].ipv6 = 0; + priv->l3l4_filters[data->location].l3_index = l3_idx; + + if (l4.cmd & HW_ATL2_RPF_L4_CMD_EN) { + l4_idx = hw_atl2_new_fl3l4_find_l4(self, &l4); + if (l4_idx < 0) + return l4_idx; + + if (priv->l3l4_filters[data->location].l4_index != l4_idx) + hw_atl2_rxf_l4_get(self, &priv->l4_filters[l4_idx], + l4_idx, &l4); + } + + if (old_l4_index != -1) { + if (!(hw_atl2_rxf_l4_is_equal(&priv->l4_filters[old_l4_index], + &l4))) { + hw_atl2_rxf_l4_put(self, + &priv->l4_filters[old_l4_index], + old_l4_index); + } + } + priv->l3l4_filters[data->location].l4_index = l4_idx; + + return 0; +} + +static int hw_atl2_hw_fl3l4_set(struct aq_hw_s *self, + struct aq_rx_filter_l3l4 *data) +{ + struct hw_atl2_priv *priv = self->priv; + struct hw_atl2_l3_filter *l3_filters; + struct hw_atl2_l3_filter *l3 = NULL; + struct hw_atl2_l4_filter *l4 = NULL; + u8 location = data->location; + u32 req_tag = 0; + u16 action = 0; + int l3_index; + int l4_index; + u32 mask = 0; + u8 index; + u8 ipv6; + int res; + + res = hw_atl2_new_fl3l4_configure(self, data); + if (res) + return res; + + l3_index = priv->l3l4_filters[location].l3_index; + l4_index = priv->l3l4_filters[location].l4_index; + ipv6 = priv->l3l4_filters[location].ipv6; + if (ipv6) + l3_filters = priv->l3_v6_filters; + else + l3_filters = priv->l3_v4_filters; + + if (!(data->cmd & HW_ATL_RX_ENABLE_FLTR_L3L4)) { + if (l3_index > -1) + hw_atl2_rxf_l3_put(self, &l3_filters[l3_index], + l3_index); + + if (l4_index > -1) + hw_atl2_rxf_l4_put(self, &priv->l4_filters[l4_index], + l4_index); + + priv->l3l4_filters[location].l3_index = -1; + priv->l3l4_filters[location].l4_index = -1; + index = priv->art_base_index + HW_ATL2_RPF_L3L4_USER_INDEX + + location; + hw_atl2_act_rslvr_table_set(self, index, 0, 0, + HW_ATL2_ACTION_DISABLE); + + return 0; + } + + if (l3_index != -1) + l3 = &l3_filters[l3_index]; + if (l4_index != -1) + l4 = &priv->l4_filters[l4_index]; + + if (l4 && (l4->cmd & HW_ATL2_RPF_L4_CMD_EN)) { + req_tag |= (l4_index + 1) << HW_ATL2_RPF_TAG_L4_OFFSET; + mask |= HW_ATL2_RPF_TAG_L4_MASK; + } + + if (l3) { + if (l3->cmd & HW_ATL2_RPF_L3_V6_CMD_EN) { + req_tag |= (l3_index + 1) << + HW_ATL2_RPF_TAG_L3_V6_OFFSET; + mask |= HW_ATL2_RPF_TAG_L3_V6_MASK; + } else { + req_tag |= (l3_index + 1) << + HW_ATL2_RPF_TAG_L3_V4_OFFSET; + mask |= HW_ATL2_RPF_TAG_L3_V4_MASK; + } + } + + if (data->cmd & (HW_ATL_RX_HOST << HW_ATL2_RPF_L3_L4_ACTF_SHIFT)) + action = HW_ATL2_ACTION_ASSIGN_QUEUE((data->cmd & + HW_ATL2_RPF_L3_L4_RXQF_MSK) >> + HW_ATL2_RPF_L3_L4_RXQF_SHIFT); + else + action = HW_ATL2_ACTION_DROP; + + index = priv->art_base_index + HW_ATL2_RPF_L3L4_USER_INDEX + location; + hw_atl2_act_rslvr_table_set(self, index, req_tag, mask, action); + return 0; +} + +static int hw_atl2_hw_fl2_set(struct aq_hw_s *self, + struct aq_rx_filter_l2 *data) +{ + struct hw_atl2_priv *priv = self->priv; + u32 mask = HW_ATL2_RPF_TAG_ET_MASK; + u32 req_tag = 0; + u16 action = 0; + u32 location; + u8 index; + int tag; + + location = priv->etype_filter_base_index + data->location; + + hw_atl_rpf_etht_flr_set(self, data->ethertype, location); + hw_atl_rpf_etht_user_priority_en_set(self, + !!data->user_priority_en, + location); + if (data->user_priority_en) { + hw_atl_rpf_etht_user_priority_set(self, + data->user_priority, + location); + req_tag |= data->user_priority << HW_ATL2_RPF_TAG_PCP_OFFSET; + mask |= HW_ATL2_RPF_TAG_PCP_MASK; + } + + if (data->queue < 0) { + hw_atl_rpf_etht_flr_act_set(self, 0U, location); + hw_atl_rpf_etht_rx_queue_en_set(self, 0U, location); + action = HW_ATL2_ACTION_DROP; + } else { + hw_atl_rpf_etht_flr_act_set(self, 1U, location); + hw_atl_rpf_etht_rx_queue_en_set(self, 1U, location); + hw_atl_rpf_etht_rx_queue_set(self, data->queue, location); + action = HW_ATL2_ACTION_ASSIGN_QUEUE(data->queue); + } + + tag = hw_atl2_filter_tag_get(priv->etype_policy, + priv->etype_filter_tag_top, + action); + + if (tag < 0) + return -ENOSPC; + + req_tag |= tag << HW_ATL2_RPF_TAG_ET_OFFSET; + hw_atl2_rpf_etht_flr_tag_set(self, tag, location); + index = priv->art_base_index + HW_ATL2_RPF_ET_PCP_USER_INDEX + + data->location; + hw_atl2_act_rslvr_table_set(self, index, req_tag, mask, action); + + hw_atl_rpf_etht_flr_en_set(self, 1U, location); + + return aq_hw_err_from_flags(self); +} + +static int hw_atl2_hw_fl2_clear(struct aq_hw_s *self, + struct aq_rx_filter_l2 *data) +{ + struct hw_atl2_priv *priv = self->priv; + u32 location; + u8 index; + u32 tag; + + location = priv->etype_filter_base_index + data->location; + hw_atl_rpf_etht_flr_en_set(self, 0U, location); + hw_atl_rpf_etht_flr_set(self, 0U, location); + hw_atl_rpf_etht_user_priority_en_set(self, 0U, location); + + index = priv->art_base_index + HW_ATL2_RPF_ET_PCP_USER_INDEX + + data->location; + hw_atl2_act_rslvr_table_set(self, index, 0, 0, + HW_ATL2_ACTION_DISABLE); + tag = hw_atl2_rpf_etht_flr_tag_get(self, location); + hw_atl2_filter_tag_put(priv->etype_policy, tag); + + return aq_hw_err_from_flags(self); +} + +/* + * Set VLAN filter table + * Configure VLAN filter table to accept (and assign the queue) traffic + * for the particular vlan ids. + * Note: use this function under vlan promisc mode not to lost the traffic + * + * param - aq_hw_s + * param - aq_rx_filter_vlan VLAN filter configuration + * return 0 - OK, <0 - error + */ static int hw_atl2_hw_vlan_set(struct aq_hw_s *self, struct aq_rx_filter_vlan *aq_vlans) { @@ -825,7 +1605,7 @@ static int hw_atl2_hw_vlan_ctrl(struct aq_hw_s *self, bool enable) const struct aq_hw_ops hw_atl2_ops = { .hw_soft_reset = hw_atl2_utils_soft_reset, .hw_prepare = hw_atl2_utils_initfw, - .hw_set_mac_address = hw_atl_b0_hw_mac_addr_set, + .hw_set_mac_address = hw_atl2_hw_mac_addr_set, .hw_init = hw_atl2_hw_init, .hw_reset = hw_atl2_hw_reset, .hw_start = hw_atl_b0_hw_start, @@ -848,6 +1628,9 @@ const struct aq_hw_ops hw_atl2_ops = { .hw_ring_rx_init = hw_atl2_hw_ring_rx_init, .hw_ring_tx_init = hw_atl2_hw_ring_tx_init, .hw_packet_filter_set = hw_atl2_hw_packet_filter_set, + .hw_filter_l2_set = hw_atl2_hw_fl2_set, + .hw_filter_l2_clear = hw_atl2_hw_fl2_clear, + .hw_filter_l3l4_set = hw_atl2_hw_fl3l4_set, .hw_filter_vlan_set = hw_atl2_hw_vlan_set, .hw_filter_vlan_ctrl = hw_atl2_hw_vlan_ctrl, .hw_multicast_list_set = hw_atl2_hw_multicast_list_set, @@ -855,9 +1638,27 @@ const struct aq_hw_ops hw_atl2_ops = { .hw_rss_set = hw_atl2_hw_rss_set, .hw_rss_hash_set = hw_atl_b0_hw_rss_hash_set, .hw_tc_rate_limit_set = hw_atl2_hw_init_tx_tc_rate_limit, + .hw_get_regs = hw_atl2_utils_hw_get_regs, .hw_get_hw_stats = hw_atl2_utils_get_hw_stats, .hw_get_fw_version = hw_atl2_utils_get_fw_version, .hw_set_offload = hw_atl_b0_hw_offload_set, .hw_set_loopback = hw_atl_b0_set_loopback, .hw_set_fc = hw_atl_b0_set_fc, + + .hw_ring_hwts_rx_fill = NULL, + .hw_ring_hwts_rx_receive = NULL, + + .hw_get_ptp_ts = aq_get_ptp_ts, + .hw_adj_clock_freq = hw_atl2_adj_clock_freq, + .hw_adj_sys_clock = hw_atl2_adj_sys_clock, + .hw_gpio_pulse = hw_atl2_gpio_pulse, + + .enable_ptp = hw_atl2_enable_ptp, + .hw_ring_tx_ptp_get_ts = hw_atl2_hw_ring_tx_ptp_get_ts, + .rx_extract_ts = hw_atl2_hw_rx_extract_ts, + .hw_tx_ptp_ring_init = hw_atl2_hw_tx_ptp_ring_init, + .hw_rx_ptp_ring_init = hw_atl2_hw_rx_ptp_ring_init, + .hw_get_clk_sel = hw_atl2_hw_get_clk_sel, + .extract_hwts = NULL, + .hw_extts_gpio_enable = NULL, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h index 346f0dc9912e..4b905231ae73 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h @@ -7,6 +7,18 @@ #define HW_ATL2_H #include "aq_common.h" +#define HW_ATL2_RX_TS_SIZE 8 + +#define HW_ATL2_PTP_OFFSET_INGRESS_100 768 +#define HW_ATL2_PTP_OFFSET_EGRESS_100 336 +#define HW_ATL2_PTP_OFFSET_INGRESS_1000 510 +#define HW_ATL2_PTP_OFFSET_EGRESS_1000 105 +#define HW_ATL2_PTP_OFFSET_INGRESS_2500 2447 +#define HW_ATL2_PTP_OFFSET_EGRESS_2500 634 +#define HW_ATL2_PTP_OFFSET_INGRESS_5000 1426 +#define HW_ATL2_PTP_OFFSET_EGRESS_5000 361 +#define HW_ATL2_PTP_OFFSET_INGRESS_10000 997 +#define HW_ATL2_PTP_OFFSET_EGRESS_10000 203 extern const struct aq_hw_caps_s hw_atl2_caps_aqc113; extern const struct aq_hw_caps_s hw_atl2_caps_aqc115c; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h index 5a89bb8722f9..c36127900de3 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h @@ -27,8 +27,11 @@ #define HW_ATL2_INT_MASK (0xFFFFFFFFU) #define HW_ATL2_TXBUF_MAX 128U -#define HW_ATL2_RXBUF_MAX 192U +#define HW_ATL2_PTP_TXBUF_SIZE 8U +/* hw_atl2 on-chip RX packet buffer available for data TCs */ +#define HW_ATL2_RXBUF_MAX 172U +#define HW_ATL2_PTP_RXBUF_SIZE 16U #define HW_ATL2_RSS_REDIRECTION_MAX 64U #define HW_ATL2_TC_MAX 8U @@ -84,6 +87,19 @@ enum HW_ATL2_RPF_ART_INDEX { HW_ATL_VLAN_MAX_FILTERS, }; +#define HW_ATL2_RPF_L3_CMD_EN BIT(0) +#define HW_ATL2_RPF_L3_CMD_SA_EN BIT(1) +#define HW_ATL2_RPF_L3_CMD_DA_EN BIT(2) +#define HW_ATL2_RPF_L3_CMD_PROTO_EN BIT(3) +#define HW_ATL2_RPF_L3_V6_CMD_EN BIT(0x10) +#define HW_ATL2_RPF_L3_V6_CMD_SA_EN BIT(0x11) +#define HW_ATL2_RPF_L3_V6_CMD_DA_EN BIT(0x12) +#define HW_ATL2_RPF_L3_V6_CMD_PROTO_EN BIT(0x13) +#define HW_ATL2_RPF_L4_CMD_EN BIT(0) +#define HW_ATL2_RPF_L4_CMD_DP_EN BIT(1) +#define HW_ATL2_RPF_L4_CMD_SP_EN BIT(2) +#define HW_ATL2_ART_TOTAL_ENTRIES 128 + #define HW_ATL2_ACTION(ACTION, RSS, INDEX, VALID) \ ((((ACTION) & 0x3U) << 8) | \ (((RSS) & 0x1U) << 7) | \ @@ -94,6 +110,13 @@ enum HW_ATL2_RPF_ART_INDEX { #define HW_ATL2_ACTION_DISABLE HW_ATL2_ACTION(0, 0, 0, 0) #define HW_ATL2_ACTION_ASSIGN_QUEUE(QUEUE) HW_ATL2_ACTION(1, 0, (QUEUE), 1) #define HW_ATL2_ACTION_ASSIGN_TC(TC) HW_ATL2_ACTION(1, 1, (TC), 1) +#define HW_ATL2_RPF_L3L4_FILTERS 8 +#define HW_ATL2_RPF_L3V4_FILTERS 8 +#define HW_ATL2_RPF_L3V6_FILTERS 6 +#define HW_ATL2_RPF_L4_FILTERS 8 +#define HW_ATL2_RPF_VLAN_FILTERS 16 +#define HW_ATL2_RPF_ETYPE_FILTERS 16 +#define HW_ATL2_RPF_ETYPE_TAGS 7 enum HW_ATL2_RPF_RSS_HASH_TYPE { HW_ATL2_RPF_RSS_HASH_TYPE_NONE = 0, @@ -119,9 +142,54 @@ enum HW_ATL2_RPF_RSS_HASH_TYPE { #define HW_ATL_MCAST_FLT_ANY_TO_HOST 0x00010FFFU +struct hw_atl2_l3_filter { + u8 proto; + u8 usage; + u32 cmd; + u32 srcip[4]; + u32 dstip[4]; +}; + +struct hw_atl2_l4_filter { + u8 usage; + u32 cmd; + u16 sport; + u16 dport; +}; + +struct hw_atl2_l3l4_filter { + s8 l3_index; + s8 l4_index; + u8 ipv6; +}; + +struct hw_atl2_tag_policy { + u16 action; + u16 usage; +}; + struct hw_atl2_priv { + struct hw_atl2_l3_filter l3_v4_filters[HW_ATL2_RPF_L3L4_FILTERS]; + struct hw_atl2_l3_filter l3_v6_filters[HW_ATL2_RPF_L3L4_FILTERS]; + struct hw_atl2_l4_filter l4_filters[HW_ATL2_RPF_L3L4_FILTERS]; + struct hw_atl2_l3l4_filter l3l4_filters[HW_ATL2_RPF_L3L4_FILTERS]; + struct hw_atl2_tag_policy etype_policy[HW_ATL2_RPF_ETYPE_FILTERS]; struct statistics_s last_stats; unsigned int art_base_index; + unsigned int art_count; + unsigned int l2_filters_base_index; + unsigned int l2_filter_count; + unsigned int etype_filter_base_index; + unsigned int etype_filter_count; + unsigned int etype_filter_tag_top; + unsigned int vlan_filter_base_index; + unsigned int vlan_filter_count; + unsigned int l3_v4_filter_base_index; + unsigned int l3_v4_filter_count; + unsigned int l3_v6_filter_base_index; + unsigned int l3_v6_filter_count; + unsigned int l4_filter_base_index; + unsigned int l4_filter_count; }; #endif /* HW_ATL2_INTERNAL_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c index cd954b11d24a..2414e60b2028 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c @@ -7,6 +7,13 @@ #include "hw_atl2_llh_internal.h" #include "aq_hw_utils.h" +u32 hw_atl2_phi_ext_tag_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg_bit(aq_hw, HW_ATL2_PHI_EXT_TAG_EN_ADR, + HW_ATL2_PHI_EXT_TAG_EN_MSK, + HW_ATL2_PHI_EXT_TAG_EN_SHIFT); +} + void hw_atl2_rpf_redirection_table2_select_set(struct aq_hw_s *aq_hw, u32 select) { @@ -66,6 +73,266 @@ void hw_atl2_rpf_vlan_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter) tag); } +void hw_atl2_rpf_etht_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_ET_TAG_ADR(filter), + HW_ATL2_RPF_ET_TAG_MSK, + HW_ATL2_RPF_ET_TAG_SHIFT, tag); +} + +u32 hw_atl2_rpf_etht_flr_tag_get(struct aq_hw_s *aq_hw, u32 filter) +{ + return aq_hw_read_reg_bit(aq_hw, HW_ATL2_RPF_ET_TAG_ADR(filter), + HW_ATL2_RPF_ET_TAG_MSK, + HW_ATL2_RPF_ET_TAG_SHIFT); +} + +void hw_atl2_rpf_l3_v4_dest_addr_set(struct aq_hw_s *aq_hw, u32 filter, u32 val) +{ + u32 addr_set = 6 + ((filter < 4) ? 0 : 1); + u32 dword = filter % 4; + + aq_hw_write_reg(aq_hw, HW_ATL2_RPF_L3_DA_DW_ADR(addr_set, dword), val); +} + +void hw_atl2_rpf_l3_v4_src_addr_set(struct aq_hw_s *aq_hw, u32 filter, u32 val) +{ + u32 addr_set = 6 + ((filter < 4) ? 0 : 1); + u32 dword = filter % 4; + + aq_hw_write_reg(aq_hw, HW_ATL2_RPF_L3_SA_DW_ADR(addr_set, dword), val); +} + +void hw_atl2_rpf_l3_v6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location, + u32 *ipv6_dst) +{ + int i; + + for (i = 0; i < 4; ++i) + aq_hw_write_reg(aq_hw, + HW_ATL2_RPF_L3_DA_DW_ADR(location, 3 - i), + ipv6_dst[i]); +} + +void hw_atl2_rpf_l3_v6_src_addr_set(struct aq_hw_s *aq_hw, u8 location, + u32 *ipv6_src) +{ + int i; + + for (i = 0; i < 4; ++i) + aq_hw_write_reg(aq_hw, + HW_ATL2_RPF_L3_SA_DW_ADR(location, 3 - i), + ipv6_src[i]); +} + +void hw_atl2_rpf_l3_v4_cmd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L3_V4_CMD_ADR(filter), + HW_ATL2_RPF_L3_V4_CMD_MSK, + HW_ATL2_RPF_L3_V4_CMD_SHIFT, val); +} + +void hw_atl2_rpf_l3_v6_cmd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L3_V6_CMD_ADR(filter), + HW_ATL2_RPF_L3_V6_CMD_MSK, + HW_ATL2_RPF_L3_V6_CMD_SHIFT, val); +} + +void hw_atl2_rpf_l3_v6_v4_select_set(struct aq_hw_s *aq_hw, u32 val) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L3_V6_V4_SELECT_ADR, + HW_ATL2_RPF_L3_V6_V4_SELECT_MSK, + HW_ATL2_RPF_L3_V6_V4_SELECT_SHIFT, val); +} + +void hw_atl2_rpf_l3_v4_tag_set(struct aq_hw_s *aq_hw, u32 val, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L3_V4_TAG_ADR(filter), + HW_ATL2_RPF_L3_V4_TAG_MSK, + HW_ATL2_RPF_L3_V4_TAG_SHIFT, val); +} + +void hw_atl2_rpf_l3_v6_tag_set(struct aq_hw_s *aq_hw, u32 val, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L3_V6_TAG_ADR(filter), + HW_ATL2_RPF_L3_V6_TAG_MSK, + HW_ATL2_RPF_L3_V6_TAG_SHIFT, val); +} + +void hw_atl2_rpf_l4_tag_set(struct aq_hw_s *aq_hw, u32 val, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L4_TAG_ADR(filter), + HW_ATL2_RPF_L4_TAG_MSK, + HW_ATL2_RPF_L4_TAG_SHIFT, val); +} + +void hw_atl2_rpf_l4_cmd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L4_CMD_ADR(filter), + HW_ATL2_RPF_L4_CMD_MSK, + HW_ATL2_RPF_L4_CMD_SHIFT, val); +} + +/* tsg */ +static void hw_atl2_clock_modif_value_set(struct aq_hw_s *aq_hw, + u32 clock_sel, u64 ns) +{ + aq_hw_write_reg64(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_MODIF_VAL_LSW), + ns); +} + +void hw_atl2_tsg_clock_en(struct aq_hw_s *aq_hw, + u32 clock_sel, u32 clock_enable) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_CFG), + HW_ATL2_TSG_CLOCK_EN_MSK, + HW_ATL2_TSG_CLOCK_EN_SHIFT, + clock_enable); +} + +void hw_atl2_tsg_clock_reset(struct aq_hw_s *aq_hw, u32 clock_sel) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_CFG), + HW_ATL2_TSG_SYNC_RESET_MSK, + HW_ATL2_TSG_SYNC_RESET_SHIFT, 1); + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_CFG), + HW_ATL2_TSG_SYNC_RESET_MSK, + HW_ATL2_TSG_SYNC_RESET_SHIFT, 0); +} + +u64 hw_atl2_tsg_clock_read(struct aq_hw_s *aq_hw, u32 clock_sel) +{ + return aq_hw_read_reg64(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, + READ_CUR_NS_LSW)); +} + +void hw_atl2_tsg_clock_add(struct aq_hw_s *aq_hw, u32 clock_sel, u64 ns) +{ + hw_atl2_clock_modif_value_set(aq_hw, clock_sel, ns); + aq_hw_write_reg(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_MODIF_CTRL), + HW_ATL2_TSG_ADD_COUNTER_MSK); +} + +void hw_atl2_tsg_clock_sub(struct aq_hw_s *aq_hw, u32 clock_sel, u64 ns) +{ + hw_atl2_clock_modif_value_set(aq_hw, clock_sel, ns); + aq_hw_write_reg(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_MODIF_CTRL), + HW_ATL2_TSG_SUBTRACT_COUNTER_MSK); +} + +void hw_atl2_tsg_clock_increment_set(struct aq_hw_s *aq_hw, + u32 clock_sel, u32 ns, u32 fns) +{ + u32 nsfns = (ns & 0xff) | (fns & 0xffffff00); + + aq_hw_write_reg(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_INC_CFG), + nsfns); + aq_hw_write_reg(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, CLOCK_MODIF_CTRL), + HW_ATL2_TSG_LOAD_INC_CFG_MSK); +} + +void hw_atl2_tpb_tps_highest_priority_tc_enable_set(struct aq_hw_s *aq_hw, + u32 tps_highest_prio_tc_en) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TPB_HIGHEST_PRIO_TC_EN_ADR, + HW_ATL2_TPB_HIGHEST_PRIO_TC_EN_MSK, + HW_ATL2_TPB_HIGHEST_PRIO_TC_EN_SHIFT, + tps_highest_prio_tc_en); +} + +void hw_atl2_tpb_tps_highest_priority_tc_set(struct aq_hw_s *aq_hw, + u32 tps_highest_prio_tc) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TPB_HIGHEST_PRIO_TC_ADR, + HW_ATL2_TPB_HIGHEST_PRIO_TC_MSK, + HW_ATL2_TPB_HIGHEST_PRIO_TC_SHIFT, + tps_highest_prio_tc); +} + +void hw_atl2_tsg_gpio_isr_to_host_set(struct aq_hw_s *aq_hw, + int on, u32 clock_sel) +{ + aq_hw_write_reg_bit(aq_hw, + HW_ATL2_GLOBAL_HIGH_PRIO_INTERRUPT_1_MASK_ADR, + clock_sel == 1 ? HW_ATL2_TSG_TSG1_GPIO_INTERRUPT_MSK : + HW_ATL2_TSG_TSG0_GPIO_INTERRUPT_MSK, + clock_sel == 1 ? HW_ATL2_TSG_TSG1_GPIO_INTERRUPT_SHIFT : + HW_ATL2_TSG_TSG0_GPIO_INTERRUPT_SHIFT, + !!on); +} + +void hw_atl2_tsg_gpio_clear_status(struct aq_hw_s *aq_hw) +{ + aq_hw_read_reg(aq_hw, HW_ATL2_GLOBAL_INTERNAL_ALARMS_1_ADR); +} + +void hw_atl2_tsg_gpio_input_event_info_get(struct aq_hw_s *aq_hw, + u32 clock_sel, + u32 *event_count, + u64 *event_ts) +{ + if (event_count) + *event_count = aq_hw_read_reg(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, + EXT_CLK_COUNT)); + + if (event_ts) + *event_ts = aq_hw_read_reg64(aq_hw, + HW_ATL2_TSG_REG_ADR(clock_sel, + GPIO_EVENT_TS_LSW)); +} + +void hw_atl2_tsg_ptp_gpio_gen_pulse(struct aq_hw_s *aq_hw, u32 clk_sel, + u64 ts, u32 period, u32 hightime) +{ + u32 val = (HW_ATL2_TSG_GPIO_EVENT_MODE_SET_ON_TIME << + (HW_ATL2_TSG_GPIO_EVENT_MODE_SHIFT - + HW_ATL2_TSG_GPIO_OUTPUT_EN_SHIFT)) | + (HW_ATL2_TSG_GPIO_GEN_OUTPUT_EN_MSK) | + (HW_ATL2_TSG_GPIO_OUTPUT_EN_MSK); + + if (ts != 0) { + aq_hw_write_reg64(aq_hw, + HW_ATL2_TSG_REG_ADR(clk_sel, + GPIO_EVENT_GEN_TS_LSW), + ts); + + aq_hw_write_reg64(aq_hw, + HW_ATL2_TSG_REG_ADR(clk_sel, + GPIO_EVENT_HIGH_TIME_LSW), + hightime); + + aq_hw_write_reg64(aq_hw, + HW_ATL2_TSG_REG_ADR(clk_sel, + GPIO_EVENT_LOW_TIME_LSW), + (period - hightime)); + } + + aq_hw_write_reg_bit(aq_hw, + HW_ATL2_TSG_REG_ADR(clk_sel, GPIO_EVENT_GEN_CFG), + HW_ATL2_TSG_GPIO_EVENT_MODE_MSK | + HW_ATL2_TSG_GPIO_OUTPUT_EN_MSK | + HW_ATL2_TSG_GPIO_GEN_OUTPUT_EN_MSK, + HW_ATL2_TSG_GPIO_OUTPUT_EN_SHIFT, + (!ts ? 0 : val)); +} + +void hw_atl2_rpf_rx_desc_timestamp_req_set(struct aq_hw_s *aq_hw, u32 request, + u32 descriptor) +{ + aq_hw_write_reg_bit(aq_hw, + HW_ATL2_RPF_TIMESTAMP_REQ_DESCD_ADR(descriptor), + HW_ATL2_RPF_TIMESTAMP_REQ_DESCD_MSK, + HW_ATL2_RPF_TIMESTAMP_REQ_DESCD_SHIFT, request); +} + /* TX */ void hw_atl2_tpb_tx_tc_q_rand_map_en_set(struct aq_hw_s *aq_hw, @@ -93,6 +360,30 @@ void hw_atl2_reg_tx_intr_moder_ctrl_set(struct aq_hw_s *aq_hw, tx_intr_moderation_ctl); } +void hw_atl2_tdm_tx_desc_timestamp_writeback_en_set(struct aq_hw_s *aq_hw, + u32 enable, u32 descriptor) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TDM_DESCD_TS_WRB_EN_ADR(descriptor), + HW_ATL2_TDM_DESCD_TS_WRB_EN_MSK, + HW_ATL2_TDM_DESCD_TS_WRB_EN_SHIFT, enable); +} + +void hw_atl2_tdm_tx_desc_timestamp_en_set(struct aq_hw_s *aq_hw, u32 enable, + u32 descriptor) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TDM_DESCD_TS_EN_ADR(descriptor), + HW_ATL2_TDM_DESCD_TS_EN_MSK, + HW_ATL2_TDM_DESCD_TS_EN_SHIFT, enable); +} + +void hw_atl2_tdm_tx_desc_avb_en_set(struct aq_hw_s *aq_hw, u32 enable, + u32 descriptor) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TDM_DESCD_AVB_EN_ADR(descriptor), + HW_ATL2_TDM_DESCD_AVB_EN_MSK, + HW_ATL2_TDM_DESCD_AVB_EN_SHIFT, enable); +} + void hw_atl2_tps_tx_pkt_shed_data_arb_mode_set(struct aq_hw_s *aq_hw, const u32 data_arb_mode) { @@ -122,6 +413,20 @@ void hw_atl2_tps_tx_pkt_shed_tc_data_weight_set(struct aq_hw_s *aq_hw, weight); } +void hw_atl2_tdm_tx_data_read_req_limit_set(struct aq_hw_s *aq_hw, u32 limit) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TDM_TX_DATA_RD_REQ_LIMIT_ADR, + HW_ATL2_TDM_TX_DATA_RD_REQ_LIMIT_MSK, + HW_ATL2_TDM_TX_DATA_RD_REQ_LIMIT_SHIFT, limit); +} + +void hw_atl2_tdm_tx_desc_read_req_limit_set(struct aq_hw_s *aq_hw, u32 limit) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_TDM_TX_DESC_RD_REQ_LIMIT_ADR, + HW_ATL2_TDM_TX_DESC_RD_REQ_LIMIT_MSK, + HW_ATL2_TDM_TX_DESC_RD_REQ_LIMIT_SHIFT, limit); +} + u32 hw_atl2_get_hw_version(struct aq_hw_s *aq_hw) { return aq_hw_read_reg(aq_hw, HW_ATL2_FPGA_VER_ADR); @@ -164,6 +469,13 @@ void hw_atl2_rpf_act_rslvr_section_en_set(struct aq_hw_s *aq_hw, u32 sections) sections); } +u32 hw_atl2_rpf_act_rslvr_section_en_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg_bit(aq_hw, HW_ATL2_RPF_REC_TAB_EN_ADR, + HW_ATL2_RPF_REC_TAB_EN_MSK, + HW_ATL2_RPF_REC_TAB_EN_SHIFT); +} + void hw_atl2_mif_shared_buf_get(struct aq_hw_s *aq_hw, int offset, u32 *data, int len) { @@ -232,3 +544,13 @@ void hw_atl2_mif_host_req_int_clr(struct aq_hw_s *aq_hw, u32 val) return aq_hw_write_reg(aq_hw, HW_ATL2_MCP_HOST_REQ_INT_CLR_ADR, val); } + +void hw_atl2_gpio_special_mode_set(struct aq_hw_s *aq_hw, + u32 gpio_special_mode, + u32 pin) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL2_GPIO_PIN_SPEC_MODE_ADR(pin), + HW_ATL2_GPIO_PIN_SPEC_MODE_MSK, + HW_ATL2_GPIO_PIN_SPEC_MODE_SHIFT, + gpio_special_mode); +} diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h index 98c7a4621297..00dcc3e0184f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h @@ -10,6 +10,9 @@ struct aq_hw_s; +/* Get Enable usage of extended tags from 32-255. */ +u32 hw_atl2_phi_ext_tag_get(struct aq_hw_s *aq_hw); + /* Set TX Interrupt Moderation Control Register */ void hw_atl2_reg_tx_intr_moder_ctrl_set(struct aq_hw_s *aq_hw, u32 tx_intr_moderation_ctl, @@ -19,7 +22,7 @@ void hw_atl2_reg_tx_intr_moder_ctrl_set(struct aq_hw_s *aq_hw, void hw_atl2_rpf_redirection_table2_select_set(struct aq_hw_s *aq_hw, u32 select); -/** Set RSS HASH type */ +/* Set RSS HASH type */ void hw_atl2_rpf_rss_hash_type_set(struct aq_hw_s *aq_hw, u32 rss_hash_type); /* set new RPF enable */ @@ -37,14 +40,91 @@ void hw_atl2_new_rpf_rss_redir_set(struct aq_hw_s *aq_hw, u32 tc, u32 index, /* Set VLAN filter tag */ void hw_atl2_rpf_vlan_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter); +/* set ethertype filter tag */ +void hw_atl2_rpf_etht_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter); + +/* get ethertype filter tag */ +u32 hw_atl2_rpf_etht_flr_tag_get(struct aq_hw_s *aq_hw, u32 filter); + +/* set L3 v4 dest address */ +void hw_atl2_rpf_l3_v4_dest_addr_set(struct aq_hw_s *aq_hw, + u32 filter, u32 val); + +/* set L3 v4 src address */ +void hw_atl2_rpf_l3_v4_src_addr_set(struct aq_hw_s *aq_hw, u32 filter, u32 val); + +/* set L3 v4 cmd */ +void hw_atl2_rpf_l3_v4_cmd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter); + +/* set L3 v6 cmd */ +void hw_atl2_rpf_l3_v6_cmd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter); + +/* set L3 v6 dest address */ +void hw_atl2_rpf_l3_v6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location, + u32 *ipv6_dst); + +/* set L3 v6 src address */ +void hw_atl2_rpf_l3_v6_src_addr_set(struct aq_hw_s *aq_hw, u8 location, + u32 *ipv6_src); + +/* set L3 v6 v4 select */ +void hw_atl2_rpf_l3_v6_v4_select_set(struct aq_hw_s *aq_hw, u32 val); + +/* set L3 v4 tag */ +void hw_atl2_rpf_l3_v4_tag_set(struct aq_hw_s *aq_hw, u32 val, u32 filter); + +/* set L3 v6 tag */ +void hw_atl2_rpf_l3_v6_tag_set(struct aq_hw_s *aq_hw, u32 val, u32 filter); + +/* set L4 cmd */ +void hw_atl2_rpf_l4_cmd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter); + +/* set L4 tag */ +void hw_atl2_rpf_l4_tag_set(struct aq_hw_s *aq_hw, u32 val, u32 filter); /* set tx random TC-queue mapping enable bit */ void hw_atl2_tpb_tx_tc_q_rand_map_en_set(struct aq_hw_s *aq_hw, const u32 tc_q_rand_map_en); +void hw_atl2_tpb_tps_highest_priority_tc_enable_set(struct aq_hw_s *aq_hw, + u32 tps_highest_prio_tc_en); + +void hw_atl2_tpb_tps_highest_priority_tc_set(struct aq_hw_s *aq_hw, + u32 tps_highest_prio_tc); + /* set tx buffer clock gate enable */ void hw_atl2_tpb_tx_buf_clk_gate_en_set(struct aq_hw_s *aq_hw, u32 clk_gate_en); +/* tsg */ + +void hw_atl2_tsg_clock_en(struct aq_hw_s *aq_hw, u32 clock_sel, + u32 clock_enable); + +void hw_atl2_tsg_clock_reset(struct aq_hw_s *aq_hw, u32 clock_sel); +u64 hw_atl2_tsg_clock_read(struct aq_hw_s *aq_hw, u32 clock_sel); +void hw_atl2_tsg_clock_add(struct aq_hw_s *aq_hw, u32 clock_sel, + u64 ns); +void hw_atl2_tsg_clock_sub(struct aq_hw_s *aq_hw, u32 clock_sel, + u64 ns); +void hw_atl2_tsg_clock_increment_set(struct aq_hw_s *aq_hw, u32 clock_sel, + u32 ns, u32 fns); +void hw_atl2_tsg_gpio_isr_to_host_set(struct aq_hw_s *aq_hw, int on, + u32 clock_sel); +void hw_atl2_tsg_gpio_clear_status(struct aq_hw_s *aq_hw); +void hw_atl2_tsg_gpio_input_event_info_get(struct aq_hw_s *aq_hw, + u32 clock_sel, + u32 *event_count, + u64 *event_ts); +/* Set Rx Descriptor0 Timestamp request */ +void hw_atl2_rpf_rx_desc_timestamp_req_set(struct aq_hw_s *aq_hw, u32 request, + u32 descriptor); +/* Set Tx Descriptor Timestamp writeback Enable */ +void hw_atl2_tdm_tx_desc_timestamp_writeback_en_set(struct aq_hw_s *aq_hw, + u32 enable, + u32 descriptor); +/* Set Tx Descriptor Timestamp enable */ +void hw_atl2_tdm_tx_desc_timestamp_en_set(struct aq_hw_s *aq_hw, u32 enable, + u32 descriptor); void hw_atl2_tps_tx_pkt_shed_data_arb_mode_set(struct aq_hw_s *aq_hw, const u32 data_arb_mode); @@ -57,6 +137,15 @@ void hw_atl2_tps_tx_pkt_shed_tc_data_max_credit_set(struct aq_hw_s *aq_hw, void hw_atl2_tps_tx_pkt_shed_tc_data_weight_set(struct aq_hw_s *aq_hw, const u32 tc, const u32 weight); +/* Set Tx Descriptor AVB enable */ +void hw_atl2_tdm_tx_desc_avb_en_set(struct aq_hw_s *aq_hw, u32 enable, + u32 descriptor); +void hw_atl2_tsg_ptp_gpio_gen_pulse(struct aq_hw_s *aq_hw, u32 clk_sel, + u64 ts, u32 period, u32 hightime); + +void hw_atl2_tdm_tx_data_read_req_limit_set(struct aq_hw_s *aq_hw, u32 limit); + +void hw_atl2_tdm_tx_desc_read_req_limit_set(struct aq_hw_s *aq_hw, u32 limit); u32 hw_atl2_get_hw_version(struct aq_hw_s *aq_hw); @@ -69,6 +158,9 @@ void hw_atl2_rpf_act_rslvr_record_set(struct aq_hw_s *aq_hw, u8 location, /* set enable action resolver section */ void hw_atl2_rpf_act_rslvr_section_en_set(struct aq_hw_s *aq_hw, u32 sections); +/* get enable action resolver section */ +u32 hw_atl2_rpf_act_rslvr_section_en_get(struct aq_hw_s *aq_hw); + /* get data from firmware shared input buffer */ void hw_atl2_mif_shared_buf_get(struct aq_hw_s *aq_hw, int offset, u32 *data, int len); @@ -99,4 +191,7 @@ u32 hw_atl2_mif_host_req_int_get(struct aq_hw_s *aq_hw); /* clear host interrupt request */ void hw_atl2_mif_host_req_int_clr(struct aq_hw_s *aq_hw, u32 val); +/* Set GPIO Special Mode */ +void hw_atl2_gpio_special_mode_set(struct aq_hw_s *aq_hw, + u32 gpio_special_mode, u32 pin); #endif /* HW_ATL2_LLH_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h index e34c5cda061e..9b9be3ef1332 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h @@ -5,6 +5,11 @@ #ifndef HW_ATL2_LLH_INTERNAL_H #define HW_ATL2_LLH_INTERNAL_H +/* RX timestamp_req_desc{D} [1:0] Bitfield Definitions + */ +#define HW_ATL2_RPF_TIMESTAMP_REQ_DESCD_ADR(descr) (0x00005B08 + (descr) * 0x20) +#define HW_ATL2_RPF_TIMESTAMP_REQ_DESCD_MSK 0x00030000 +#define HW_ATL2_RPF_TIMESTAMP_REQ_DESCD_SHIFT 16 /* RX pif_rpf_redir_2_en_i Bitfield Definitions * PORT="pif_rpf_redir_2_en_i" @@ -114,7 +119,68 @@ #define HW_ATL2_RPF_VL_TAG_WIDTH 4 /* default value of bitfield vlan_req_tag0{f}[3:0] */ #define HW_ATL2_RPF_VL_TAG_DEFAULT 0x0 +/* register address for bitfield etype_req_tag0{f}[2:0] */ +#define HW_ATL2_RPF_ET_TAG_ADR(filter) (0x00005340 + (filter) * 0x4) +/* bitmask for bitfield etype_req_tag0{f}[2:0] */ +#define HW_ATL2_RPF_ET_TAG_MSK 0x00000007 +/* lower bit position of bitfield etype_req_tag0{f}[2:0] */ +#define HW_ATL2_RPF_ET_TAG_SHIFT 0 +/* Lower bit position of bitfield l3_l4_act{F}[2:0] */ +#define HW_ATL2_RPF_L3_L4_ACTF_SHIFT 16 +/* Bitmask for bitfield l3_l4_rxq{F}[4:0] */ +#define HW_ATL2_RPF_L3_L4_RXQF_MSK 0x00001F00u +/* Lower bit position of bitfield l3_l4_rxq{F}[4:0] */ +#define HW_ATL2_RPF_L3_L4_RXQF_SHIFT 8 +/* Register address for bitfield rpf_l3_v6_sa{F}_dw{D}[1F:0] */ +#define HW_ATL2_RPF_L3_SA_DW_ADR(filter, dword) \ + (0x00006400u + (filter) * 0x10 + (dword) * 0x4) +/* Register address for bitfield rpf_l3_v6_da{F}_dw{D}[1F:0] */ +#define HW_ATL2_RPF_L3_DA_DW_ADR(filter, dword) \ + (0x00006480u + (filter) * 0x10 + (dword) * 0x4) + +/* Register address for bitfield rpf_l3_cmd{F}[1F:0] */ +#define HW_ATL2_RPF_L3_V4_CMD_ADR(filter) (0x00006500u + (filter) * 0x4) +/* Bitmask for bitfield rpf_l3_cmd{F}[F:0] */ +#define HW_ATL2_RPF_L3_V4_CMD_MSK 0x0000FFFFu +/* Lower bit position of bitfield rpf_l3_cmd{F}[1F:0] */ +#define HW_ATL2_RPF_L3_V4_CMD_SHIFT 0 +/* Register address for bitfield rpf_l3_v6_cmd{F}[1F:0] */ +#define HW_ATL2_RPF_L3_V6_CMD_ADR(filter) (0x00006500u + (filter) * 0x4) +/* Bitmask for bitfield rpf_l3_v6_cmd{F}[F:0] */ +#define HW_ATL2_RPF_L3_V6_CMD_MSK 0xFF7F0000u +/* Lower bit position of bitfield rpf_l3_v6_cmd{F}[1F:0] */ +#define HW_ATL2_RPF_L3_V6_CMD_SHIFT 0 +/* Register address for bitfield rpf_l3_v6_cmd{F}[F:0] */ +#define HW_ATL2_RPF_L3_V6_V4_SELECT_ADR 0x00006500u +/* Bitmask for bitfield pif_rpf_l3_v6_v4_select*/ +#define HW_ATL2_RPF_L3_V6_V4_SELECT_MSK 0x00800000u +/* Lower bit position of bitfield pif_rpf_l3_v6_v4_select */ +#define HW_ATL2_RPF_L3_V6_V4_SELECT_SHIFT 23 +/* Register address for bitfield rpf_l3_v4_req_tag{F}[2:0] */ +#define HW_ATL2_RPF_L3_V4_TAG_ADR(filter) (0x00006500u + (filter) * 0x4) +/* Bitmask for bitfield rpf_l3_v4_req_tag{F}[2:0] */ +#define HW_ATL2_RPF_L3_V4_TAG_MSK 0x00000070u +/* Lower bit position of bitfield rpf_l3_v4_req_tag{F}[2:0] */ +#define HW_ATL2_RPF_L3_V4_TAG_SHIFT 4 +/* Register address for bitfield rpf_l3_v6_req_tag{F}[2:0] */ +#define HW_ATL2_RPF_L3_V6_TAG_ADR(filter) (0x00006500u + (filter) * 0x4) +/* Bitmask for bitfield rpf_l3_v6_req_tag{F}[2:0] */ +#define HW_ATL2_RPF_L3_V6_TAG_MSK 0x00700000 +/* Lower bit position of bitfield rpf_l3_v6_req_tag{F}[2:0] */ +#define HW_ATL2_RPF_L3_V6_TAG_SHIFT 20 +/* Register address for bitfield rpf_l4_cmd{F}[2:0] */ +#define HW_ATL2_RPF_L4_CMD_ADR(filter) (0x00006520u + (filter) * 0x4) +/* Bitmask for bitfield rpf_l4_cmd{F}[2:0] */ +#define HW_ATL2_RPF_L4_CMD_MSK 0x00000007u +/* Lower bit position of bitfield rpf_l4_cmd{F}[2:0] */ +#define HW_ATL2_RPF_L4_CMD_SHIFT 0 +/* Register address for bitfield rpf_l4_tag{F}[2:0] */ +#define HW_ATL2_RPF_L4_TAG_ADR(filter) (0x00006520u + (filter) * 0x4) +/* Bitmask for bitfield rpf_l4_tag{F}[2:0] */ +#define HW_ATL2_RPF_L4_TAG_MSK 0x00000070u +/* Lower bit position of bitfield rpf_l4_tag{F}[2:0] */ +#define HW_ATL2_RPF_L4_TAG_SHIFT 4 /* RX rx_q{Q}_tc_map[2:0] Bitfield Definitions * Preprocessor definitions for the bitfield "rx_q{Q}_tc_map[2:0]". * Parameter: Queue {Q} | bit-level stride | range [0, 31] @@ -131,7 +197,24 @@ #define HW_ATL2_RX_Q_TC_MAP_WIDTH 3 /* Default value of bitfield rx_q{Q}_tc_map[2:0] */ #define HW_ATL2_RX_Q_TC_MAP_DEFAULT 0x0 - +/* TX desc{D}_ts_wrb_en Bitfield Definitions + */ +#define HW_ATL2_TDM_DESCD_TS_WRB_EN_ADR(descriptor) \ + (0x00007C08 + (descriptor) * 0x40) +#define HW_ATL2_TDM_DESCD_TS_WRB_EN_MSK 0x00040000 +#define HW_ATL2_TDM_DESCD_TS_WRB_EN_SHIFT 18 +/* TX desc{D}_ts_en Bitfield Definitions + */ +#define HW_ATL2_TDM_DESCD_TS_EN_ADR(descriptor) \ + (0x00007C08 + (descriptor) * 0x40) +#define HW_ATL2_TDM_DESCD_TS_EN_MSK 0x00020000 +#define HW_ATL2_TDM_DESCD_TS_EN_SHIFT 17 +/* TX desc{D}_avb_en Bitfield Definitions + */ +#define HW_ATL2_TDM_DESCD_AVB_EN_ADR(descriptor) \ + (0x00007C08 + (descriptor) * 0x40) +#define HW_ATL2_TDM_DESCD_AVB_EN_MSK 0x00010000 +#define HW_ATL2_TDM_DESCD_AVB_EN_SHIFT 16 /* tx tx_tc_q_rand_map_en bitfield definitions * preprocessor definitions for the bitfield "tx_tc_q_rand_map_en". * port="pif_tpb_tx_tc_q_rand_map_en_i" @@ -221,7 +304,18 @@ #define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_WIDTH 16 /* default value of bitfield data_tc{t}_credit_max[f:0] */ #define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_DEFAULT 0x0 - +/* register address for bitfield pif_tpb_highest_prio_tc_en */ +#define HW_ATL2_TPB_HIGHEST_PRIO_TC_EN_ADR 0x00007180 +/* bitmask for bitfield pif_tpb_highest_prio_tc_en */ +#define HW_ATL2_TPB_HIGHEST_PRIO_TC_EN_MSK 0x00000100 +/* lower bit position of bitfield pif_tpb_highest_prio_tc_en */ +#define HW_ATL2_TPB_HIGHEST_PRIO_TC_EN_SHIFT 8 +/* register address for bitfield pif_tpb_highest_prio_tc */ +#define HW_ATL2_TPB_HIGHEST_PRIO_TC_ADR 0x00007180 +/* bitmask for bitfield pif_tpb_highest_prio_tc */ +#define HW_ATL2_TPB_HIGHEST_PRIO_TC_MSK 0x00000007 +/* lower bit position of bitfield pif_tpb_highest_prio_tc */ +#define HW_ATL2_TPB_HIGHEST_PRIO_TC_SHIFT 0 /* tx data_tc{t}_weight[e:0] bitfield definitions * preprocessor definitions for the bitfield "data_tc{t}_weight[e:0]". * parameter: tc {t} | stride size 0x4 | range [0, 7] @@ -248,7 +342,87 @@ */ #define HW_ATL2_TX_INTR_MODERATION_CTL_ADR(queue) (0x00007c28u + (queue) * 0x40) +/* TX tx_data_rd_req_limit[7:0] Bitfield Definitions + */ +#define HW_ATL2_TDM_TX_DATA_RD_REQ_LIMIT_ADR 0x00007B04 +#define HW_ATL2_TDM_TX_DATA_RD_REQ_LIMIT_MSK 0x0000FF00 +#define HW_ATL2_TDM_TX_DATA_RD_REQ_LIMIT_SHIFT 8 +/* TX tx_desc_rd_req_limit[4:0] Bitfield Definitions + */ +#define HW_ATL2_TDM_TX_DESC_RD_REQ_LIMIT_ADR 0x00007B04 +#define HW_ATL2_TDM_TX_DESC_RD_REQ_LIMIT_MSK 0x0000001F +#define HW_ATL2_TDM_TX_DESC_RD_REQ_LIMIT_SHIFT 0 +/* register address for bitfield uP Force Interrupt */ +#define HW_ATL2_GLB_CONTROL_2_ADR 0x00000404 +#define HW_ATL2_MIF_INTERRUPT_2_TO_ITR_MSK 0x00000100 +/* lower bit position of bitfield MIF Interrupt to ITR */ +#define HW_ATL2_MIF_INTERRUPT_TO_ITR_SHIFT 6 +#define HW_ATL2_EN_INTERRUPT_MIF2_TO_ITR_MSK 0x00001000 +/* lower bit position of bitfield Enable MIF Interrupt to ITR */ +#define HW_ATL2_EN_INTERRUPT_TO_ITR_SHIFT 0xA +#define HW_ATL2_GLOBAL_INTERNAL_ALARMS_1_ADR 0x00000924 +#define HW_ATL2_GLOBAL_HIGH_PRIO_INTERRUPT_1_MASK_ADR 0x00000964 +/* bitmask for bitfield TSG PTM GPIO interrupt */ +#define HW_ATL2_TSG_TSG1_GPIO_INTERRUPT_MSK 0x00000200 +/* lower bit position of bitfield TSG PTM GPIO interrupt */ +#define HW_ATL2_TSG_TSG1_GPIO_INTERRUPT_SHIFT 9 +/* bitmask for bitfield TSG0 GPIO interrupt */ +#define HW_ATL2_TSG_TSG0_GPIO_INTERRUPT_MSK 0x00000020 +/* lower bit position of bitfield TSG0 GPIO interrupt */ +#define HW_ATL2_TSG_TSG0_GPIO_INTERRUPT_SHIFT 5 +/* TSG registers */ +#define HW_ATL2_TSG_REG_ADR(clk, reg_name) \ + ((clk) == 0 ? HW_ATL2_CLK0_##reg_name##_ADR :\ + HW_ATL2_CLK1_##reg_name##_ADR) +#define HW_ATL2_CLK0_CLOCK_CFG_ADR 0x00000CA0u +#define HW_ATL2_CLK1_CLOCK_CFG_ADR 0x00000D50u +#define HW_ATL2_TSG_SYNC_RESET_MSK 0x00000001 +#define HW_ATL2_TSG_SYNC_RESET_SHIFT 0x00000000 +#define HW_ATL2_TSG_CLOCK_EN_MSK 0x00000002 +#define HW_ATL2_TSG_CLOCK_EN_SHIFT 0x00000001 +#define HW_ATL2_CLK0_CLOCK_MODIF_CTRL_ADR 0x00000CA4u +#define HW_ATL2_CLK1_CLOCK_MODIF_CTRL_ADR 0x00000D54u +#define HW_ATL2_TSG_SUBTRACT_COUNTER_MSK 0x00000002 +#define HW_ATL2_TSG_ADD_COUNTER_MSK 0x00000004 +#define HW_ATL2_TSG_LOAD_INC_CFG_MSK 0x00000008 +#define HW_ATL2_CLK0_CLOCK_MODIF_VAL_LSW_ADR 0x00000CA8u +#define HW_ATL2_CLK1_CLOCK_MODIF_VAL_LSW_ADR 0x00000D58u +#define HW_ATL2_CLK0_CLOCK_INC_CFG_ADR 0x00000CB0u +#define HW_ATL2_CLK1_CLOCK_INC_CFG_ADR 0x00000D60u +#define HW_ATL2_CLK0_READ_CUR_NS_LSW_ADR 0x00000CB8u +#define HW_ATL2_CLK1_READ_CUR_NS_LSW_ADR 0x00000D68u + +#define HW_ATL2_CLK0_GPIO_CFG_ADR 0x00000CC4u +#define HW_ATL2_CLK1_GPIO_CFG_ADR 0x00000D74u +#define HW_ATL2_TSG_GPIO_IN_MONITOR_EN_SHIFT 0x00000000 +#define HW_ATL2_TSG_GPIO_IN_MONITOR_EN_MSK 0x00000001 +#define HW_ATL2_TSG_GPIO_IN_MODE_SHIFT 0x00000001 +#define HW_ATL2_TSG_GPIO_IN_MODE_MSK 0x00000006 +#define HW_ATL2_TSG_GPIO_IN_MODE_POSEDGE 0x00000000 +#define HW_ATL2_CLK0_EXT_CLK_COUNT_ADR 0x00000CCCu +#define HW_ATL2_CLK1_EXT_CLK_COUNT_ADR 0x00000D7Cu +#define HW_ATL2_CLK0_GPIO_EVENT_TS_LSW_ADR 0x00000CD0u +#define HW_ATL2_CLK1_GPIO_EVENT_TS_LSW_ADR 0x00000D80u +#define HW_ATL2_CLK0_GPIO_EVENT_GEN_TS_LSW_ADR 0x00000CE0u +#define HW_ATL2_CLK1_GPIO_EVENT_GEN_TS_LSW_ADR 0x00000D90u +#define HW_ATL2_CLK0_GPIO_EVENT_GEN_CFG_ADR 0x00000CE8u +#define HW_ATL2_CLK1_GPIO_EVENT_GEN_CFG_ADR 0x00000D98u +#define HW_ATL2_TSG_GPIO_OUTPUT_EN_SHIFT 0x00000000 +#define HW_ATL2_TSG_GPIO_OUTPUT_EN_MSK 0x00000001 +#define HW_ATL2_TSG_GPIO_EVENT_MODE_SHIFT 0x00000001 +#define HW_ATL2_TSG_GPIO_EVENT_MODE_MSK 0x00000006 +#define HW_ATL2_TSG_GPIO_EVENT_MODE_SET_ON_TIME 0x00000003 +#define HW_ATL2_TSG_GPIO_GEN_OUTPUT_EN_MSK 0x00000008 +#define HW_ATL2_CLK0_GPIO_EVENT_HIGH_TIME_LSW_ADR 0x00000CF0u +#define HW_ATL2_CLK1_GPIO_EVENT_HIGH_TIME_LSW_ADR 0x00000DA0u +#define HW_ATL2_CLK0_GPIO_EVENT_LOW_TIME_LSW_ADR 0x00000CF8u +#define HW_ATL2_CLK1_GPIO_EVENT_LOW_TIME_LSW_ADR 0x00000DA8u +/* PCIE Extended tag enable Bitfield Definitions + */ +#define HW_ATL2_PHI_EXT_TAG_EN_ADR 0x00001000 +#define HW_ATL2_PHI_EXT_TAG_EN_MSK 0x00000020 +#define HW_ATL2_PHI_EXT_TAG_EN_SHIFT 5 /* Launch time control register */ #define HW_ATL2_LT_CTRL_ADR 0x00007a1c @@ -387,5 +561,25 @@ #define HW_ATL2_MCP_HOST_REQ_INT_ADR 0x00000F00u #define HW_ATL2_MCP_HOST_REQ_INT_SET_ADR 0x00000F04u #define HW_ATL2_MCP_HOST_REQ_INT_CLR_ADR 0x00000F08u - +/* Register address for bitfield PTP EXT GPIO TS SEL */ +#define HW_ATL2_TSG0_EXT_GPIO_TS_INPUT_SEL_ADR 0x00003664 +/* Bitmask for bitfield PTP EXT GPIO TS SEL */ +#define HW_ATL2_TSG0_EXT_GPIO_TS_INPUT_SEL_MSK 0x00001F00 +/* Lower bit position of bitfield PTP EXT GPIO TS SEL */ +#define HW_ATL2_TSG0_EXT_GPIO_TS_INPUT_SEL_SHIFT 8 +/* Register address for bitfield TSG EXT GPIO TS SEL */ +#define HW_ATL2_TSG1_EXT_GPIO_TS_INPUT_SEL_ADR 0x00003660 +/* Bitmask for bitfield TSG EXT GPIO TS SEL */ +#define HW_ATL2_TSG1_EXT_GPIO_TS_INPUT_SEL_MSK 0x00001F00 +/* Lower bit position of bitfield TSG EXT GPIO TS SEL */ +#define HW_ATL2_TSG1_EXT_GPIO_TS_INPUT_SEL_SHIFT 8 +/* Register address for bitfield GPIO{P} Special Mode */ +#define HW_ATL2_GPIO_PIN_SPEC_MODE_ADR(pin) (0x00003698 + (pin) * 0x4) +/* Bitmask for bitfield GPIO{P} Special Mode */ +#define HW_ATL2_GPIO_PIN_SPEC_MODE_MSK 0x0000000C +/* Lower bit position of bitfield GPIO{P} Special Mode */ +#define HW_ATL2_GPIO_PIN_SPEC_MODE_SHIFT 2 +#define HW_ATL2_GPIO_PIN_SPEC_MODE_TSG1_EVENT_OUTPUT 0 +#define HW_ATL2_GPIO_PIN_SPEC_MODE_TSG0_EVENT_OUTPUT 2 +#define HW_ATL2_GPIO_PIN_SPEC_MODE_GPIO 3 #endif /* HW_ATL2_LLH_INTERNAL_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c index 0fe6257d9c08..ef549e172dd9 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c @@ -128,3 +128,37 @@ int hw_atl2_utils_soft_reset(struct aq_hw_s *self) err_exit: return err; } + +static const u32 hw_atl2_utils_hw_mac_regs[] = { + 0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U, + 0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U, + 0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U, + 0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U, + 0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U, + 0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U, + 0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U, + 0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U, + 0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U, + 0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U, + 0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U, + 0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U, + 0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U, + 0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U, + 0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U, + 0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U, + 0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU, + 0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU, +}; + +int hw_atl2_utils_hw_get_regs(struct aq_hw_s *self, + const struct aq_hw_caps_s *aq_hw_caps, + u32 *regs_buff) +{ + unsigned int i; + + for (i = 0; i < min_t(u32, aq_hw_caps->mac_regs_count, + ARRAY_SIZE(hw_atl2_utils_hw_mac_regs)); i++) + regs_buff[i] = aq_hw_read_reg(self, + hw_atl2_utils_hw_mac_regs[i]); + return 0; +} diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h index 6bad64c77b87..e02ed522b004 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h @@ -8,6 +8,16 @@ #include "aq_hw.h" +/* Hardware tx launch time descriptor */ +struct hw_atl2_txts_s { + __le64 ts; + __le32 ctrl; + u32 reserved; +}; + +#define HW_ATL2_TXTS_DD BIT(3) +#define HW_ATL2_TXTS_TS_VALID BIT(20) + /* F W A P I */ struct link_options_s { @@ -626,10 +636,13 @@ int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops); int hw_atl2_utils_soft_reset(struct aq_hw_s *self); +int hw_atl2_utils_hw_get_regs(struct aq_hw_s *self, + const struct aq_hw_caps_s *aq_hw_caps, + u32 *regs_buff); + u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self); -int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self, - u8 *base_index, u8 *count); +int hw_atl2_utils_get_filter_caps(struct aq_hw_s *self); extern const struct aq_fw_ops aq_a2_fw_ops; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c index 7370e3f76b62..dc9b2ef4640b 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c @@ -121,6 +121,10 @@ static int aq_a2_fw_init(struct aq_hw_s *self) u32 val; int err; + err = hw_atl2_utils_get_filter_caps(self); + if (err) + return err; + hw_atl2_shared_buffer_get(self, link_control, link_control); link_control.mode = AQ_HOST_MODE_ACTIVE; hw_atl2_shared_buffer_write(self, link_control, link_control); @@ -606,18 +610,77 @@ u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self) version.bundle.build; } -int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self, - u8 *base_index, u8 *count) +int hw_atl2_utils_get_filter_caps(struct aq_hw_s *self) { + struct hw_atl2_priv *priv = self->priv; struct filter_caps_s filter_caps; + u32 tag_top; int err; err = hw_atl2_shared_buffer_read_safe(self, filter_caps, &filter_caps); if (err) return err; - *base_index = filter_caps.rslv_tbl_base_index; - *count = filter_caps.rslv_tbl_count; + priv->art_base_index = filter_caps.rslv_tbl_base_index * 8; + priv->art_count = filter_caps.rslv_tbl_count * 8; + if (priv->art_count == 0) + priv->art_count = HW_ATL2_ART_TOTAL_ENTRIES; + priv->l2_filters_base_index = filter_caps.l2_filters_base_index; + if (priv->l2_filters_base_index >= HW_ATL2_MAC_MAX) + priv->l2_filters_base_index = HW_ATL2_MAC_UC; + priv->l2_filter_count = filter_caps.l2_filter_count; + priv->etype_filter_base_index = filter_caps.ethertype_filter_base_index; + priv->etype_filter_count = filter_caps.ethertype_filter_count; + priv->etype_filter_tag_top = + (priv->etype_filter_count >= HW_ATL2_RPF_ETYPE_TAGS) ? + (HW_ATL2_RPF_ETYPE_TAGS) : (HW_ATL2_RPF_ETYPE_TAGS >> 1); + priv->vlan_filter_base_index = filter_caps.vlan_filter_base_index; + /* 0 - no tag, 1 - reserved for vlan-filter-offload filters */ + tag_top = + (filter_caps.vlan_filter_count == HW_ATL2_RPF_VLAN_FILTERS) ? + (HW_ATL2_RPF_VLAN_FILTERS - 2) : + (HW_ATL2_RPF_VLAN_FILTERS / 2 - 2); + + if (filter_caps.vlan_filter_count > 2) { + priv->vlan_filter_count = min_t(u32, + filter_caps.vlan_filter_count - 2, + tag_top); + } else { + pr_debug("atlantic: FW vlan_filter_count=%u <= 2, no usable VLAN filters\n", + filter_caps.vlan_filter_count); + priv->vlan_filter_count = 0; + } + + priv->l3_v4_filter_base_index = filter_caps.l3_ip4_filter_base_index; + if (priv->l3_v4_filter_base_index >= HW_ATL2_RPF_L3V4_FILTERS) { + priv->l3_v4_filter_base_index = 0; + priv->l3_v4_filter_count = 0; + } else { + priv->l3_v4_filter_count = min_t(u32, + filter_caps.l3_ip4_filter_count, + HW_ATL2_RPF_L3V4_FILTERS - 1); + } + + priv->l3_v6_filter_base_index = filter_caps.l3_ip6_filter_base_index; + if (priv->l3_v6_filter_base_index >= HW_ATL2_RPF_L3V6_FILTERS) { + priv->l3_v6_filter_base_index = 0; + priv->l3_v6_filter_count = 0; + } else { + priv->l3_v6_filter_count = min_t(u32, + filter_caps.l3_ip6_filter_count, + HW_ATL2_RPF_L3V6_FILTERS - 1); + } + + priv->l4_filter_base_index = filter_caps.l4_filter_base_index; + if (priv->l4_filter_base_index >= HW_ATL2_RPF_L4_FILTERS) { + priv->l4_filter_base_index = 0; + priv->l4_filter_count = 0; + } else { + priv->l4_filter_count = min_t(u32, + filter_caps.l4_filter_count, + HW_ATL2_RPF_L4_FILTERS - 1); + } + return 0; } diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 280e2f5f4aa5..5560d5cc3169 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -2756,10 +2756,8 @@ static void atl2_force_ps(struct atl2_hw *hw) /* All parameters are treated the same, as an integer array of values. * This macro just reduces the need to repeat the same declaration code * over and over (plus this helps to avoid typo bugs). - */ -#define ATL2_PARAM_INIT {[0 ... ATL2_MAX_NIC] = OPTION_UNSET} -#ifndef module_param_array -/* Module Parameters are always initialized to -1, so that the driver + * + * Module parameters are always initialized to -1, so that the driver * can tell the difference between no user specified value or the * user asking for the default value. * The true default values are loaded in when atl2_check_options is called. @@ -2770,16 +2768,10 @@ static void atl2_force_ps(struct atl2_hw *hw) */ #define ATL2_PARAM(X, desc) \ - static const int X[ATL2_MAX_NIC + 1] = ATL2_PARAM_INIT; \ - MODULE_PARM(X, "1-" __MODULE_STRING(ATL2_MAX_NIC) "i"); \ - MODULE_PARM_DESC(X, desc); -#else -#define ATL2_PARAM(X, desc) \ - static int X[ATL2_MAX_NIC+1] = ATL2_PARAM_INIT; \ + static int X[ATL2_MAX_NIC+1] = {[0 ... ATL2_MAX_NIC] = OPTION_UNSET}; \ static unsigned int num_##X; \ module_param_array_named(X, X, int, &num_##X, 0); \ MODULE_PARM_DESC(X, desc); -#endif /* * Transmit Memory Size @@ -2924,9 +2916,6 @@ static void atl2_check_options(struct atl2_adapter *adapter) printk(KERN_NOTICE "Warning: no configuration for board #%i\n", bd); printk(KERN_NOTICE "Using defaults for all values\n"); -#ifndef module_param_array - bd = ATL2_MAX_NIC; -#endif } /* Bytes of Transmit Memory */ @@ -2936,16 +2925,12 @@ static void atl2_check_options(struct atl2_adapter *adapter) opt.def = ATL2_DEFAULT_TX_MEMSIZE; opt.arg.r.min = ATL2_MIN_TX_MEMSIZE; opt.arg.r.max = ATL2_MAX_TX_MEMSIZE; -#ifdef module_param_array if (num_TxMemSize > bd) { -#endif val = TxMemSize[bd]; atl2_validate_option(&val, &opt); adapter->txd_ring_size = ((u32) val) * 1024; -#ifdef module_param_array } else adapter->txd_ring_size = ((u32)opt.def) * 1024; -#endif /* txs ring size: */ adapter->txs_ring_size = adapter->txd_ring_size / 128; if (adapter->txs_ring_size > 160) @@ -2958,18 +2943,14 @@ static void atl2_check_options(struct atl2_adapter *adapter) opt.def = ATL2_DEFAULT_RXD_COUNT; opt.arg.r.min = ATL2_MIN_RXD_COUNT; opt.arg.r.max = ATL2_MAX_RXD_COUNT; -#ifdef module_param_array if (num_RxMemBlock > bd) { -#endif val = RxMemBlock[bd]; atl2_validate_option(&val, &opt); adapter->rxd_ring_size = (u32)val; /* FIXME */ /* ((u16)val)&~1; */ /* even number */ -#ifdef module_param_array } else adapter->rxd_ring_size = (u32)opt.def; -#endif /* init RXD Flow control value */ adapter->hw.fc_rxd_hi = (adapter->rxd_ring_size / 8) * 7; adapter->hw.fc_rxd_lo = (ATL2_MIN_RXD_COUNT / 8) > @@ -2983,16 +2964,12 @@ static void atl2_check_options(struct atl2_adapter *adapter) opt.def = INT_MOD_DEFAULT_CNT; opt.arg.r.min = INT_MOD_MIN_CNT; opt.arg.r.max = INT_MOD_MAX_CNT; -#ifdef module_param_array if (num_IntModTimer > bd) { -#endif val = IntModTimer[bd]; atl2_validate_option(&val, &opt); adapter->imt = (u16) val; -#ifdef module_param_array } else adapter->imt = (u16)(opt.def); -#endif /* Flash Vendor */ opt.type = range_option; opt.name = "SPI Flash Vendor"; @@ -3000,16 +2977,12 @@ static void atl2_check_options(struct atl2_adapter *adapter) opt.def = FLASH_VENDOR_DEFAULT; opt.arg.r.min = FLASH_VENDOR_MIN; opt.arg.r.max = FLASH_VENDOR_MAX; -#ifdef module_param_array if (num_FlashVendor > bd) { -#endif val = FlashVendor[bd]; atl2_validate_option(&val, &opt); adapter->hw.flash_vendor = (u8) val; -#ifdef module_param_array } else adapter->hw.flash_vendor = (u8)(opt.def); -#endif /* MediaType */ opt.type = range_option; opt.name = "Speed/Duplex Selection"; @@ -3017,14 +2990,10 @@ static void atl2_check_options(struct atl2_adapter *adapter) opt.def = MEDIA_TYPE_AUTO_SENSOR; opt.arg.r.min = MEDIA_TYPE_AUTO_SENSOR; opt.arg.r.max = MEDIA_TYPE_10M_HALF; -#ifdef module_param_array if (num_MediaType > bd) { -#endif val = MediaType[bd]; atl2_validate_option(&val, &opt); adapter->hw.MediaType = (u16) val; -#ifdef module_param_array } else adapter->hw.MediaType = (u16)(opt.def); -#endif } diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 4287edc7ddd6..f0bac0dd1439 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -78,6 +78,7 @@ config BCMGENET select BCM7XXX_PHY select MDIO_BCM_UNIMAC select DIMLIB + select PAGE_POOL select BROADCOM_PHY if ARCH_BCM2835 help This driver supports the built-in Ethernet MACs found in the diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c index ec63f50a849e..ed0977832ce4 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c @@ -344,40 +344,35 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static void bcmasp_netif_start(struct net_device *dev) +static void umac_reset_and_init(struct bcmasp_intf *intf, + const unsigned char *addr) { - struct bcmasp_intf *intf = netdev_priv(dev); + struct phy_device *phydev = intf->ndev->phydev; + u32 mac0, mac1; - bcmasp_set_rx_mode(dev); - napi_enable(&intf->tx_napi); - napi_enable(&intf->rx_napi); - - bcmasp_enable_rx_irq(intf, 1); - bcmasp_enable_tx_irq(intf, 1); - bcmasp_enable_phy_irq(intf, 1); - - phy_start(dev->phydev); -} - -static void umac_reset(struct bcmasp_intf *intf) -{ umac_wl(intf, 0x0, UMC_CMD); umac_wl(intf, UMC_CMD_SW_RESET, UMC_CMD); usleep_range(10, 100); /* We hold the umac in reset and bring it out of * reset when phy link is up. */ -} -static void umac_set_hw_addr(struct bcmasp_intf *intf, - const unsigned char *addr) -{ - u32 mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | - addr[3]; - u32 mac1 = (addr[4] << 8) | addr[5]; + umac_wl(intf, 0x800, UMC_FRM_LEN); + umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL); + umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ); + + mac0 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | + addr[3]; + mac1 = (addr[4] << 8) | addr[5]; umac_wl(intf, mac0, UMC_MAC0); umac_wl(intf, mac1, UMC_MAC1); + + /* Reset shadow values since we reset the umac */ + intf->old_duplex = -1; + intf->old_link = -1; + intf->old_pause = -1; + phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); } static void umac_enable_set(struct bcmasp_intf *intf, u32 mask, @@ -401,13 +396,6 @@ static void umac_enable_set(struct bcmasp_intf *intf, u32 mask, usleep_range(1000, 2000); } -static void umac_init(struct bcmasp_intf *intf) -{ - umac_wl(intf, 0x800, UMC_FRM_LEN); - umac_wl(intf, 0xffff, UMC_PAUSE_CNTRL); - umac_wl(intf, 0x800, UMC_RX_MAX_PKT_SZ); -} - static int bcmasp_tx_reclaim(struct bcmasp_intf *intf) { struct bcmasp_intf_stats64 *stats = &intf->stats64; @@ -927,7 +915,15 @@ static void bcmasp_rgmii_mode_en_set(struct bcmasp_intf *intf, bool enable) rgmii_wl(intf, reg, RGMII_OOB_CNTRL); } -static void bcmasp_netif_deinit(struct net_device *dev) +static void bcmasp_phy_hw_unprepare(struct bcmasp_intf *intf) +{ + if (intf->internal_phy) + bcmasp_ephy_enable_set(intf, false); + else + bcmasp_rgmii_mode_en_set(intf, false); +} + +static void bcmasp_netif_deinit(struct net_device *dev, bool stop_phy) { struct bcmasp_intf *intf = netdev_priv(dev); u32 reg, timeout = 1000; @@ -950,7 +946,8 @@ static void bcmasp_netif_deinit(struct net_device *dev) umac_enable_set(intf, UMC_CMD_TX_EN, 0); - phy_stop(dev->phydev); + if (stop_phy) + phy_stop(dev->phydev); umac_enable_set(intf, UMC_CMD_RX_EN, 0); @@ -978,17 +975,13 @@ static int bcmasp_stop(struct net_device *dev) /* Stop tx from updating HW */ netif_tx_disable(dev); - bcmasp_netif_deinit(dev); + bcmasp_netif_deinit(dev, true); bcmasp_reclaim_free_buffers(intf); phy_disconnect(dev->phydev); - /* Disable internal EPHY or external PHY */ - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, false); - else - bcmasp_rgmii_mode_en_set(intf, false); + bcmasp_phy_hw_unprepare(intf); /* Disable the interface clocks */ bcmasp_core_clock_set_intf(intf, false); @@ -998,10 +991,15 @@ static int bcmasp_stop(struct net_device *dev) return 0; } -static void bcmasp_configure_port(struct bcmasp_intf *intf) +static void bcmasp_phy_hw_prepare(struct bcmasp_intf *intf) { u32 reg, id_mode_dis = 0; + if (intf->internal_phy) + bcmasp_ephy_enable_set(intf, true); + else + bcmasp_rgmii_mode_en_set(intf, true); + reg = rgmii_rl(intf, RGMII_PORT_CNTRL); reg &= ~RGMII_PORT_MODE_MASK; @@ -1036,26 +1034,8 @@ static void bcmasp_configure_port(struct bcmasp_intf *intf) rgmii_wl(intf, reg, RGMII_OOB_CNTRL); } -static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) +static phy_interface_t bcmasp_phy_iface_for_connect(phy_interface_t mode) { - struct bcmasp_intf *intf = netdev_priv(dev); - phy_interface_t phy_iface = intf->phy_interface; - u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE | - PHY_BRCM_DIS_TXCRXC_NOENRGY | - PHY_BRCM_IDDQ_SUSPEND; - struct phy_device *phydev = NULL; - int ret; - - /* Always enable interface clocks */ - bcmasp_core_clock_set_intf(intf, true); - - /* Enable internal PHY or external PHY before any MAC activity */ - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, true); - else - bcmasp_rgmii_mode_en_set(intf, true); - bcmasp_configure_port(intf); - /* This is an ugly quirk but we have not been correctly * interpreting the phy_interface values and we have done that * across different drivers, so at least we are consistent in @@ -1081,46 +1061,43 @@ static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) * affected because they use different phy_interface_t values * or the Generic PHY driver. */ - switch (phy_iface) { + switch (mode) { case PHY_INTERFACE_MODE_RGMII: - phy_iface = PHY_INTERFACE_MODE_RGMII_ID; - break; + return PHY_INTERFACE_MODE_RGMII_ID; case PHY_INTERFACE_MODE_RGMII_TXID: - phy_iface = PHY_INTERFACE_MODE_RGMII_RXID; - break; + return PHY_INTERFACE_MODE_RGMII_RXID; default: - break; + return mode; } +} - if (phy_connect) { - phydev = of_phy_connect(dev, intf->phy_dn, - bcmasp_adj_link, phy_flags, - phy_iface); - if (!phydev) { - ret = -ENODEV; - netdev_err(dev, "could not attach to PHY\n"); - goto err_phy_disable; - } +static int bcmasp_phy_attach(struct bcmasp_intf *intf) +{ + u32 phy_flags = PHY_BRCM_AUTO_PWRDWN_ENABLE | + PHY_BRCM_DIS_TXCRXC_NOENRGY | + PHY_BRCM_IDDQ_SUSPEND; + struct phy_device *phydev; + phy_interface_t phy_iface; - if (intf->internal_phy) - dev->phydev->irq = PHY_MAC_INTERRUPT; - - /* Indicate that the MAC is responsible for PHY PM */ - phydev->mac_managed_pm = true; - - /* Set phylib's copy of the LPI timer */ - phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); + phy_iface = bcmasp_phy_iface_for_connect(intf->phy_interface); + phydev = of_phy_connect(intf->ndev, intf->phy_dn, + bcmasp_adj_link, phy_flags, + phy_iface); + if (!phydev) { + netdev_err(intf->ndev, "could not attach to PHY\n"); + return -ENODEV; } + if (intf->internal_phy) + intf->ndev->phydev->irq = PHY_MAC_INTERRUPT; - umac_reset(intf); + phydev->mac_managed_pm = true; - umac_init(intf); + return 0; +} - umac_set_hw_addr(intf, dev->dev_addr); - - intf->old_duplex = -1; - intf->old_link = -1; - intf->old_pause = -1; +static void bcmasp_netif_init(struct net_device *dev) +{ + struct bcmasp_intf *intf = netdev_priv(dev); bcmasp_init_tx(intf); netif_napi_add_tx(intf->ndev, &intf->tx_napi, bcmasp_tx_poll); @@ -1132,18 +1109,13 @@ static int bcmasp_netif_init(struct net_device *dev, bool phy_connect) intf->crc_fwd = !!(umac_rl(intf, UMC_CMD) & UMC_CMD_CRC_FWD); - bcmasp_netif_start(dev); + bcmasp_set_rx_mode(dev); + napi_enable(&intf->tx_napi); + napi_enable(&intf->rx_napi); - netif_start_queue(dev); - - return 0; - -err_phy_disable: - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, false); - else - bcmasp_rgmii_mode_en_set(intf, false); - return ret; + bcmasp_enable_rx_irq(intf, 1); + bcmasp_enable_tx_irq(intf, 1); + bcmasp_enable_phy_irq(intf, 1); } static int bcmasp_open(struct net_device *dev) @@ -1161,14 +1133,30 @@ static int bcmasp_open(struct net_device *dev) if (ret) goto err_free_mem; - ret = bcmasp_netif_init(dev, true); - if (ret) { - clk_disable_unprepare(intf->parent->clk); - goto err_free_mem; - } + bcmasp_core_clock_set_intf(intf, true); + + bcmasp_phy_hw_prepare(intf); + + ret = bcmasp_phy_attach(intf); + if (ret) + goto err_phy_attach; + + umac_reset_and_init(intf, dev->dev_addr); + + dev->phydev->eee_cfg.tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); + + bcmasp_netif_init(dev); + + phy_start(dev->phydev); + + netif_start_queue(dev); return ret; +err_phy_attach: + bcmasp_phy_hw_unprepare(intf); + bcmasp_core_clock_set_intf(intf, false); + clk_disable_unprepare(intf->parent->clk); err_free_mem: bcmasp_reclaim_free_buffers(intf); @@ -1398,19 +1386,27 @@ int bcmasp_interface_suspend(struct bcmasp_intf *intf) { struct device *kdev = &intf->parent->pdev->dev; struct net_device *dev = intf->ndev; + bool wake; if (!netif_running(dev)) return 0; netif_device_detach(dev); - bcmasp_netif_deinit(dev); + wake = device_may_wakeup(kdev) && intf->wolopts; - if (!intf->wolopts) { - if (intf->internal_phy) - bcmasp_ephy_enable_set(intf, false); - else - bcmasp_rgmii_mode_en_set(intf, false); + bcmasp_netif_deinit(dev, !wake); + + if (wake) { + /* Disable phy status updates while suspending */ + mutex_lock(&dev->phydev->lock); + dev->phydev->state = PHY_READY; + mutex_unlock(&dev->phydev->lock); + cancel_delayed_work_sync(&dev->phydev->state_queue); + + bcmasp_suspend_to_wol(intf); + } else { + bcmasp_phy_hw_unprepare(intf); /* If Wake-on-LAN is disabled, we can safely * disable the network interface clocks. @@ -1418,9 +1414,6 @@ int bcmasp_interface_suspend(struct bcmasp_intf *intf) bcmasp_core_clock_set_intf(intf, false); } - if (device_may_wakeup(kdev) && intf->wolopts) - bcmasp_suspend_to_wol(intf); - clk_disable_unprepare(intf->parent->clk); return 0; @@ -1444,8 +1437,11 @@ static void bcmasp_resume_from_wol(struct bcmasp_intf *intf) int bcmasp_interface_resume(struct bcmasp_intf *intf) { + struct device *kdev = &intf->parent->pdev->dev; struct net_device *dev = intf->ndev; + bool wake; int ret; + u32 reg; if (!netif_running(dev)) return 0; @@ -1454,17 +1450,41 @@ int bcmasp_interface_resume(struct bcmasp_intf *intf) if (ret) return ret; - ret = bcmasp_netif_init(dev, false); - if (ret) - goto out; + wake = device_may_wakeup(kdev) && intf->wolopts; - bcmasp_resume_from_wol(intf); + bcmasp_core_clock_set_intf(intf, true); + + /* The interface might be HW reset in some suspend modes, so we may + * need to restore the UNIMAC/PHY if that is the case. + */ + reg = umac_rl(intf, UMC_CMD); + if (wake && (reg & UMC_CMD_RX_EN)) { + umac_enable_set(intf, UMC_CMD_TX_EN, 1); + bcmasp_resume_from_wol(intf); + } else { + bcmasp_phy_hw_prepare(intf); + umac_reset_and_init(intf, dev->dev_addr); + } + + bcmasp_netif_init(dev); + + if (wake) { + /* If HW was reset, reprogram the unimac/PHY before resuming + * link status tracking to avoid racing the state machine. + */ + if (!(reg & UMC_CMD_RX_EN)) + bcmasp_adj_link(dev); + + /* Resume link status tracking */ + mutex_lock(&dev->phydev->lock); + dev->phydev->state = dev->phydev->link ? PHY_RUNNING : PHY_NOLINK; + mutex_unlock(&dev->phydev->lock); + phy_trigger_machine(dev->phydev); + } else { + phy_start(dev->phydev); + } netif_device_attach(dev); return 0; - -out: - clk_disable_unprepare(intf->parent->clk); - return ret; } diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 90df02e0039c..f994636fbd5f 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2031,11 +2031,11 @@ static int b44_set_pauseparam(struct net_device *dev, static void b44_get_strings(struct net_device *dev, u32 stringset, u8 *data) { - switch(stringset) { - case ETH_SS_STATS: - memcpy(data, *b44_gstrings, sizeof(b44_gstrings)); - break; - } + if (stringset != ETH_SS_STATS) + return; + + for (int i = 0; i < ARRAY_SIZE(b44_gstrings); i++) + ethtool_puts(&data, b44_gstrings[i]); } static int b44_get_sset_count(struct net_device *dev, int sset) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index f5722e929833..3e2fd2939e87 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -115,29 +115,45 @@ static struct { }; static const struct pci_device_id bnx2_pci_tbl[] = { - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, - PCI_VENDOR_ID_HP, 0x3101, 0, 0, NC370T }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, - PCI_VENDOR_ID_HP, 0x3106, 0, 0, NC370I }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706 }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708 }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, - PCI_VENDOR_ID_HP, 0x3102, 0, 0, NC370F }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706S }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708S }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709 }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S }, - { PCI_VENDOR_ID_BROADCOM, 0x163b, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716 }, - { PCI_VENDOR_ID_BROADCOM, 0x163c, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716S }, - { 0, } + { + PCI_VDEVICE_SUB(BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_VENDOR_ID_HP, 0x3101), + .driver_data = NC370T, + }, { + PCI_VDEVICE_SUB(BROADCOM, PCI_DEVICE_ID_NX2_5706, + PCI_VENDOR_ID_HP, 0x3106), + .driver_data = NC370I, + }, { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5706), + .driver_data = BCM5706, + }, { + PCI_VDEVICE_SUB(BROADCOM, PCI_DEVICE_ID_NX2_5708, + PCI_ANY_ID, PCI_ANY_ID), + .driver_data = BCM5708, + }, { + PCI_VDEVICE_SUB(BROADCOM, PCI_DEVICE_ID_NX2_5706S, + PCI_VENDOR_ID_HP, 0x3102), + .driver_data = NC370F, + }, { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5706S), + .driver_data = BCM5706S, + }, { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5708S), + .driver_data = BCM5708S, + }, { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5709), + .driver_data = BCM5709, + }, { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5709S), + .driver_data = BCM5709S, + }, { + PCI_VDEVICE(BROADCOM, 0x163b), + .driver_data = BCM5716, + }, { + PCI_VDEVICE(BROADCOM, 0x163c), + .driver_data = BCM5716S, + }, + { } }; static const struct flash_spec flash_table[] = diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index da0f8c353e6a..208a894d6190 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -259,31 +259,31 @@ static struct { #endif static const struct pci_device_id bnx2x_pci_tbl[] = { - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57710), BCM57710 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711), BCM57711 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711E), BCM57711E }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712), BCM57712 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_MF), BCM57712_MF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_VF), BCM57712_VF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800), BCM57800 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_MF), BCM57800_MF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_VF), BCM57800_VF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810), BCM57810 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_MF), BCM57810_MF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_O), BCM57840_O }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_4_10), BCM57840_4_10 }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_NX2_57840_4_10), BCM57840_4_10 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_2_20), BCM57840_2_20 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_VF), BCM57810_VF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MFO), BCM57840_MFO }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MF), BCM57840_MF }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_NX2_57840_MF), BCM57840_MF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_VF), BCM57840_VF }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_NX2_57840_VF), BCM57840_VF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811), BCM57811 }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811_MF), BCM57811_MF }, - { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811_VF), BCM57811_VF }, - { 0 } + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57710), .driver_data = BCM57710 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711), .driver_data = BCM57711 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711E), .driver_data = BCM57711E }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712), .driver_data = BCM57712 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_MF), .driver_data = BCM57712_MF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_VF), .driver_data = BCM57712_VF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800), .driver_data = BCM57800 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_MF), .driver_data = BCM57800_MF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_VF), .driver_data = BCM57800_VF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810), .driver_data = BCM57810 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_MF), .driver_data = BCM57810_MF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_O), .driver_data = BCM57840_O }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_4_10), .driver_data = BCM57840_4_10 }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_NX2_57840_4_10), .driver_data = BCM57840_4_10 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_2_20), .driver_data = BCM57840_2_20 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_VF), .driver_data = BCM57810_VF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MFO), .driver_data = BCM57840_MFO }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MF), .driver_data = BCM57840_MF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_NX2_57840_MF), .driver_data = BCM57840_MF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_VF), .driver_data = BCM57840_VF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_NX2_57840_VF), .driver_data = BCM57840_VF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811), .driver_data = BCM57811 }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811_MF), .driver_data = BCM57811_MF }, + { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811_VF), .driver_data = BCM57811_VF }, + { } }; MODULE_DEVICE_TABLE(pci, bnx2x_pci_tbl); @@ -13893,6 +13893,7 @@ static int bnx2x_init_one(struct pci_dev *pdev, rc = bnx2x_init_dev(bp, pdev, dev, ent->driver_data); if (rc < 0) { + pci_set_drvdata(pdev, NULL); free_netdev(dev); return rc; } @@ -13932,13 +13933,13 @@ static int bnx2x_init_one(struct pci_dev *pdev, dev_err(&bp->pdev->dev, "Cannot map doorbell space, aborting\n"); rc = -ENOMEM; - goto init_one_freemem; + goto init_one_vf_pci_dealloc; } if (IS_VF(bp)) { rc = bnx2x_vfpf_acquire(bp, tx_count, rx_count); if (rc) - goto init_one_freemem; + goto init_one_vf_pci_dealloc; #ifdef CONFIG_BNX2X_SRIOV /* VF with OLD Hypervisor or old PF do not support filtering */ @@ -13952,7 +13953,7 @@ static int bnx2x_init_one(struct pci_dev *pdev, /* Enable SRIOV if capability found in configuration space */ rc = bnx2x_iov_init_one(bp, int_mode, BNX2X_MAX_NUM_OF_VFS); if (rc) - goto init_one_freemem; + goto init_one_vfpf_release; /* calc qm_cid_count */ bp->qm_cid_count = bnx2x_set_qm_cid_count(bp); @@ -13971,7 +13972,7 @@ static int bnx2x_init_one(struct pci_dev *pdev, rc = bnx2x_set_int_mode(bp); if (rc) { dev_err(&pdev->dev, "Cannot set interrupts\n"); - goto init_one_freemem; + goto init_one_iov_remove; } BNX2X_DEV_INFO("set interrupts successfully\n"); @@ -13979,7 +13980,7 @@ static int bnx2x_init_one(struct pci_dev *pdev, rc = register_netdev(dev); if (rc) { dev_err(&pdev->dev, "Cannot register net device\n"); - goto init_one_freemem; + goto init_one_disable_msi; } BNX2X_DEV_INFO("device name after netdev register %s\n", dev->name); @@ -14001,6 +14002,20 @@ static int bnx2x_init_one(struct pci_dev *pdev, return 0; +init_one_disable_msi: + bnx2x_disable_msi(bp); + +init_one_iov_remove: + bnx2x_iov_remove_one(bp); + +init_one_vfpf_release: + if (IS_VF(bp)) + bnx2x_vfpf_release(bp); + +init_one_vf_pci_dealloc: + if (IS_VF(bp)) + bnx2x_vf_pci_dealloc(bp); + init_one_freemem: bnx2x_free_mem_bp(bp); @@ -14011,6 +14026,7 @@ static int bnx2x_init_one(struct pci_dev *pdev, if (IS_PF(bp) && bp->doorbells) iounmap(bp->doorbells); + pci_set_drvdata(pdev, NULL); free_netdev(dev); if (atomic_read(&pdev->enable_cnt) == 1) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index c999f9733326..055e93a417b6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1011,6 +1011,7 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, mapping += bp->rx_dma_offset; rx_buf->data = page; rx_buf->data_ptr = page_address(page) + offset + bp->rx_offset; + rx_buf->offset = offset; } else { u8 *data = __bnxt_alloc_rx_frag(bp, &mapping, rxr, gfp); @@ -1019,6 +1020,7 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, rx_buf->data = data; rx_buf->data_ptr = data + bp->rx_offset; + rx_buf->offset = 0; } rx_buf->mapping = mapping; @@ -1040,6 +1042,7 @@ void bnxt_reuse_rx_data(struct bnxt_rx_ring_info *rxr, u16 cons, void *data) prod_rx_buf->data_ptr = cons_rx_buf->data_ptr; prod_rx_buf->mapping = cons_rx_buf->mapping; + prod_rx_buf->offset = cons_rx_buf->offset; prod_bd = &rxr->rx_desc_ring[RX_RING(bp, prod)][RX_IDX(prod)]; cons_bd = &rxr->rx_desc_ring[RX_RING(bp, cons)][RX_IDX(cons)]; @@ -1175,8 +1178,11 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, struct page *page = data; u16 prod = rxr->rx_prod; struct sk_buff *skb; + void *frag_start; int err; + frag_start = page_address(page) + rxr->rx_buf_ring[cons].offset; + err = bnxt_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC); if (unlikely(err)) { bnxt_reuse_rx_data(rxr, cons, data); @@ -1185,13 +1191,13 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, dma_addr -= bp->rx_dma_offset; dma_sync_single_for_cpu(&bp->pdev->dev, dma_addr, rxr->rx_page_size, bp->rx_dir); - skb = napi_build_skb(data_ptr - bp->rx_offset, rxr->rx_page_size); + skb = napi_build_skb(frag_start, rxr->rx_page_size); if (!skb) { page_pool_recycle_direct(rxr->page_pool, page); return NULL; } skb_mark_for_recycle(skb); - skb_reserve(skb, bp->rx_offset); + skb_reserve(skb, data_ptr - (u8 *)frag_start); __skb_put(skb, len); return skb; @@ -4846,11 +4852,11 @@ static void bnxt_init_ring_params(struct bnxt *bp) { unsigned int rx_size; - bp->rx_copybreak = BNXT_DEFAULT_RX_COPYBREAK; + bp->rx_copybreak = 0; /* rx-copybreak disabled by default */ /* Try to fit 4 chunks into a 4k page */ rx_size = SZ_1K - NET_SKB_PAD - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - bp->dev->cfg->hds_thresh = max(BNXT_DEFAULT_RX_COPYBREAK, rx_size); + bp->dev->cfg->hds_thresh = max(BNXT_MIN_RX_HDR_BUF, rx_size); } /* bp->rx_ring_size, bp->tx_ring_size, dev->mtu, BNXT_FLAG_{G|L}RO flags must @@ -4911,7 +4917,7 @@ void bnxt_set_ring_params(struct bnxt *bp) ALIGN(max(NET_SKB_PAD, XDP_PACKET_HEADROOM), 8) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); } else { - rx_size = max3(BNXT_DEFAULT_RX_COPYBREAK, + rx_size = max3(BNXT_MIN_RX_HDR_BUF, bp->rx_copybreak, bp->dev->cfg_pending->hds_thresh); rx_size = SKB_DATA_ALIGN(rx_size + NET_IP_ALIGN); @@ -11232,8 +11238,12 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) } rc = bnxt_cfg_rx_mode(bp, &bp->dev->uc, true); - if (rc) + if (rc == -EAGAIN) { + netif_rx_mode_schedule_retry(bp->dev); + rc = 0; + } else if (rc) { goto err_out; + } skip_rx_mask: rc = bnxt_hwrm_set_coal(bp); @@ -13679,9 +13689,9 @@ static bool bnxt_uc_list_updated(struct bnxt *bp, return false; } -static void bnxt_set_rx_mode(struct net_device *dev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int bnxt_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct bnxt *bp = netdev_priv(dev); struct bnxt_vnic_info *vnic; @@ -13690,7 +13700,7 @@ static void bnxt_set_rx_mode(struct net_device *dev, u32 mask; if (!test_bit(BNXT_STATE_OPEN, &bp->state)) - return; + return 0; vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; mask = vnic->rx_mask; @@ -13716,8 +13726,10 @@ static void bnxt_set_rx_mode(struct net_device *dev, if (mask != vnic->rx_mask || uc_update || mc_update) { vnic->rx_mask = mask; - bnxt_cfg_rx_mode(bp, uc, uc_update); + return bnxt_cfg_rx_mode(bp, uc, uc_update); } + + return 0; } static int bnxt_cfg_rx_mode(struct bnxt *bp, struct netdev_hw_addr_list *uc, @@ -13756,11 +13768,10 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp, struct netdev_hw_addr_list *uc, rc = bnxt_hwrm_set_vnic_filter(bp, 0, i, vnic->uc_list + off); if (rc) { if (BNXT_VF(bp) && rc == -ENODEV) { - if (!test_and_set_bit(BNXT_STATE_L2_FILTER_RETRY, &bp->state)) - netdev_warn(bp->dev, "Cannot configure L2 filters while PF is unavailable, will retry\n"); - else - netdev_dbg(bp->dev, "PF still unavailable while configuring L2 filters.\n"); - rc = 0; + netdev_warn(bp->dev, "Cannot configure L2 filters while PF is unavailable, will retry\n"); + rc = -EAGAIN; + } else if (rc == -EAGAIN) { + netdev_warn(bp->dev, "FW busy while setting vnic filter, will retry\n"); } else { netdev_err(bp->dev, "HWRM vnic filter failure rc: %x\n", rc); } @@ -13768,8 +13779,6 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp, struct netdev_hw_addr_list *uc, return rc; } } - if (test_and_clear_bit(BNXT_STATE_L2_FILTER_RETRY, &bp->state)) - netdev_notice(bp->dev, "Retry of L2 filter configuration successful.\n"); skip_uc: if ((vnic->rx_mask & CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS) && @@ -14360,9 +14369,6 @@ static void bnxt_timer(struct timer_list *t) } } - if (test_bit(BNXT_STATE_L2_FILTER_RETRY, &bp->state)) - bnxt_queue_sp_work(bp, BNXT_RX_MASK_SP_EVENT); - if ((BNXT_CHIP_P5(bp)) && !bp->chip_rev && netif_carrier_ok(dev)) bnxt_queue_sp_work(bp, BNXT_RING_COAL_NOW_SP_EVENT); @@ -14725,7 +14731,6 @@ static void bnxt_ulp_restart(struct bnxt *bp) static void bnxt_sp_task(struct work_struct *work) { struct bnxt *bp = container_of(work, struct bnxt, sp_task); - struct net_device *dev = bp->dev; set_bit(BNXT_STATE_IN_SP_TASK, &bp->state); smp_mb__after_atomic(); @@ -14803,13 +14808,6 @@ static void bnxt_sp_task(struct work_struct *work) /* These functions below will clear BNXT_STATE_IN_SP_TASK. They * must be the last functions to be called before exiting. */ - if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event)) { - bnxt_lock_sp(bp); - if (test_bit(BNXT_STATE_OPEN, &bp->state)) - bnxt_cfg_rx_mode(bp, &dev->uc, true); - bnxt_unlock_sp(bp); - } - if (test_and_clear_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event)) bnxt_reset(bp, false); @@ -16780,7 +16778,7 @@ int bnxt_restore_pf_fw_resources(struct bnxt *bp) { int rc; - netdev_ops_assert_locked(bp->dev); + netdev_assert_locked_ops_compat(bp->dev); bnxt_hwrm_func_qcaps(bp); if (netif_running(bp->dev)) @@ -17141,7 +17139,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->queue_mgmt_ops = &bnxt_queue_mgmt_ops_unsupp; if (BNXT_SUPPORTS_QUEUE_API(bp)) dev->queue_mgmt_ops = &bnxt_queue_mgmt_ops; - dev->netmem_tx = true; + dev->netmem_tx = NETMEM_TX_DMA; rc = register_netdev(dev); if (rc) @@ -17414,7 +17412,6 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) pci_write_config_dword(bp->pdev, off, 0); } pci_restore_state(pdev); - pci_save_state(pdev); bnxt_inv_fw_health_reg(bp); bnxt_try_map_fw_health_reg(bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 61c847b36b9f..6d312259f852 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -36,8 +36,8 @@ #include #endif -#define BNXT_DEFAULT_RX_COPYBREAK 256 -#define BNXT_MAX_RX_COPYBREAK 1024 +#define BNXT_MIN_RX_HDR_BUF 256 +#define BNXT_MAX_RX_HDR_BUF 1024 extern struct list_head bnxt_block_cb_list; @@ -911,6 +911,7 @@ struct bnxt_sw_rx_bd { void *data; u8 *data_ptr; dma_addr_t mapping; + unsigned int offset; }; struct bnxt_sw_rx_agg_bd { @@ -2467,7 +2468,6 @@ struct bnxt { #define BNXT_STATE_DRV_REGISTERED 7 #define BNXT_STATE_PCI_CHANNEL_IO_FROZEN 8 #define BNXT_STATE_NAPI_DISABLED 9 -#define BNXT_STATE_L2_FILTER_RETRY 10 #define BNXT_STATE_FW_ACTIVATE 11 #define BNXT_STATE_RECOVER 12 #define BNXT_STATE_FW_NON_FATAL_COND 13 @@ -2622,7 +2622,6 @@ struct bnxt { struct work_struct sp_task; unsigned long sp_event; -#define BNXT_RX_MASK_SP_EVENT 0 #define BNXT_RX_NTP_FLTR_SP_EVENT 1 #define BNXT_LINK_CHNG_SP_EVENT 2 #define BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT 3 diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 835f2b413931..eb17a3454b4c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -1123,7 +1123,7 @@ static int bnxt_dl_nvm_param_set(struct devlink *dl, u32 id, } static int bnxt_dl_roce_validate(struct devlink *dl, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { const struct bnxt_dl_nvm_param nvm_roce_cap = {0, NVM_OFF_RDMA_CAPABLE, @@ -1149,7 +1149,7 @@ static int bnxt_dl_roce_validate(struct devlink *dl, u32 id, } static int bnxt_dl_msix_validate(struct devlink *dl, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { int max_val = -1; @@ -1160,7 +1160,7 @@ static int bnxt_dl_msix_validate(struct devlink *dl, u32 id, if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN) max_val = BNXT_MSIX_VEC_MIN_MAX; - if (val.vu32 > max_val) { + if (val->vu32 > max_val) { NL_SET_ERR_MSG_MOD(extack, "MSIX value is exceeding the range"); return -EINVAL; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 9b14134d62d2..56d74a3c24b7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -4594,7 +4594,7 @@ static int bnxt_set_tunable(struct net_device *dev, switch (tuna->id) { case ETHTOOL_RX_COPYBREAK: rx_copybreak = *(u32 *)data; - if (rx_copybreak > BNXT_MAX_RX_COPYBREAK) + if (rx_copybreak > BNXT_MAX_RX_HDR_BUF) return -ERANGE; if (rx_copybreak != bp->rx_copybreak) { if (netif_running(dev)) @@ -5172,7 +5172,7 @@ static int bnxt_run_loopback(struct bnxt *bp) cpr = &rxr->bnapi->cp_ring; if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) cpr = rxr->rx_cpr; - pkt_size = min(bp->dev->mtu + ETH_HLEN, max(BNXT_DEFAULT_RX_COPYBREAK, + pkt_size = min(bp->dev->mtu + ETH_HLEN, max(BNXT_MIN_RX_HDR_BUF, bp->rx_copybreak)); skb = netdev_alloc_skb(bp->dev, pkt_size); if (!skb) @@ -5729,6 +5729,10 @@ const struct ethtool_ops bnxt_ethtool_ops = { .rxfh_max_num_contexts = BNXT_MAX_ETH_RSS_CTX + 1, .rxfh_indir_space = BNXT_MAX_RSS_TABLE_ENTRIES_P5, .rxfh_priv_size = sizeof(struct bnxt_rss_ctx), + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM | + ETHTOOL_OP_NEEDS_RTNL_SCOALESCE | + ETHTOOL_OP_NEEDS_RTNL_RSS, .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USECS_IRQ | diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 7c11cf916762..a2305e6428d1 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -40,9 +40,8 @@ #include "bcmgenet.h" -/* Default highest priority queue for multi queue support */ -#define GENET_Q1_PRIORITY 0 -#define GENET_Q0_PRIORITY 1 +#define GENET_Q0_WEIGHT 1 +#define GENET_Q1_WEIGHT 4 #define GENET_Q0_RX_BD_CNT \ (TOTAL_DESC - priv->hw_params->rx_queues * priv->hw_params->rx_bds_per_q) @@ -52,6 +51,12 @@ #define RX_BUF_LENGTH 2048 #define SKB_ALIGNMENT 32 +/* Page pool RX buffer layout: + * RSB(64) + pad(2) | frame data | skb_shared_info + * The HW writes the 64B RSB + 2B alignment padding before the frame. + */ +#define GENET_RSB_PAD (sizeof(struct status_64) + 2) + /* Tx/Rx DMA register offset, skip 256 descriptors */ #define WORDS_PER_BD(p) (p->hw_params->words_per_bd) #define DMA_DESC_SIZE (WORDS_PER_BD(priv) * sizeof(u32)) @@ -1153,7 +1158,6 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { UMAC_RBUF_ERR_CNT_V1), STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT), STAT_GENET_SOFT_MIB("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), - STAT_GENET_SOFT_MIB("rx_dma_failed", mib.rx_dma_failed), STAT_GENET_SOFT_MIB("tx_dma_failed", mib.tx_dma_failed), STAT_GENET_SOFT_MIB("tx_realloc_tsb", mib.tx_realloc_tsb), STAT_GENET_SOFT_MIB("tx_realloc_tsb_failed", @@ -1894,21 +1898,13 @@ static struct sk_buff *bcmgenet_free_tx_cb(struct device *dev, } /* Simple helper to free a receive control block's resources */ -static struct sk_buff *bcmgenet_free_rx_cb(struct device *dev, - struct enet_cb *cb) +static void bcmgenet_free_rx_cb(struct enet_cb *cb, + struct page_pool *pool) { - struct sk_buff *skb; - - skb = cb->skb; - cb->skb = NULL; - - if (dma_unmap_addr(cb, dma_addr)) { - dma_unmap_single(dev, dma_unmap_addr(cb, dma_addr), - dma_unmap_len(cb, dma_len), DMA_FROM_DEVICE); - dma_unmap_addr_set(cb, dma_addr, 0); + if (cb->rx_page) { + page_pool_put_full_page(pool, cb->rx_page, false); + cb->rx_page = NULL; } - - return skb; } /* Unlocked version of the reclaim routine */ @@ -2129,13 +2125,6 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) int i; index = skb_get_queue_mapping(skb); - /* Mapping strategy: - * queue_mapping = 0, unclassified, packet xmited through ring 0 - * queue_mapping = 1, goes to ring 1. (highest priority queue) - * queue_mapping = 2, goes to ring 2. - * queue_mapping = 3, goes to ring 3. - * queue_mapping = 4, goes to ring 4. - */ ring = &priv->tx_rings[index]; txq = netdev_get_tx_queue(dev, index); @@ -2249,46 +2238,29 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) goto out; } -static struct sk_buff *bcmgenet_rx_refill(struct bcmgenet_priv *priv, - struct enet_cb *cb) +static int bcmgenet_rx_refill(struct bcmgenet_rx_ring *ring, + struct enet_cb *cb) { - struct device *kdev = &priv->pdev->dev; - struct sk_buff *skb; - struct sk_buff *rx_skb; + struct bcmgenet_priv *priv = ring->priv; dma_addr_t mapping; + struct page *page; - /* Allocate a new Rx skb */ - skb = __netdev_alloc_skb(priv->dev, priv->rx_buf_len + SKB_ALIGNMENT, - GFP_ATOMIC | __GFP_NOWARN); - if (!skb) { + page = page_pool_alloc_pages(ring->page_pool, + GFP_ATOMIC); + if (!page) { priv->mib.alloc_rx_buff_failed++; netif_err(priv, rx_err, priv->dev, - "%s: Rx skb allocation failed\n", __func__); - return NULL; + "%s: Rx page allocation failed\n", __func__); + return -ENOMEM; } - /* DMA-map the new Rx skb */ - mapping = dma_map_single(kdev, skb->data, priv->rx_buf_len, - DMA_FROM_DEVICE); - if (dma_mapping_error(kdev, mapping)) { - priv->mib.rx_dma_failed++; - dev_kfree_skb_any(skb); - netif_err(priv, rx_err, priv->dev, - "%s: Rx skb DMA mapping failed\n", __func__); - return NULL; - } + /* page_pool handles DMA mapping via PP_FLAG_DMA_MAP */ + mapping = page_pool_get_dma_addr(page); - /* Grab the current Rx skb from the ring and DMA-unmap it */ - rx_skb = bcmgenet_free_rx_cb(kdev, cb); - - /* Put the new Rx skb on the ring */ - cb->skb = skb; - dma_unmap_addr_set(cb, dma_addr, mapping); - dma_unmap_len_set(cb, dma_len, priv->rx_buf_len); + cb->rx_page = page; dmadesc_set_addr(priv, cb->bd_addr, mapping); - /* Return the current Rx skb to caller */ - return rx_skb; + return 0; } /* bcmgenet_desc_rx - descriptor based rx process. @@ -2340,25 +2312,29 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, while ((rxpktprocessed < rxpkttoprocess) && (rxpktprocessed < budget)) { struct status_64 *status; + struct page *rx_page; + void *hard_start; __be16 rx_csum; cb = &priv->rx_cbs[ring->read_ptr]; - skb = bcmgenet_rx_refill(priv, cb); - if (unlikely(!skb)) { + /* Save the received page before refilling */ + rx_page = cb->rx_page; + + if (bcmgenet_rx_refill(ring, cb)) { BCMGENET_STATS64_INC(stats, dropped); goto next; } - status = (struct status_64 *)skb->data; + /* Sync the full buffer; the HW may have written anywhere + * up to RX_BUF_LENGTH. + */ + page_pool_dma_sync_for_cpu(ring->page_pool, rx_page, 0, + RX_BUF_LENGTH); + + hard_start = page_address(rx_page); + status = (struct status_64 *)hard_start; dma_length_status = status->length_status; - if (dev->features & NETIF_F_RXCSUM) { - rx_csum = (__force __be16)(status->rx_csum & 0xffff); - if (rx_csum) { - skb->csum = (__force __wsum)ntohs(rx_csum); - skb->ip_summed = CHECKSUM_COMPLETE; - } - } /* DMA flags and length are still valid no matter how * we got the Receive Status Vector (64B RSB or register) @@ -2371,10 +2347,13 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, __func__, p_index, ring->c_index, ring->read_ptr, dma_length_status); - if (unlikely(len > RX_BUF_LENGTH)) { - netif_err(priv, rx_status, dev, "oversized packet\n"); + /* Reject lengths that would underflow the SKB build path. */ + if (unlikely(len > RX_BUF_LENGTH || len < GENET_RSB_PAD)) { + netif_err(priv, rx_status, dev, + "invalid packet length %d\n", len); BCMGENET_STATS64_INC(stats, length_errors); - dev_kfree_skb_any(skb); + page_pool_put_full_page(ring->page_pool, rx_page, + true); goto next; } @@ -2382,7 +2361,8 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, netif_err(priv, rx_status, dev, "dropping fragmented packet!\n"); BCMGENET_STATS64_INC(stats, fragmented_errors); - dev_kfree_skb_any(skb); + page_pool_put_full_page(ring->page_pool, rx_page, + true); goto next; } @@ -2410,21 +2390,42 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, DMA_RX_RXER)) == DMA_RX_RXER) u64_stats_inc(&stats->errors); u64_stats_update_end(&stats->syncp); - dev_kfree_skb_any(skb); + page_pool_put_full_page(ring->page_pool, rx_page, + true); goto next; } /* error packet */ - skb_put(skb, len); - - /* remove RSB and hardware 2bytes added for IP alignment */ - skb_pull(skb, 66); - len -= 66; - - if (priv->crc_fwd_en) { - skb_trim(skb, len - ETH_FCS_LEN); - len -= ETH_FCS_LEN; + /* Build SKB from the page - data starts at hard_start, + * frame begins after RSB(64) + pad(2) = 66 bytes. + */ + skb = napi_build_skb(hard_start, PAGE_SIZE); + if (unlikely(!skb)) { + BCMGENET_STATS64_INC(stats, dropped); + page_pool_put_full_page(ring->page_pool, rx_page, + true); + goto next; } + skb_mark_for_recycle(skb); + + /* Reserve the RSB + pad, then set the data length */ + skb_reserve(skb, GENET_RSB_PAD); + __skb_put(skb, len - GENET_RSB_PAD); + + if (priv->crc_fwd_en) { + skb_trim(skb, skb->len - ETH_FCS_LEN); + } + + /* Set up checksum offload */ + if (dev->features & NETIF_F_RXCSUM) { + rx_csum = (__force __be16)(status->rx_csum & 0xffff); + if (rx_csum) { + skb->csum = (__force __wsum)ntohs(rx_csum); + skb->ip_summed = CHECKSUM_COMPLETE; + } + } + + len = skb->len; bytes_processed += len; /*Finish setting up the received SKB and send it to the kernel*/ @@ -2496,12 +2497,11 @@ static void bcmgenet_dim_work(struct work_struct *work) dim->state = DIM_START_MEASURE; } -/* Assign skb to RX DMA descriptor. */ +/* Assign page_pool pages to RX DMA descriptors. */ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv, struct bcmgenet_rx_ring *ring) { struct enet_cb *cb; - struct sk_buff *skb; int i; netif_dbg(priv, hw, priv->dev, "%s\n", __func__); @@ -2509,10 +2509,7 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv, /* loop here for each buffer needing assign */ for (i = 0; i < ring->size; i++) { cb = ring->cbs + i; - skb = bcmgenet_rx_refill(priv, cb); - if (skb) - dev_consume_skb_any(skb); - if (!cb->skb) + if (bcmgenet_rx_refill(ring, cb)) return -ENOMEM; } @@ -2521,16 +2518,18 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv, static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv) { - struct sk_buff *skb; + struct bcmgenet_rx_ring *ring; struct enet_cb *cb; - int i; + int q, i; - for (i = 0; i < priv->num_rx_bds; i++) { - cb = &priv->rx_cbs[i]; - - skb = bcmgenet_free_rx_cb(&priv->pdev->dev, cb); - if (skb) - dev_consume_skb_any(skb); + for (q = 0; q <= priv->hw_params->rx_queues; q++) { + ring = &priv->rx_rings[q]; + if (!ring->page_pool) + continue; + for (i = 0; i < ring->size; i++) { + cb = ring->cbs + i; + bcmgenet_free_rx_cb(cb, ring->page_pool); + } } } @@ -2748,6 +2747,30 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, netif_napi_add_tx(priv->dev, &ring->napi, bcmgenet_tx_poll); } +static int bcmgenet_rx_ring_create_pool(struct bcmgenet_priv *priv, + struct bcmgenet_rx_ring *ring) +{ + struct page_pool_params pp_params = { + .order = 0, + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .pool_size = ring->size, + .nid = NUMA_NO_NODE, + .dev = &priv->pdev->dev, + .dma_dir = DMA_FROM_DEVICE, + .max_len = RX_BUF_LENGTH, + }; + int err; + + ring->page_pool = page_pool_create(&pp_params); + if (IS_ERR(ring->page_pool)) { + err = PTR_ERR(ring->page_pool); + ring->page_pool = NULL; + return err; + } + + return 0; +} + /* Initialize a RDMA ring */ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, unsigned int index, unsigned int size, @@ -2755,7 +2778,7 @@ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, { struct bcmgenet_rx_ring *ring = &priv->rx_rings[index]; u32 words_per_bd = WORDS_PER_BD(priv); - int ret; + int ret, i; ring->priv = priv; ring->index = index; @@ -2766,10 +2789,19 @@ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, ring->cb_ptr = start_ptr; ring->end_ptr = end_ptr - 1; - ret = bcmgenet_alloc_rx_buffers(priv, ring); + ret = bcmgenet_rx_ring_create_pool(priv, ring); if (ret) return ret; + ret = bcmgenet_alloc_rx_buffers(priv, ring); + if (ret) { + for (i = 0; i < ring->size; i++) + bcmgenet_free_rx_cb(ring->cbs + i, ring->page_pool); + page_pool_destroy(ring->page_pool); + ring->page_pool = NULL; + return ret; + } + bcmgenet_init_dim(ring, bcmgenet_dim_work); bcmgenet_init_rx_coalesce(ring); @@ -2881,8 +2913,9 @@ static int bcmgenet_rdma_disable(struct bcmgenet_priv *priv) /* Initialize Tx queues * - * Queues 1-4 are priority-based, each one has 32 descriptors, - * with queue 1 being the highest priority queue. + * Queues 1-4 are the priority queues, each one has 32 descriptors. + * The weighted round-robin arbiter gives them a larger share of TX + * bandwidth than the default queue 0. * * Queue 0 is the default Tx queue with * GENET_Q0_TX_BD_CNT = 256 - 4 * 32 = 128 descriptors. @@ -2900,8 +2933,8 @@ static void bcmgenet_init_tx_queues(struct net_device *dev) unsigned int start = 0, end = GENET_Q0_TX_BD_CNT; u32 i, ring_mask, dma_priority[3] = {0, 0, 0}; - /* Enable strict priority arbiter mode */ - bcmgenet_tdma_writel(priv, DMA_ARBITER_SP, DMA_ARB_CTRL); + /* Enable Weighted Round-Robin arbiter mode */ + bcmgenet_tdma_writel(priv, DMA_ARBITER_WRR, DMA_ARB_CTRL); /* Initialize Tx priority queues */ for (i = 0; i <= priv->hw_params->tx_queues; i++) { @@ -2909,7 +2942,7 @@ static void bcmgenet_init_tx_queues(struct net_device *dev) start = end; end += priv->hw_params->tx_bds_per_q; dma_priority[DMA_PRIO_REG_INDEX(i)] |= - (i ? GENET_Q1_PRIORITY : GENET_Q0_PRIORITY) + (i ? GENET_Q1_WEIGHT : GENET_Q0_WEIGHT) << DMA_PRIO_REG_SHIFT(i); } @@ -2962,6 +2995,20 @@ static void bcmgenet_fini_rx_napi(struct bcmgenet_priv *priv) } } +static void bcmgenet_destroy_rx_page_pools(struct bcmgenet_priv *priv) +{ + struct bcmgenet_rx_ring *ring; + unsigned int i; + + for (i = 0; i <= priv->hw_params->rx_queues; ++i) { + ring = &priv->rx_rings[i]; + if (ring->page_pool) { + page_pool_destroy(ring->page_pool); + ring->page_pool = NULL; + } + } +} + /* Initialize Rx queues * * Queues 0-15 are priority queues. Hardware Filtering Block (HFB) can be @@ -3033,6 +3080,7 @@ static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) } bcmgenet_free_rx_buffers(priv); + bcmgenet_destroy_rx_page_pools(priv); kfree(priv->rx_cbs); kfree(priv->tx_cbs); } @@ -3109,6 +3157,7 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv, bool flush_rx) if (ret) { netdev_err(priv->dev, "failed to initialize Rx queues\n"); bcmgenet_free_rx_buffers(priv); + bcmgenet_destroy_rx_page_pools(priv); kfree(priv->rx_cbs); kfree(priv->tx_cbs); return ret; @@ -4026,8 +4075,6 @@ static int bcmgenet_probe(struct platform_device *pdev) /* Mii wait queue */ init_waitqueue_head(&priv->wq); - /* Always use RX_BUF_LENGTH (2KB) buffer for all chips */ - priv->rx_buf_len = RX_BUF_LENGTH; INIT_WORK(&priv->bcmgenet_irq_work, bcmgenet_irq_task); priv->clk_wol = devm_clk_get_optional(&priv->pdev->dev, "enet-wol"); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 9e4110c7fdf6..22a958ba9902 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "../unimac.h" @@ -149,7 +150,6 @@ struct bcmgenet_mib_counters { u32 rbuf_err_cnt; u32 mdf_err_cnt; u32 alloc_rx_buff_failed; - u32 rx_dma_failed; u32 tx_dma_failed; u32 tx_realloc_tsb; u32 tx_realloc_tsb_failed; @@ -469,6 +469,7 @@ struct bcmgenet_rx_stats64 { struct enet_cb { struct sk_buff *skb; + struct page *rx_page; void __iomem *bd_addr; DEFINE_DMA_UNMAP_ADDR(dma_addr); DEFINE_DMA_UNMAP_LEN(dma_len); @@ -575,6 +576,7 @@ struct bcmgenet_rx_ring { struct bcmgenet_net_dim dim; u32 rx_max_coalesced_frames; u32 rx_coalesce_usecs; + struct page_pool *page_pool; struct bcmgenet_priv *priv; }; @@ -609,7 +611,6 @@ struct bcmgenet_priv { void __iomem *rx_bds; struct enet_cb *rx_cbs; unsigned int num_rx_bds; - unsigned int rx_buf_len; struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES]; struct list_head rxnfc_list; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index c1a3df225254..0db08ac3d098 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -404,17 +404,15 @@ static const struct pci_error_handlers liquidio_err_handler = { static const struct pci_device_id liquidio_pci_tbl[] = { { /* 68xx */ - PCI_VENDOR_ID_CAVIUM, 0x91, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 + PCI_VDEVICE(CAVIUM, 0x0091) }, { /* 66xx */ - PCI_VENDOR_ID_CAVIUM, 0x92, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 + PCI_VDEVICE(CAVIUM, 0x0092) }, { /* 23xx pf */ - PCI_VENDOR_ID_CAVIUM, 0x9702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 + PCI_VDEVICE(CAVIUM, 0x9702) }, - { - 0, 0, 0, 0, 0, 0, 0 - } + { } }; MODULE_DEVICE_TABLE(pci, liquidio_pci_tbl); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 43c595f3b84e..36255352e7ea 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -237,12 +237,9 @@ static const struct pci_error_handlers liquidio_vf_err_handler = { static const struct pci_device_id liquidio_vf_pci_tbl[] = { { - PCI_VENDOR_ID_CAVIUM, OCTEON_CN23XX_VF_VID, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 + PCI_VDEVICE(CAVIUM, OCTEON_CN23XX_VF_VID), }, - { - 0, 0, 0, 0, 0, 0, 0 - } + { } }; MODULE_DEVICE_TABLE(pci, liquidio_vf_pci_tbl); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 1c183827cb9e..b9ade881c81c 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -2109,8 +2109,10 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } err = pci_enable_device(pdev); - if (err) - return dev_err_probe(dev, err, "Failed to enable PCI device\n"); + if (err) { + err = dev_err_probe(dev, err, "Failed to enable PCI device\n"); + goto err_put_ptp; + } err = pci_request_regions(pdev, DRV_NAME); if (err) { @@ -2264,6 +2266,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_release_regions(pdev); err_disable_device: pci_disable_device(pdev); +err_put_ptp: + cavium_ptp_put(ptp_clock); return err; } diff --git a/drivers/net/ethernet/chelsio/cxgb/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h index 304bb282ab03..292f8ec248e2 100644 --- a/drivers/net/ethernet/chelsio/cxgb/common.h +++ b/drivers/net/ethernet/chelsio/cxgb/common.h @@ -48,7 +48,7 @@ #define DRV_NAME "cxgb" #define CH_DEVICE(devid, ssid, idx) \ - { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, ssid, 0, 0, idx } + { PCI_VDEVICE_SUB(CHELSIO, devid, PCI_ANY_ID, ssid), .driver_data = idx } #define SUPPORTED_PAUSE (1 << 13) #define SUPPORTED_LOOPBACK (1 << 15) diff --git a/drivers/net/ethernet/chelsio/cxgb/subr.c b/drivers/net/ethernet/chelsio/cxgb/subr.c index 367a9e4581d5..d4488e400bc6 100644 --- a/drivers/net/ethernet/chelsio/cxgb/subr.c +++ b/drivers/net/ethernet/chelsio/cxgb/subr.c @@ -533,7 +533,7 @@ const struct pci_device_id t1_pci_tbl[] = { CH_DEVICE(11, 1, CH_BRD_T210_1F), CH_DEVICE(14, 1, CH_BRD_T210_1CU), CH_DEVICE(16, 1, CH_BRD_N204_4CU), - { 0 } + { } }; MODULE_DEVICE_TABLE(pci, t1_pci_tbl); diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 88b6d76817e9..3bee82bc0680 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -83,7 +83,7 @@ enum { #define EEPROM_MAGIC 0x38E2F10C #define CH_DEVICE(devid, idx) \ - { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, PCI_ANY_ID, 0, 0, idx } + { PCI_VDEVICE(CHELSIO, devid), .driver_data = idx } static const struct pci_device_id cxgb3_pci_tbl[] = { CH_DEVICE(0x20, 0), /* PE9000 */ @@ -99,7 +99,7 @@ static const struct pci_device_id cxgb3_pci_tbl[] = { CH_DEVICE(0x35, 6), /* T3C20-derived T3C10 */ CH_DEVICE(0x36, 3), /* S320E-CR */ CH_DEVICE(0x37, 7), /* N320E-G2 */ - {0,} + { } }; MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 10694d5924ee..9e2c2fa16d7a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -112,10 +112,10 @@ char cxgb4_driver_name[] = KBUILD_MODNAME; #define CH_PCI_DEVICE_ID_FUNCTION2 0x0 #define CH_PCI_ID_TABLE_ENTRY(devid) \ - {PCI_VDEVICE(CHELSIO, (devid)), CXGB4_UNIFIED_PF} + { PCI_VDEVICE(CHELSIO, (devid)), .driver_data = CXGB4_UNIFIED_PF } #define CH_PCI_DEVICE_ID_TABLE_DEFINE_END \ - { 0, } \ + { } \ } #include "t4_pci_id_tbl.h" @@ -6795,8 +6795,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_TSO | NETIF_F_TSO6; netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_HW_TLS_RECORD; + NETIF_F_GSO_UDP_TUNNEL_CSUM; if (adapter->rawf_cnt) netdev->udp_tunnel_nic_info = &cxgb_udp_tunnels; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 9eaa9aadee33..37b53cc64f39 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -3388,9 +3388,9 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev) #define CH_PCI_DEVICE_ID_FUNCTION 0x8 #define CH_PCI_ID_TABLE_ENTRY(devid) \ - { PCI_VDEVICE(CHELSIO, (devid)), 0 } + { PCI_VDEVICE(CHELSIO, (devid)) } -#define CH_PCI_DEVICE_ID_TABLE_DEFINE_END { 0, } } +#define CH_PCI_DEVICE_ID_TABLE_DEFINE_END { } } #include "../cxgb4/t4_pci_id_tbl.h" diff --git a/drivers/net/ethernet/chelsio/inline_crypto/Kconfig b/drivers/net/ethernet/chelsio/inline_crypto/Kconfig index 521955e1f894..b7d452f4a7f1 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/Kconfig +++ b/drivers/net/ethernet/chelsio/inline_crypto/Kconfig @@ -13,18 +13,6 @@ config CHELSIO_INLINE_CRYPTO if CHELSIO_INLINE_CRYPTO -config CRYPTO_DEV_CHELSIO_TLS - tristate "Chelsio Crypto Inline TLS Driver" - depends on CHELSIO_T4 - depends on TLS - depends on TLS_TOE - help - Support Chelsio Inline TLS with Chelsio crypto accelerator. - Enable inline TLS support for Tx and Rx. - - To compile this driver as a module, choose M here: the module - will be called chtls. - config CHELSIO_IPSEC_INLINE tristate "Chelsio IPSec XFRM Tx crypto offload" depends on CHELSIO_T4 diff --git a/drivers/net/ethernet/chelsio/inline_crypto/Makefile b/drivers/net/ethernet/chelsio/inline_crypto/Makefile index 27e6d7e2f1eb..ca6548adc6a7 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/Makefile +++ b/drivers/net/ethernet/chelsio/inline_crypto/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_CRYPTO_DEV_CHELSIO_TLS) += chtls/ obj-$(CONFIG_CHELSIO_IPSEC_INLINE) += ch_ipsec/ obj-$(CONFIG_CHELSIO_TLS_DEVICE) += ch_ktls/ diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/Makefile b/drivers/net/ethernet/chelsio/inline_crypto/chtls/Makefile deleted file mode 100644 index bc11495acdb3..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -ccflags-y := -I $(srctree)/drivers/net/ethernet/chelsio/cxgb4 \ - -I $(srctree)/drivers/crypto/chelsio - -obj-$(CONFIG_CRYPTO_DEV_CHELSIO_TLS) += chtls.o -chtls-objs := chtls_main.o chtls_cm.o chtls_io.o chtls_hw.o diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h deleted file mode 100644 index 1de5744a49b0..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h +++ /dev/null @@ -1,584 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2018 Chelsio Communications, Inc. - */ - -#ifndef __CHTLS_H__ -#define __CHTLS_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "t4fw_api.h" -#include "t4_msg.h" -#include "cxgb4.h" -#include "cxgb4_uld.h" -#include "l2t.h" -#include "chcr_algo.h" -#include "chcr_core.h" -#include "chcr_crypto.h" - -#define CHTLS_DRV_VERSION "1.0.0.0-ko" - -#define TLS_KEYCTX_RXFLIT_CNT_S 24 -#define TLS_KEYCTX_RXFLIT_CNT_V(x) ((x) << TLS_KEYCTX_RXFLIT_CNT_S) - -#define TLS_KEYCTX_RXPROT_VER_S 20 -#define TLS_KEYCTX_RXPROT_VER_M 0xf -#define TLS_KEYCTX_RXPROT_VER_V(x) ((x) << TLS_KEYCTX_RXPROT_VER_S) - -#define TLS_KEYCTX_RXCIPH_MODE_S 16 -#define TLS_KEYCTX_RXCIPH_MODE_M 0xf -#define TLS_KEYCTX_RXCIPH_MODE_V(x) ((x) << TLS_KEYCTX_RXCIPH_MODE_S) - -#define TLS_KEYCTX_RXAUTH_MODE_S 12 -#define TLS_KEYCTX_RXAUTH_MODE_M 0xf -#define TLS_KEYCTX_RXAUTH_MODE_V(x) ((x) << TLS_KEYCTX_RXAUTH_MODE_S) - -#define TLS_KEYCTX_RXCIAU_CTRL_S 11 -#define TLS_KEYCTX_RXCIAU_CTRL_V(x) ((x) << TLS_KEYCTX_RXCIAU_CTRL_S) - -#define TLS_KEYCTX_RX_SEQCTR_S 9 -#define TLS_KEYCTX_RX_SEQCTR_M 0x3 -#define TLS_KEYCTX_RX_SEQCTR_V(x) ((x) << TLS_KEYCTX_RX_SEQCTR_S) - -#define TLS_KEYCTX_RX_VALID_S 8 -#define TLS_KEYCTX_RX_VALID_V(x) ((x) << TLS_KEYCTX_RX_VALID_S) - -#define TLS_KEYCTX_RXCK_SIZE_S 3 -#define TLS_KEYCTX_RXCK_SIZE_M 0x7 -#define TLS_KEYCTX_RXCK_SIZE_V(x) ((x) << TLS_KEYCTX_RXCK_SIZE_S) - -#define TLS_KEYCTX_RXMK_SIZE_S 0 -#define TLS_KEYCTX_RXMK_SIZE_M 0x7 -#define TLS_KEYCTX_RXMK_SIZE_V(x) ((x) << TLS_KEYCTX_RXMK_SIZE_S) - -#define KEYCTX_TX_WR_IV_S 55 -#define KEYCTX_TX_WR_IV_M 0x1ffULL -#define KEYCTX_TX_WR_IV_V(x) ((x) << KEYCTX_TX_WR_IV_S) -#define KEYCTX_TX_WR_IV_G(x) \ - (((x) >> KEYCTX_TX_WR_IV_S) & KEYCTX_TX_WR_IV_M) - -#define KEYCTX_TX_WR_AAD_S 47 -#define KEYCTX_TX_WR_AAD_M 0xffULL -#define KEYCTX_TX_WR_AAD_V(x) ((x) << KEYCTX_TX_WR_AAD_S) -#define KEYCTX_TX_WR_AAD_G(x) (((x) >> KEYCTX_TX_WR_AAD_S) & \ - KEYCTX_TX_WR_AAD_M) - -#define KEYCTX_TX_WR_AADST_S 39 -#define KEYCTX_TX_WR_AADST_M 0xffULL -#define KEYCTX_TX_WR_AADST_V(x) ((x) << KEYCTX_TX_WR_AADST_S) -#define KEYCTX_TX_WR_AADST_G(x) \ - (((x) >> KEYCTX_TX_WR_AADST_S) & KEYCTX_TX_WR_AADST_M) - -#define KEYCTX_TX_WR_CIPHER_S 30 -#define KEYCTX_TX_WR_CIPHER_M 0x1ffULL -#define KEYCTX_TX_WR_CIPHER_V(x) ((x) << KEYCTX_TX_WR_CIPHER_S) -#define KEYCTX_TX_WR_CIPHER_G(x) \ - (((x) >> KEYCTX_TX_WR_CIPHER_S) & KEYCTX_TX_WR_CIPHER_M) - -#define KEYCTX_TX_WR_CIPHERST_S 23 -#define KEYCTX_TX_WR_CIPHERST_M 0x7f -#define KEYCTX_TX_WR_CIPHERST_V(x) ((x) << KEYCTX_TX_WR_CIPHERST_S) -#define KEYCTX_TX_WR_CIPHERST_G(x) \ - (((x) >> KEYCTX_TX_WR_CIPHERST_S) & KEYCTX_TX_WR_CIPHERST_M) - -#define KEYCTX_TX_WR_AUTH_S 14 -#define KEYCTX_TX_WR_AUTH_M 0x1ff -#define KEYCTX_TX_WR_AUTH_V(x) ((x) << KEYCTX_TX_WR_AUTH_S) -#define KEYCTX_TX_WR_AUTH_G(x) \ - (((x) >> KEYCTX_TX_WR_AUTH_S) & KEYCTX_TX_WR_AUTH_M) - -#define KEYCTX_TX_WR_AUTHST_S 7 -#define KEYCTX_TX_WR_AUTHST_M 0x7f -#define KEYCTX_TX_WR_AUTHST_V(x) ((x) << KEYCTX_TX_WR_AUTHST_S) -#define KEYCTX_TX_WR_AUTHST_G(x) \ - (((x) >> KEYCTX_TX_WR_AUTHST_S) & KEYCTX_TX_WR_AUTHST_M) - -#define KEYCTX_TX_WR_AUTHIN_S 0 -#define KEYCTX_TX_WR_AUTHIN_M 0x7f -#define KEYCTX_TX_WR_AUTHIN_V(x) ((x) << KEYCTX_TX_WR_AUTHIN_S) -#define KEYCTX_TX_WR_AUTHIN_G(x) \ - (((x) >> KEYCTX_TX_WR_AUTHIN_S) & KEYCTX_TX_WR_AUTHIN_M) - -struct sge_opaque_hdr { - void *dev; - dma_addr_t addr[MAX_SKB_FRAGS + 1]; -}; - -#define MAX_IVS_PAGE 256 -#define TLS_KEY_CONTEXT_SZ 64 -#define CIPHER_BLOCK_SIZE 16 -#define GCM_TAG_SIZE 16 -#define KEY_ON_MEM_SZ 16 -#define AEAD_EXPLICIT_DATA_SIZE 8 -#define TLS_HEADER_LENGTH 5 -#define SCMD_CIPH_MODE_AES_GCM 2 -/* Any MFS size should work and come from openssl */ -#define TLS_MFS 16384 - -#define RSS_HDR sizeof(struct rss_header) -#define TLS_WR_CPL_LEN \ - (sizeof(struct fw_tlstx_data_wr) + sizeof(struct cpl_tx_tls_sfo)) - -enum { - CHTLS_KEY_CONTEXT_DSGL, - CHTLS_KEY_CONTEXT_IMM, - CHTLS_KEY_CONTEXT_DDR, -}; - -enum { - CHTLS_LISTEN_START, - CHTLS_LISTEN_STOP, -}; - -/* Flags for return value of CPL message handlers */ -enum { - CPL_RET_BUF_DONE = 1, /* buffer processing done */ - CPL_RET_BAD_MSG = 2, /* bad CPL message */ - CPL_RET_UNKNOWN_TID = 4 /* unexpected unknown TID */ -}; - -#define LISTEN_INFO_HASH_SIZE 32 -#define RSPQ_HASH_BITS 5 -struct listen_info { - struct listen_info *next; /* Link to next entry */ - struct sock *sk; /* The listening socket */ - unsigned int stid; /* The server TID */ -}; - -enum { - T4_LISTEN_START_PENDING, - T4_LISTEN_STARTED -}; - -enum csk_flags { - CSK_CALLBACKS_CHKD, /* socket callbacks have been sanitized */ - CSK_ABORT_REQ_RCVD, /* received one ABORT_REQ_RSS message */ - CSK_TX_MORE_DATA, /* sending ULP data; don't set SHOVE bit */ - CSK_TX_WAIT_IDLE, /* suspend Tx until in-flight data is ACKed */ - CSK_ABORT_SHUTDOWN, /* shouldn't send more abort requests */ - CSK_ABORT_RPL_PENDING, /* expecting an abort reply */ - CSK_CLOSE_CON_REQUESTED,/* we've sent a close_conn_req */ - CSK_TX_DATA_SENT, /* sent a TX_DATA WR on this connection */ - CSK_TX_FAILOVER, /* Tx traffic failing over */ - CSK_UPDATE_RCV_WND, /* Need to update rcv window */ - CSK_RST_ABORTED, /* outgoing RST was aborted */ - CSK_TLS_HANDSHK, /* TLS Handshake */ - CSK_CONN_INLINE, /* Connection on HW */ -}; - -enum chtls_cdev_state { - CHTLS_CDEV_STATE_UP = 1 -}; - -struct listen_ctx { - struct sock *lsk; - struct chtls_dev *cdev; - struct sk_buff_head synq; - u32 state; -}; - -struct key_map { - unsigned long *addr; - unsigned int start; - unsigned int available; - unsigned int size; - spinlock_t lock; /* lock for key id request from map */ -} __packed; - -struct tls_scmd { - u32 seqno_numivs; - u32 ivgen_hdrlen; -}; - -struct chtls_dev { - struct tls_toe_device tlsdev; - struct list_head list; - struct cxgb4_lld_info *lldi; - struct pci_dev *pdev; - struct listen_info *listen_hash_tab[LISTEN_INFO_HASH_SIZE]; - spinlock_t listen_lock; /* lock for listen list */ - struct net_device **ports; - struct tid_info *tids; - unsigned int pfvf; - const unsigned short *mtus; - - struct idr hwtid_idr; - struct idr stid_idr; - - spinlock_t idr_lock ____cacheline_aligned_in_smp; - - struct net_device *egr_dev[NCHAN * 2]; - struct sk_buff *rspq_skb_cache[1 << RSPQ_HASH_BITS]; - struct sk_buff *askb; - - struct sk_buff_head deferq; - struct work_struct deferq_task; - - struct list_head list_node; - struct list_head rcu_node; - struct list_head na_node; - unsigned int send_page_order; - int max_host_sndbuf; - u32 round_robin_cnt; - struct key_map kmap; - unsigned int cdev_state; -}; - -struct chtls_listen { - struct chtls_dev *cdev; - struct sock *sk; -}; - -struct chtls_hws { - struct sk_buff_head sk_recv_queue; - u8 txqid; - u8 ofld; - u16 type; - u16 rstate; - u16 keyrpl; - u16 pldlen; - u16 rcvpld; - u16 compute; - u16 expansion; - u16 keylen; - u16 pdus; - u16 adjustlen; - u16 ivsize; - u16 txleft; - u32 mfs; - s32 txkey; - s32 rxkey; - u32 fcplenmax; - u32 copied_seq; - u64 tx_seq_no; - struct tls_scmd scmd; - union { - struct tls12_crypto_info_aes_gcm_128 aes_gcm_128; - struct tls12_crypto_info_aes_gcm_256 aes_gcm_256; - } crypto_info; -}; - -struct chtls_sock { - struct sock *sk; - struct chtls_dev *cdev; - struct l2t_entry *l2t_entry; /* pointer to the L2T entry */ - struct net_device *egress_dev; /* TX_CHAN for act open retry */ - - struct sk_buff_head txq; - struct sk_buff *wr_skb_head; - struct sk_buff *wr_skb_tail; - struct sk_buff *ctrl_skb_cache; - struct sk_buff *txdata_skb_cache; /* abort path messages */ - struct kref kref; - unsigned long flags; - u32 opt2; - u32 wr_credits; - u32 wr_unacked; - u32 wr_max_credits; - u32 wr_nondata; - u32 hwtid; /* TCP Control Block ID */ - u32 txq_idx; - u32 rss_qid; - u32 tid; - u32 idr; - u32 mss; - u32 ulp_mode; - u32 tx_chan; - u32 rx_chan; - u32 sndbuf; - u32 txplen_max; - u32 mtu_idx; /* MTU table index */ - u32 smac_idx; - u8 port_id; - u8 tos; - u16 resv2; - u32 delack_mode; - u32 delack_seq; - u32 snd_win; - u32 rcv_win; - - void *passive_reap_next; /* placeholder for passive */ - struct chtls_hws tlshws; - struct synq { - struct sk_buff *next; - struct sk_buff *prev; - } synq; - struct listen_ctx *listen_ctx; -}; - -struct tls_hdr { - u8 type; - u16 version; - u16 length; -} __packed; - -struct tlsrx_cmp_hdr { - u8 type; - u16 version; - u16 length; - - u64 tls_seq; - u16 reserved1; - u8 res_to_mac_error; -} __packed; - -/* res_to_mac_error fields */ -#define TLSRX_HDR_PKT_INT_ERROR_S 4 -#define TLSRX_HDR_PKT_INT_ERROR_M 0x1 -#define TLSRX_HDR_PKT_INT_ERROR_V(x) \ - ((x) << TLSRX_HDR_PKT_INT_ERROR_S) -#define TLSRX_HDR_PKT_INT_ERROR_G(x) \ - (((x) >> TLSRX_HDR_PKT_INT_ERROR_S) & TLSRX_HDR_PKT_INT_ERROR_M) -#define TLSRX_HDR_PKT_INT_ERROR_F TLSRX_HDR_PKT_INT_ERROR_V(1U) - -#define TLSRX_HDR_PKT_SPP_ERROR_S 3 -#define TLSRX_HDR_PKT_SPP_ERROR_M 0x1 -#define TLSRX_HDR_PKT_SPP_ERROR_V(x) ((x) << TLSRX_HDR_PKT_SPP_ERROR) -#define TLSRX_HDR_PKT_SPP_ERROR_G(x) \ - (((x) >> TLSRX_HDR_PKT_SPP_ERROR_S) & TLSRX_HDR_PKT_SPP_ERROR_M) -#define TLSRX_HDR_PKT_SPP_ERROR_F TLSRX_HDR_PKT_SPP_ERROR_V(1U) - -#define TLSRX_HDR_PKT_CCDX_ERROR_S 2 -#define TLSRX_HDR_PKT_CCDX_ERROR_M 0x1 -#define TLSRX_HDR_PKT_CCDX_ERROR_V(x) ((x) << TLSRX_HDR_PKT_CCDX_ERROR_S) -#define TLSRX_HDR_PKT_CCDX_ERROR_G(x) \ - (((x) >> TLSRX_HDR_PKT_CCDX_ERROR_S) & TLSRX_HDR_PKT_CCDX_ERROR_M) -#define TLSRX_HDR_PKT_CCDX_ERROR_F TLSRX_HDR_PKT_CCDX_ERROR_V(1U) - -#define TLSRX_HDR_PKT_PAD_ERROR_S 1 -#define TLSRX_HDR_PKT_PAD_ERROR_M 0x1 -#define TLSRX_HDR_PKT_PAD_ERROR_V(x) ((x) << TLSRX_HDR_PKT_PAD_ERROR_S) -#define TLSRX_HDR_PKT_PAD_ERROR_G(x) \ - (((x) >> TLSRX_HDR_PKT_PAD_ERROR_S) & TLSRX_HDR_PKT_PAD_ERROR_M) -#define TLSRX_HDR_PKT_PAD_ERROR_F TLSRX_HDR_PKT_PAD_ERROR_V(1U) - -#define TLSRX_HDR_PKT_MAC_ERROR_S 0 -#define TLSRX_HDR_PKT_MAC_ERROR_M 0x1 -#define TLSRX_HDR_PKT_MAC_ERROR_V(x) ((x) << TLSRX_HDR_PKT_MAC_ERROR) -#define TLSRX_HDR_PKT_MAC_ERROR_G(x) \ - (((x) >> S_TLSRX_HDR_PKT_MAC_ERROR_S) & TLSRX_HDR_PKT_MAC_ERROR_M) -#define TLSRX_HDR_PKT_MAC_ERROR_F TLSRX_HDR_PKT_MAC_ERROR_V(1U) - -#define TLSRX_HDR_PKT_ERROR_M 0x1F -#define CONTENT_TYPE_ERROR 0x7F - -struct ulp_mem_rw { - __be32 cmd; - __be32 len16; /* command length */ - __be32 dlen; /* data length in 32-byte units */ - __be32 lock_addr; -}; - -struct tls_key_wr { - __be32 op_to_compl; - __be32 flowid_len16; - __be32 ftid; - u8 reneg_to_write_rx; - u8 protocol; - __be16 mfs; -}; - -struct tls_key_req { - struct tls_key_wr wr; - struct ulp_mem_rw req; - struct ulptx_idata sc_imm; -}; - -/* - * This lives in skb->cb and is used to chain WRs in a linked list. - */ -struct wr_skb_cb { - struct l2t_skb_cb l2t; /* reserve space for l2t CB */ - struct sk_buff *next_wr; /* next write request */ -}; - -/* Per-skb backlog handler. Run when a socket's backlog is processed. */ -struct blog_skb_cb { - void (*backlog_rcv)(struct sock *sk, struct sk_buff *skb); - struct chtls_dev *cdev; -}; - -/* - * Similar to tcp_skb_cb but with ULP elements added to support TLS, - * etc. - */ -struct ulp_skb_cb { - struct wr_skb_cb wr; /* reserve space for write request */ - u16 flags; /* TCP-like flags */ - u8 psh; - u8 ulp_mode; /* ULP mode/submode of sk_buff */ - u32 seq; /* TCP sequence number */ - union { /* ULP-specific fields */ - struct { - u8 type; - u8 ofld; - u8 iv; - } tls; - } ulp; -}; - -#define ULP_SKB_CB(skb) ((struct ulp_skb_cb *)&((skb)->cb[0])) -#define BLOG_SKB_CB(skb) ((struct blog_skb_cb *)(skb)->cb) - -/* - * Flags for ulp_skb_cb.flags. - */ -enum { - ULPCB_FLAG_NEED_HDR = 1 << 0, /* packet needs a TX_DATA_WR header */ - ULPCB_FLAG_NO_APPEND = 1 << 1, /* don't grow this skb */ - ULPCB_FLAG_BARRIER = 1 << 2, /* set TX_WAIT_IDLE after sending */ - ULPCB_FLAG_HOLD = 1 << 3, /* skb not ready for Tx yet */ - ULPCB_FLAG_COMPL = 1 << 4, /* request WR completion */ - ULPCB_FLAG_URG = 1 << 5, /* urgent data */ - ULPCB_FLAG_TLS_HDR = 1 << 6, /* payload with tls hdr */ - ULPCB_FLAG_NO_HDR = 1 << 7, /* not a ofld wr */ -}; - -/* The ULP mode/submode of an skbuff */ -#define skb_ulp_mode(skb) (ULP_SKB_CB(skb)->ulp_mode) -#define TCP_PAGE(sk) (sk->sk_frag.page) -#define TCP_OFF(sk) (sk->sk_frag.offset) - -static inline struct chtls_dev *to_chtls_dev(struct tls_toe_device *tlsdev) -{ - return container_of(tlsdev, struct chtls_dev, tlsdev); -} - -static inline void csk_set_flag(struct chtls_sock *csk, - enum csk_flags flag) -{ - __set_bit(flag, &csk->flags); -} - -static inline void csk_reset_flag(struct chtls_sock *csk, - enum csk_flags flag) -{ - __clear_bit(flag, &csk->flags); -} - -static inline bool csk_conn_inline(const struct chtls_sock *csk) -{ - return test_bit(CSK_CONN_INLINE, &csk->flags); -} - -static inline int csk_flag(const struct sock *sk, enum csk_flags flag) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - if (!csk_conn_inline(csk)) - return 0; - return test_bit(flag, &csk->flags); -} - -static inline int csk_flag_nochk(const struct chtls_sock *csk, - enum csk_flags flag) -{ - return test_bit(flag, &csk->flags); -} - -static inline void *cplhdr(struct sk_buff *skb) -{ - return skb->data; -} - -static inline int is_neg_adv(unsigned int status) -{ - return status == CPL_ERR_RTX_NEG_ADVICE || - status == CPL_ERR_KEEPALV_NEG_ADVICE || - status == CPL_ERR_PERSIST_NEG_ADVICE; -} - -static inline void process_cpl_msg(void (*fn)(struct sock *, struct sk_buff *), - struct sock *sk, - struct sk_buff *skb) -{ - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - - bh_lock_sock(sk); - if (unlikely(sock_owned_by_user(sk))) { - BLOG_SKB_CB(skb)->backlog_rcv = fn; - __sk_add_backlog(sk, skb); - } else { - fn(sk, skb); - } - bh_unlock_sock(sk); -} - -static inline void chtls_sock_free(struct kref *ref) -{ - struct chtls_sock *csk = container_of(ref, struct chtls_sock, - kref); - kfree(csk); -} - -static inline void __chtls_sock_put(const char *fn, struct chtls_sock *csk) -{ - kref_put(&csk->kref, chtls_sock_free); -} - -static inline void __chtls_sock_get(const char *fn, - struct chtls_sock *csk) -{ - kref_get(&csk->kref); -} - -static inline void send_or_defer(struct sock *sk, struct tcp_sock *tp, - struct sk_buff *skb, int through_l2t) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - if (through_l2t) { - /* send through L2T */ - cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry); - } else { - /* send directly */ - cxgb4_ofld_send(csk->egress_dev, skb); - } -} - -typedef int (*chtls_handler_func)(struct chtls_dev *, struct sk_buff *); -extern chtls_handler_func chtls_handlers[NUM_CPL_CMDS]; -void chtls_install_cpl_ops(struct sock *sk); -int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi); -void chtls_listen_stop(struct chtls_dev *cdev, struct sock *sk); -int chtls_listen_start(struct chtls_dev *cdev, struct sock *sk); -void chtls_close(struct sock *sk, long timeout); -int chtls_disconnect(struct sock *sk, int flags); -void chtls_shutdown(struct sock *sk, int how); -void chtls_destroy_sock(struct sock *sk); -int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); -int chtls_recvmsg(struct sock *sk, struct msghdr *msg, - size_t len, int flags); -void chtls_splice_eof(struct socket *sock); -int send_tx_flowc_wr(struct sock *sk, int compl, - u32 snd_nxt, u32 rcv_nxt); -void chtls_tcp_push(struct sock *sk, int flags); -int chtls_push_frames(struct chtls_sock *csk, int comp); -void chtls_set_tcb_field_rpl_skb(struct sock *sk, u16 word, - u64 mask, u64 val, u8 cookie, - int through_l2t); -int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 mode, int cipher_type); -void chtls_set_quiesce_ctrl(struct sock *sk, int val); -void skb_entail(struct sock *sk, struct sk_buff *skb, int flags); -unsigned int keyid_to_addr(int start_addr, int keyid); -void free_tls_keyid(struct sock *sk); -#endif diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c deleted file mode 100644 index 0e3e5cf52c2c..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c +++ /dev/null @@ -1,2336 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2018 Chelsio Communications, Inc. - * - * Written by: Atul Gupta (atul.gupta@chelsio.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "chtls.h" -#include "chtls_cm.h" -#include "clip_tbl.h" -#include "t4_tcb.h" - -/* - * State transitions and actions for close. Note that if we are in SYN_SENT - * we remain in that state as we cannot control a connection while it's in - * SYN_SENT; such connections are allowed to establish and are then aborted. - */ -static unsigned char new_state[16] = { - /* current state: new state: action: */ - /* (Invalid) */ TCP_CLOSE, - /* TCP_ESTABLISHED */ TCP_FIN_WAIT1 | TCP_ACTION_FIN, - /* TCP_SYN_SENT */ TCP_SYN_SENT, - /* TCP_SYN_RECV */ TCP_FIN_WAIT1 | TCP_ACTION_FIN, - /* TCP_FIN_WAIT1 */ TCP_FIN_WAIT1, - /* TCP_FIN_WAIT2 */ TCP_FIN_WAIT2, - /* TCP_TIME_WAIT */ TCP_CLOSE, - /* TCP_CLOSE */ TCP_CLOSE, - /* TCP_CLOSE_WAIT */ TCP_LAST_ACK | TCP_ACTION_FIN, - /* TCP_LAST_ACK */ TCP_LAST_ACK, - /* TCP_LISTEN */ TCP_CLOSE, - /* TCP_CLOSING */ TCP_CLOSING, -}; - -static struct chtls_sock *chtls_sock_create(struct chtls_dev *cdev) -{ - struct chtls_sock *csk = kzalloc_obj(*csk, GFP_ATOMIC); - - if (!csk) - return NULL; - - csk->txdata_skb_cache = alloc_skb(TXDATA_SKB_LEN, GFP_ATOMIC); - if (!csk->txdata_skb_cache) { - kfree(csk); - return NULL; - } - - kref_init(&csk->kref); - csk->cdev = cdev; - skb_queue_head_init(&csk->txq); - csk->wr_skb_head = NULL; - csk->wr_skb_tail = NULL; - csk->mss = MAX_MSS; - csk->tlshws.ofld = 1; - csk->tlshws.txkey = -1; - csk->tlshws.rxkey = -1; - csk->tlshws.mfs = TLS_MFS; - skb_queue_head_init(&csk->tlshws.sk_recv_queue); - return csk; -} - -static void chtls_sock_release(struct kref *ref) -{ - struct chtls_sock *csk = - container_of(ref, struct chtls_sock, kref); - - kfree(csk); -} - -static struct net_device *chtls_find_netdev(struct chtls_dev *cdev, - struct sock *sk) -{ - struct adapter *adap = pci_get_drvdata(cdev->pdev); - struct net_device *ndev = cdev->ports[0]; -#if IS_ENABLED(CONFIG_IPV6) - struct net_device *temp; - int addr_type; -#endif - int i; - - switch (sk->sk_family) { - case PF_INET: - if (likely(!inet_sk(sk)->inet_rcv_saddr)) - return ndev; - ndev = __ip_dev_find(&init_net, inet_sk(sk)->inet_rcv_saddr, false); - break; -#if IS_ENABLED(CONFIG_IPV6) - case PF_INET6: - addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr); - if (likely(addr_type == IPV6_ADDR_ANY)) - return ndev; - - for_each_netdev_rcu(&init_net, temp) { - if (ipv6_chk_addr(&init_net, (struct in6_addr *) - &sk->sk_v6_rcv_saddr, temp, 1)) { - ndev = temp; - break; - } - } - break; -#endif - default: - return NULL; - } - - if (!ndev) - return NULL; - - if (is_vlan_dev(ndev)) - ndev = vlan_dev_real_dev(ndev); - - for_each_port(adap, i) - if (cdev->ports[i] == ndev) - return ndev; - return NULL; -} - -static void assign_rxopt(struct sock *sk, unsigned int opt) -{ - const struct chtls_dev *cdev; - struct chtls_sock *csk; - struct tcp_sock *tp; - - csk = rcu_dereference_sk_user_data(sk); - tp = tcp_sk(sk); - - cdev = csk->cdev; - tp->tcp_header_len = sizeof(struct tcphdr); - tp->rx_opt.mss_clamp = cdev->mtus[TCPOPT_MSS_G(opt)] - 40; - tp->mss_cache = tp->rx_opt.mss_clamp; - tp->rx_opt.tstamp_ok = TCPOPT_TSTAMP_G(opt); - tp->rx_opt.snd_wscale = TCPOPT_SACK_G(opt); - tp->rx_opt.wscale_ok = TCPOPT_WSCALE_OK_G(opt); - SND_WSCALE(tp) = TCPOPT_SND_WSCALE_G(opt); - if (!tp->rx_opt.wscale_ok) - tp->rx_opt.rcv_wscale = 0; - if (tp->rx_opt.tstamp_ok) { - tp->tcp_header_len += TCPOLEN_TSTAMP_ALIGNED; - tp->rx_opt.mss_clamp -= TCPOLEN_TSTAMP_ALIGNED; - } else if (csk->opt2 & TSTAMPS_EN_F) { - csk->opt2 &= ~TSTAMPS_EN_F; - csk->mtu_idx = TCPOPT_MSS_G(opt); - } -} - -static void chtls_purge_receive_queue(struct sock *sk) -{ - struct sk_buff *skb; - - while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { - skb_dstref_steal(skb); - kfree_skb(skb); - } -} - -static void chtls_purge_write_queue(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct sk_buff *skb; - - while ((skb = __skb_dequeue(&csk->txq))) { - sk->sk_wmem_queued -= skb->truesize; - __kfree_skb(skb); - } -} - -static void chtls_purge_recv_queue(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct chtls_hws *tlsk = &csk->tlshws; - struct sk_buff *skb; - - while ((skb = __skb_dequeue(&tlsk->sk_recv_queue)) != NULL) { - skb_dstref_steal(skb); - kfree_skb(skb); - } -} - -static void abort_arp_failure(void *handle, struct sk_buff *skb) -{ - struct cpl_abort_req *req = cplhdr(skb); - struct chtls_dev *cdev; - - cdev = (struct chtls_dev *)handle; - req->cmd = CPL_ABORT_NO_RST; - cxgb4_ofld_send(cdev->lldi->ports[0], skb); -} - -static struct sk_buff *alloc_ctrl_skb(struct sk_buff *skb, int len) -{ - if (likely(skb && !skb_shared(skb) && !skb_cloned(skb))) { - __skb_trim(skb, 0); - refcount_inc(&skb->users); - } else { - skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL); - } - return skb; -} - -static void chtls_send_abort(struct sock *sk, int mode, struct sk_buff *skb) -{ - struct cpl_abort_req *req; - struct chtls_sock *csk; - struct tcp_sock *tp; - - csk = rcu_dereference_sk_user_data(sk); - tp = tcp_sk(sk); - - if (!skb) - skb = alloc_ctrl_skb(csk->txdata_skb_cache, sizeof(*req)); - - req = (struct cpl_abort_req *)skb_put(skb, sizeof(*req)); - INIT_TP_WR_CPL(req, CPL_ABORT_REQ, csk->tid); - skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA); - req->rsvd0 = htonl(tp->snd_nxt); - req->rsvd1 = !csk_flag_nochk(csk, CSK_TX_DATA_SENT); - req->cmd = mode; - t4_set_arp_err_handler(skb, csk->cdev, abort_arp_failure); - send_or_defer(sk, tp, skb, mode == CPL_ABORT_SEND_RST); -} - -static void chtls_send_reset(struct sock *sk, int mode, struct sk_buff *skb) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - if (unlikely(csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN) || - !csk->cdev)) { - if (sk->sk_state == TCP_SYN_RECV) - csk_set_flag(csk, CSK_RST_ABORTED); - goto out; - } - - if (!csk_flag_nochk(csk, CSK_TX_DATA_SENT)) { - struct tcp_sock *tp = tcp_sk(sk); - - if (send_tx_flowc_wr(sk, 0, tp->snd_nxt, tp->rcv_nxt) < 0) - WARN_ONCE(1, "send tx flowc error"); - csk_set_flag(csk, CSK_TX_DATA_SENT); - } - - csk_set_flag(csk, CSK_ABORT_RPL_PENDING); - chtls_purge_write_queue(sk); - - csk_set_flag(csk, CSK_ABORT_SHUTDOWN); - if (sk->sk_state != TCP_SYN_RECV) - chtls_send_abort(sk, mode, skb); - else - chtls_set_tcb_field_rpl_skb(sk, TCB_T_FLAGS_W, - TCB_T_FLAGS_V(TCB_T_FLAGS_M), 0, - TCB_FIELD_COOKIE_TFLAG, 1); - - return; -out: - kfree_skb(skb); -} - -static void release_tcp_port(struct sock *sk) -{ - if (inet_csk(sk)->icsk_bind_hash) - inet_put_port(sk); -} - -static void tcp_uncork(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - if (tp->nonagle & TCP_NAGLE_CORK) { - tp->nonagle &= ~TCP_NAGLE_CORK; - chtls_tcp_push(sk, 0); - } -} - -static void chtls_close_conn(struct sock *sk) -{ - struct cpl_close_con_req *req; - struct chtls_sock *csk; - struct sk_buff *skb; - unsigned int tid; - unsigned int len; - - len = roundup(sizeof(struct cpl_close_con_req), 16); - csk = rcu_dereference_sk_user_data(sk); - tid = csk->tid; - - skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL); - req = (struct cpl_close_con_req *)__skb_put(skb, len); - memset(req, 0, len); - req->wr.wr_hi = htonl(FW_WR_OP_V(FW_TP_WR) | - FW_WR_IMMDLEN_V(sizeof(*req) - - sizeof(req->wr))); - req->wr.wr_mid = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16)) | - FW_WR_FLOWID_V(tid)); - - OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid)); - - tcp_uncork(sk); - skb_entail(sk, skb, ULPCB_FLAG_NO_HDR | ULPCB_FLAG_NO_APPEND); - if (sk->sk_state != TCP_SYN_SENT) - chtls_push_frames(csk, 1); -} - -/* - * Perform a state transition during close and return the actions indicated - * for the transition. Do not make this function inline, the main reason - * it exists at all is to avoid multiple inlining of tcp_set_state. - */ -static int make_close_transition(struct sock *sk) -{ - int next = (int)new_state[sk->sk_state]; - - tcp_set_state(sk, next & TCP_STATE_MASK); - return next & TCP_ACTION_FIN; -} - -void chtls_close(struct sock *sk, long timeout) -{ - int data_lost, prev_state; - struct chtls_sock *csk; - - csk = rcu_dereference_sk_user_data(sk); - - lock_sock(sk); - sk->sk_shutdown |= SHUTDOWN_MASK; - - data_lost = skb_queue_len(&sk->sk_receive_queue); - data_lost |= skb_queue_len(&csk->tlshws.sk_recv_queue); - chtls_purge_recv_queue(sk); - chtls_purge_receive_queue(sk); - - if (sk->sk_state == TCP_CLOSE) { - goto wait; - } else if (data_lost || sk->sk_state == TCP_SYN_SENT) { - chtls_send_reset(sk, CPL_ABORT_SEND_RST, NULL); - release_tcp_port(sk); - goto unlock; - } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { - sk->sk_prot->disconnect(sk, 0); - } else if (make_close_transition(sk)) { - chtls_close_conn(sk); - } -wait: - if (timeout) - sk_stream_wait_close(sk, timeout); - -unlock: - prev_state = sk->sk_state; - sock_hold(sk); - sock_orphan(sk); - - release_sock(sk); - - local_bh_disable(); - bh_lock_sock(sk); - - if (prev_state != TCP_CLOSE && sk->sk_state == TCP_CLOSE) - goto out; - - if (sk->sk_state == TCP_FIN_WAIT2 && tcp_sk(sk)->linger2 < 0 && - !csk_flag(sk, CSK_ABORT_SHUTDOWN)) { - struct sk_buff *skb; - - skb = alloc_skb(sizeof(struct cpl_abort_req), GFP_ATOMIC); - if (skb) - chtls_send_reset(sk, CPL_ABORT_SEND_RST, skb); - } - - if (sk->sk_state == TCP_CLOSE) - inet_csk_destroy_sock(sk); - -out: - bh_unlock_sock(sk); - local_bh_enable(); - sock_put(sk); -} - -/* - * Wait until a socket enters on of the given states. - */ -static int wait_for_states(struct sock *sk, unsigned int states) -{ - DECLARE_WAITQUEUE(wait, current); - struct socket_wq _sk_wq; - long current_timeo; - int err = 0; - - current_timeo = 200; - - /* - * We want this to work even when there's no associated struct socket. - * In that case we provide a temporary wait_queue_head_t. - */ - if (!sk->sk_wq) { - init_waitqueue_head(&_sk_wq.wait); - _sk_wq.fasync_list = NULL; - init_rcu_head_on_stack(&_sk_wq.rcu); - RCU_INIT_POINTER(sk->sk_wq, &_sk_wq); - } - - add_wait_queue(sk_sleep(sk), &wait); - while (!sk_in_state(sk, states)) { - if (!current_timeo) { - err = -EBUSY; - break; - } - if (signal_pending(current)) { - err = sock_intr_errno(current_timeo); - break; - } - set_current_state(TASK_UNINTERRUPTIBLE); - release_sock(sk); - if (!sk_in_state(sk, states)) - current_timeo = schedule_timeout(current_timeo); - __set_current_state(TASK_RUNNING); - lock_sock(sk); - } - remove_wait_queue(sk_sleep(sk), &wait); - - if (rcu_dereference(sk->sk_wq) == &_sk_wq) - sk->sk_wq = NULL; - return err; -} - -int chtls_disconnect(struct sock *sk, int flags) -{ - struct tcp_sock *tp; - int err; - - tp = tcp_sk(sk); - chtls_purge_recv_queue(sk); - chtls_purge_receive_queue(sk); - chtls_purge_write_queue(sk); - - if (sk->sk_state != TCP_CLOSE) { - sk->sk_err = ECONNRESET; - chtls_send_reset(sk, CPL_ABORT_SEND_RST, NULL); - err = wait_for_states(sk, TCPF_CLOSE); - if (err) - return err; - } - chtls_purge_recv_queue(sk); - chtls_purge_receive_queue(sk); - tp->max_window = 0xFFFF << (tp->rx_opt.snd_wscale); - return tcp_disconnect(sk, flags); -} - -#define SHUTDOWN_ELIGIBLE_STATE (TCPF_ESTABLISHED | \ - TCPF_SYN_RECV | TCPF_CLOSE_WAIT) -void chtls_shutdown(struct sock *sk, int how) -{ - if ((how & SEND_SHUTDOWN) && - sk_in_state(sk, SHUTDOWN_ELIGIBLE_STATE) && - make_close_transition(sk)) - chtls_close_conn(sk); -} - -void chtls_destroy_sock(struct sock *sk) -{ - struct chtls_sock *csk; - - csk = rcu_dereference_sk_user_data(sk); - chtls_purge_recv_queue(sk); - csk->ulp_mode = ULP_MODE_NONE; - chtls_purge_write_queue(sk); - free_tls_keyid(sk); - kref_put(&csk->kref, chtls_sock_release); - if (sk->sk_family == AF_INET) - sk->sk_prot = &tcp_prot; -#if IS_ENABLED(CONFIG_IPV6) - else - sk->sk_prot = &tcpv6_prot; -#endif - sk->sk_prot->destroy(sk); -} - -static void reset_listen_child(struct sock *child) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(child); - struct sk_buff *skb; - - skb = alloc_ctrl_skb(csk->txdata_skb_cache, - sizeof(struct cpl_abort_req)); - - chtls_send_reset(child, CPL_ABORT_SEND_RST, skb); - sock_orphan(child); - tcp_orphan_count_inc(); - if (child->sk_state == TCP_CLOSE) - inet_csk_destroy_sock(child); -} - -static void chtls_disconnect_acceptq(struct sock *listen_sk) -{ - struct request_sock **pprev; - - pprev = ACCEPT_QUEUE(listen_sk); - while (*pprev) { - struct request_sock *req = *pprev; - - if (req->rsk_ops == &chtls_rsk_ops || - req->rsk_ops == &chtls_rsk_opsv6) { - struct sock *child = req->sk; - - *pprev = req->dl_next; - sk_acceptq_removed(listen_sk); - reqsk_put(req); - sock_hold(child); - local_bh_disable(); - bh_lock_sock(child); - release_tcp_port(child); - reset_listen_child(child); - bh_unlock_sock(child); - local_bh_enable(); - sock_put(child); - } else { - pprev = &req->dl_next; - } - } -} - -static int listen_hashfn(const struct sock *sk) -{ - return ((unsigned long)sk >> 10) & (LISTEN_INFO_HASH_SIZE - 1); -} - -static struct listen_info *listen_hash_add(struct chtls_dev *cdev, - struct sock *sk, - unsigned int stid) -{ - struct listen_info *p = kmalloc_obj(*p); - - if (p) { - int key = listen_hashfn(sk); - - p->sk = sk; - p->stid = stid; - spin_lock(&cdev->listen_lock); - p->next = cdev->listen_hash_tab[key]; - cdev->listen_hash_tab[key] = p; - spin_unlock(&cdev->listen_lock); - } - return p; -} - -static int listen_hash_find(struct chtls_dev *cdev, - struct sock *sk) -{ - struct listen_info *p; - int stid = -1; - int key; - - key = listen_hashfn(sk); - - spin_lock(&cdev->listen_lock); - for (p = cdev->listen_hash_tab[key]; p; p = p->next) - if (p->sk == sk) { - stid = p->stid; - break; - } - spin_unlock(&cdev->listen_lock); - return stid; -} - -static int listen_hash_del(struct chtls_dev *cdev, - struct sock *sk) -{ - struct listen_info *p, **prev; - int stid = -1; - int key; - - key = listen_hashfn(sk); - prev = &cdev->listen_hash_tab[key]; - - spin_lock(&cdev->listen_lock); - for (p = *prev; p; prev = &p->next, p = p->next) - if (p->sk == sk) { - stid = p->stid; - *prev = p->next; - kfree(p); - break; - } - spin_unlock(&cdev->listen_lock); - return stid; -} - -static void cleanup_syn_rcv_conn(struct sock *child, struct sock *parent) -{ - struct request_sock *req; - struct chtls_sock *csk; - - csk = rcu_dereference_sk_user_data(child); - req = csk->passive_reap_next; - - reqsk_queue_removed(&inet_csk(parent)->icsk_accept_queue, req); - __skb_unlink((struct sk_buff *)&csk->synq, &csk->listen_ctx->synq); - chtls_reqsk_free(req); - csk->passive_reap_next = NULL; -} - -static void chtls_reset_synq(struct listen_ctx *listen_ctx) -{ - struct sock *listen_sk = listen_ctx->lsk; - - while (!skb_queue_empty(&listen_ctx->synq)) { - struct chtls_sock *csk = - container_of((struct synq *)skb_peek - (&listen_ctx->synq), struct chtls_sock, synq); - struct sock *child = csk->sk; - - cleanup_syn_rcv_conn(child, listen_sk); - sock_hold(child); - local_bh_disable(); - bh_lock_sock(child); - release_tcp_port(child); - reset_listen_child(child); - bh_unlock_sock(child); - local_bh_enable(); - sock_put(child); - } -} - -int chtls_listen_start(struct chtls_dev *cdev, struct sock *sk) -{ - struct net_device *ndev; -#if IS_ENABLED(CONFIG_IPV6) - bool clip_valid = false; -#endif - struct listen_ctx *ctx; - struct adapter *adap; - struct port_info *pi; - int ret = 0; - int stid; - - rcu_read_lock(); - ndev = chtls_find_netdev(cdev, sk); - rcu_read_unlock(); - if (!ndev) - return -EBADF; - - pi = netdev_priv(ndev); - adap = pi->adapter; - if (!(adap->flags & CXGB4_FULL_INIT_DONE)) - return -EBADF; - - if (listen_hash_find(cdev, sk) >= 0) /* already have it */ - return -EADDRINUSE; - - ctx = kmalloc_obj(*ctx); - if (!ctx) - return -ENOMEM; - - __module_get(THIS_MODULE); - ctx->lsk = sk; - ctx->cdev = cdev; - ctx->state = T4_LISTEN_START_PENDING; - skb_queue_head_init(&ctx->synq); - - stid = cxgb4_alloc_stid(cdev->tids, sk->sk_family, ctx); - if (stid < 0) - goto free_ctx; - - sock_hold(sk); - if (!listen_hash_add(cdev, sk, stid)) - goto free_stid; - - if (sk->sk_family == PF_INET) { - ret = cxgb4_create_server(ndev, stid, - inet_sk(sk)->inet_rcv_saddr, - inet_sk(sk)->inet_sport, 0, - cdev->lldi->rxq_ids[0]); -#if IS_ENABLED(CONFIG_IPV6) - } else { - int addr_type; - - addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr); - if (addr_type != IPV6_ADDR_ANY) { - ret = cxgb4_clip_get(ndev, (const u32 *) - &sk->sk_v6_rcv_saddr, 1); - if (ret) - goto del_hash; - clip_valid = true; - } - ret = cxgb4_create_server6(ndev, stid, - &sk->sk_v6_rcv_saddr, - inet_sk(sk)->inet_sport, - cdev->lldi->rxq_ids[0]); -#endif - } - if (ret > 0) - ret = net_xmit_errno(ret); - if (ret) - goto del_hash; - return 0; -del_hash: -#if IS_ENABLED(CONFIG_IPV6) - if (clip_valid) - cxgb4_clip_release(ndev, (const u32 *)&sk->sk_v6_rcv_saddr, 1); -#endif - listen_hash_del(cdev, sk); -free_stid: - cxgb4_free_stid(cdev->tids, stid, sk->sk_family); - sock_put(sk); -free_ctx: - kfree(ctx); - module_put(THIS_MODULE); - return -EBADF; -} - -void chtls_listen_stop(struct chtls_dev *cdev, struct sock *sk) -{ - struct listen_ctx *listen_ctx; - int stid; - - stid = listen_hash_del(cdev, sk); - if (stid < 0) - return; - - listen_ctx = (struct listen_ctx *)lookup_stid(cdev->tids, stid); - chtls_reset_synq(listen_ctx); - - cxgb4_remove_server(cdev->lldi->ports[0], stid, - cdev->lldi->rxq_ids[0], sk->sk_family == PF_INET6); - -#if IS_ENABLED(CONFIG_IPV6) - if (sk->sk_family == PF_INET6) { - struct net_device *ndev = chtls_find_netdev(cdev, sk); - int addr_type = 0; - - addr_type = ipv6_addr_type((const struct in6_addr *) - &sk->sk_v6_rcv_saddr); - if (addr_type != IPV6_ADDR_ANY) - cxgb4_clip_release(ndev, (const u32 *) - &sk->sk_v6_rcv_saddr, 1); - } -#endif - chtls_disconnect_acceptq(sk); -} - -static int chtls_pass_open_rpl(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_pass_open_rpl *rpl = cplhdr(skb) + RSS_HDR; - unsigned int stid = GET_TID(rpl); - struct listen_ctx *listen_ctx; - - listen_ctx = (struct listen_ctx *)lookup_stid(cdev->tids, stid); - if (!listen_ctx) - return CPL_RET_BUF_DONE; - - if (listen_ctx->state == T4_LISTEN_START_PENDING) { - listen_ctx->state = T4_LISTEN_STARTED; - return CPL_RET_BUF_DONE; - } - - if (rpl->status != CPL_ERR_NONE) { - pr_info("Unexpected PASS_OPEN_RPL status %u for STID %u\n", - rpl->status, stid); - } else { - cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); - sock_put(listen_ctx->lsk); - kfree(listen_ctx); - module_put(THIS_MODULE); - } - return CPL_RET_BUF_DONE; -} - -static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_close_listsvr_rpl *rpl = cplhdr(skb) + RSS_HDR; - struct listen_ctx *listen_ctx; - unsigned int stid; - void *data; - - stid = GET_TID(rpl); - data = lookup_stid(cdev->tids, stid); - listen_ctx = (struct listen_ctx *)data; - - if (rpl->status != CPL_ERR_NONE) { - pr_info("Unexpected CLOSE_LISTSRV_RPL status %u for STID %u\n", - rpl->status, stid); - } else { - cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); - sock_put(listen_ctx->lsk); - kfree(listen_ctx); - module_put(THIS_MODULE); - } - return CPL_RET_BUF_DONE; -} - -static void chtls_purge_wr_queue(struct sock *sk) -{ - struct sk_buff *skb; - - while ((skb = dequeue_wr(sk)) != NULL) - kfree_skb(skb); -} - -static void chtls_release_resources(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct chtls_dev *cdev = csk->cdev; - unsigned int tid = csk->tid; - struct tid_info *tids; - - if (!cdev) - return; - - tids = cdev->tids; - kfree_skb(csk->txdata_skb_cache); - csk->txdata_skb_cache = NULL; - - if (csk->wr_credits != csk->wr_max_credits) { - chtls_purge_wr_queue(sk); - chtls_reset_wr_list(csk); - } - - if (csk->l2t_entry) { - cxgb4_l2t_release(csk->l2t_entry); - csk->l2t_entry = NULL; - } - - if (sk->sk_state != TCP_SYN_SENT) { - cxgb4_remove_tid(tids, csk->port_id, tid, sk->sk_family); - sock_put(sk); - } -} - -static void chtls_conn_done(struct sock *sk) -{ - if (sock_flag(sk, SOCK_DEAD)) - chtls_purge_receive_queue(sk); - sk_wakeup_sleepers(sk, 0); - tcp_done(sk); -} - -static void do_abort_syn_rcv(struct sock *child, struct sock *parent) -{ - /* - * If the server is still open we clean up the child connection, - * otherwise the server already did the clean up as it was purging - * its SYN queue and the skb was just sitting in its backlog. - */ - if (likely(parent->sk_state == TCP_LISTEN)) { - cleanup_syn_rcv_conn(child, parent); - /* Without the below call to sock_orphan, - * we leak the socket resource with syn_flood test - * as inet_csk_destroy_sock will not be called - * in tcp_done since SOCK_DEAD flag is not set. - * Kernel handles this differently where new socket is - * created only after 3 way handshake is done. - */ - sock_orphan(child); - tcp_orphan_count_inc(); - chtls_release_resources(child); - chtls_conn_done(child); - } else { - if (csk_flag(child, CSK_RST_ABORTED)) { - chtls_release_resources(child); - chtls_conn_done(child); - } - } -} - -static void pass_open_abort(struct sock *child, struct sock *parent, - struct sk_buff *skb) -{ - do_abort_syn_rcv(child, parent); - kfree_skb(skb); -} - -static void bl_pass_open_abort(struct sock *lsk, struct sk_buff *skb) -{ - pass_open_abort(skb->sk, lsk, skb); -} - -static void chtls_pass_open_arp_failure(struct sock *sk, - struct sk_buff *skb) -{ - const struct request_sock *oreq; - struct chtls_sock *csk; - struct chtls_dev *cdev; - struct sock *parent; - void *data; - - csk = rcu_dereference_sk_user_data(sk); - cdev = csk->cdev; - - /* - * If the connection is being aborted due to the parent listening - * socket going away there's nothing to do, the ABORT_REQ will close - * the connection. - */ - if (csk_flag(sk, CSK_ABORT_RPL_PENDING)) { - kfree_skb(skb); - return; - } - - oreq = csk->passive_reap_next; - data = lookup_stid(cdev->tids, oreq->ts_recent); - parent = ((struct listen_ctx *)data)->lsk; - - bh_lock_sock(parent); - if (!sock_owned_by_user(parent)) { - pass_open_abort(sk, parent, skb); - } else { - BLOG_SKB_CB(skb)->backlog_rcv = bl_pass_open_abort; - __sk_add_backlog(parent, skb); - } - bh_unlock_sock(parent); -} - -static void chtls_accept_rpl_arp_failure(void *handle, - struct sk_buff *skb) -{ - struct sock *sk = (struct sock *)handle; - - sock_hold(sk); - process_cpl_msg(chtls_pass_open_arp_failure, sk, skb); - sock_put(sk); -} - -static unsigned int chtls_select_mss(const struct chtls_sock *csk, - unsigned int pmtu, - struct cpl_pass_accept_req *req) -{ - struct chtls_dev *cdev; - struct dst_entry *dst; - unsigned int tcpoptsz; - unsigned int iphdrsz; - unsigned int mtu_idx; - struct tcp_sock *tp; - unsigned int mss; - struct sock *sk; - u16 user_mss; - - mss = ntohs(req->tcpopt.mss); - sk = csk->sk; - dst = __sk_dst_get(sk); - cdev = csk->cdev; - tp = tcp_sk(sk); - tcpoptsz = 0; - -#if IS_ENABLED(CONFIG_IPV6) - if (sk->sk_family == AF_INET6) - iphdrsz = sizeof(struct ipv6hdr) + sizeof(struct tcphdr); - else -#endif - iphdrsz = sizeof(struct iphdr) + sizeof(struct tcphdr); - if (req->tcpopt.tstamp) - tcpoptsz += round_up(TCPOLEN_TIMESTAMP, 4); - - tp->advmss = dst_metric_advmss(dst); - user_mss = USER_MSS(tp); - if (user_mss && tp->advmss > user_mss) - tp->advmss = user_mss; - if (tp->advmss > pmtu - iphdrsz) - tp->advmss = pmtu - iphdrsz; - if (mss && tp->advmss > mss) - tp->advmss = mss; - - tp->advmss = cxgb4_best_aligned_mtu(cdev->lldi->mtus, - iphdrsz + tcpoptsz, - tp->advmss - tcpoptsz, - 8, &mtu_idx); - tp->advmss -= iphdrsz; - - inet_csk(sk)->icsk_pmtu_cookie = pmtu; - return mtu_idx; -} - -static unsigned int select_rcv_wscale(int space, int wscale_ok, int win_clamp) -{ - int wscale = 0; - - if (space > MAX_RCV_WND) - space = MAX_RCV_WND; - if (win_clamp && win_clamp < space) - space = win_clamp; - - if (wscale_ok) { - while (wscale < 14 && (65535 << wscale) < space) - wscale++; - } - return wscale; -} - -static void chtls_pass_accept_rpl(struct sk_buff *skb, - struct cpl_pass_accept_req *req, - unsigned int tid) - -{ - struct cpl_t5_pass_accept_rpl *rpl5; - struct cxgb4_lld_info *lldi; - const struct tcphdr *tcph; - const struct tcp_sock *tp; - struct chtls_sock *csk; - unsigned int len; - struct sock *sk; - u32 opt2, hlen; - u64 opt0; - - sk = skb->sk; - tp = tcp_sk(sk); - csk = sk->sk_user_data; - csk->tid = tid; - lldi = csk->cdev->lldi; - len = roundup(sizeof(*rpl5), 16); - - rpl5 = __skb_put_zero(skb, len); - INIT_TP_WR(rpl5, tid); - - OPCODE_TID(rpl5) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL, - csk->tid)); - csk->mtu_idx = chtls_select_mss(csk, dst_mtu(__sk_dst_get(sk)), - req); - opt0 = TCAM_BYPASS_F | - WND_SCALE_V(RCV_WSCALE(tp)) | - MSS_IDX_V(csk->mtu_idx) | - L2T_IDX_V(csk->l2t_entry->idx) | - NAGLE_V(!(tp->nonagle & TCP_NAGLE_OFF)) | - TX_CHAN_V(csk->tx_chan) | - SMAC_SEL_V(csk->smac_idx) | - DSCP_V(csk->tos >> 2) | - ULP_MODE_V(ULP_MODE_TLS) | - RCV_BUFSIZ_V(min(tp->rcv_wnd >> 10, RCV_BUFSIZ_M)); - - opt2 = RX_CHANNEL_V(0) | - RSS_QUEUE_VALID_F | RSS_QUEUE_V(csk->rss_qid); - - if (!is_t5(lldi->adapter_type)) - opt2 |= RX_FC_DISABLE_F; - if (req->tcpopt.tstamp) - opt2 |= TSTAMPS_EN_F; - if (req->tcpopt.sack) - opt2 |= SACK_EN_F; - hlen = ntohl(req->hdr_len); - - tcph = (struct tcphdr *)((u8 *)(req + 1) + - T6_ETH_HDR_LEN_G(hlen) + T6_IP_HDR_LEN_G(hlen)); - if (tcph->ece && tcph->cwr) - opt2 |= CCTRL_ECN_V(1); - opt2 |= CONG_CNTRL_V(CONG_ALG_NEWRENO); - opt2 |= T5_ISS_F; - opt2 |= T5_OPT_2_VALID_F; - opt2 |= WND_SCALE_EN_V(WSCALE_OK(tp)); - rpl5->opt0 = cpu_to_be64(opt0); - rpl5->opt2 = cpu_to_be32(opt2); - rpl5->iss = cpu_to_be32((get_random_u32() & ~7UL) - 1); - set_wr_txq(skb, CPL_PRIORITY_SETUP, csk->port_id); - t4_set_arp_err_handler(skb, sk, chtls_accept_rpl_arp_failure); - cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry); -} - -static void inet_inherit_port(struct sock *lsk, struct sock *newsk) -{ - local_bh_disable(); - __inet_inherit_port(lsk, newsk); - local_bh_enable(); -} - -static int chtls_backlog_rcv(struct sock *sk, struct sk_buff *skb) -{ - if (skb->protocol) { - kfree_skb(skb); - return 0; - } - BLOG_SKB_CB(skb)->backlog_rcv(sk, skb); - return 0; -} - -static void chtls_set_tcp_window(struct chtls_sock *csk) -{ - struct net_device *ndev = csk->egress_dev; - struct port_info *pi = netdev_priv(ndev); - unsigned int linkspeed; - u8 scale; - - linkspeed = pi->link_cfg.speed; - scale = linkspeed / SPEED_10000; -#define CHTLS_10G_RCVWIN (256 * 1024) - csk->rcv_win = CHTLS_10G_RCVWIN; - if (scale) - csk->rcv_win *= scale; -#define CHTLS_10G_SNDWIN (256 * 1024) - csk->snd_win = CHTLS_10G_SNDWIN; - if (scale) - csk->snd_win *= scale; -} - -static struct sock *chtls_recv_sock(struct sock *lsk, - struct request_sock *oreq, - void *network_hdr, - const struct cpl_pass_accept_req *req, - struct chtls_dev *cdev) -{ - struct adapter *adap = pci_get_drvdata(cdev->pdev); - struct neighbour *n = NULL; - struct inet_sock *newinet; - const struct iphdr *iph; - struct tls_context *ctx; - struct net_device *ndev; - struct chtls_sock *csk; - struct dst_entry *dst; - struct tcp_sock *tp; - struct sock *newsk; - bool found = false; - u16 port_id; - int rxq_idx; - int step, i; - - iph = (const struct iphdr *)network_hdr; - newsk = tcp_create_openreq_child(lsk, oreq, cdev->askb); - if (!newsk) - goto free_oreq; - - if (lsk->sk_family == AF_INET) { - dst = inet_csk_route_child_sock(lsk, newsk, oreq); - if (!dst) - goto free_sk; - - n = dst_neigh_lookup(dst, &iph->saddr); -#if IS_ENABLED(CONFIG_IPV6) - } else { - const struct ipv6hdr *ip6h; - struct flowi6 fl6; - - ip6h = (const struct ipv6hdr *)network_hdr; - memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_proto = IPPROTO_TCP; - fl6.saddr = ip6h->daddr; - fl6.daddr = ip6h->saddr; - fl6.fl6_dport = inet_rsk(oreq)->ir_rmt_port; - fl6.fl6_sport = htons(inet_rsk(oreq)->ir_num); - security_req_classify_flow(oreq, flowi6_to_flowi_common(&fl6)); - dst = ip6_dst_lookup_flow(sock_net(lsk), lsk, &fl6, NULL); - if (IS_ERR(dst)) - goto free_sk; - n = dst_neigh_lookup(dst, &ip6h->saddr); -#endif - } - if (!n || !n->dev) - goto free_dst; - - ndev = n->dev; - if (is_vlan_dev(ndev)) - ndev = vlan_dev_real_dev(ndev); - - for_each_port(adap, i) - if (cdev->ports[i] == ndev) - found = true; - - if (!found) - goto free_dst; - - port_id = cxgb4_port_idx(ndev); - - csk = chtls_sock_create(cdev); - if (!csk) - goto free_dst; - - csk->l2t_entry = cxgb4_l2t_get(cdev->lldi->l2t, n, ndev, 0); - if (!csk->l2t_entry) - goto free_csk; - - newsk->sk_user_data = csk; - newsk->sk_backlog_rcv = chtls_backlog_rcv; - - tp = tcp_sk(newsk); - newinet = inet_sk(newsk); - - if (iph->version == 0x4) { - newinet->inet_daddr = iph->saddr; - newinet->inet_rcv_saddr = iph->daddr; - newinet->inet_saddr = iph->daddr; -#if IS_ENABLED(CONFIG_IPV6) - } else { - struct tcp6_sock *newtcp6sk = (struct tcp6_sock *)newsk; - struct inet_request_sock *treq = inet_rsk(oreq); - struct ipv6_pinfo *newnp = inet6_sk(newsk); - struct ipv6_pinfo *np = inet6_sk(lsk); - - newinet->pinet6 = &newtcp6sk->inet6; - newinet->ipv6_fl_list = NULL; - memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - newsk->sk_v6_daddr = treq->ir_v6_rmt_addr; - newsk->sk_v6_rcv_saddr = treq->ir_v6_loc_addr; - inet6_sk(newsk)->saddr = treq->ir_v6_loc_addr; - newnp->pktoptions = NULL; - newsk->sk_bound_dev_if = treq->ir_iif; - newinet->inet_opt = NULL; - newinet->inet_daddr = LOOPBACK4_IPV6; - newinet->inet_saddr = LOOPBACK4_IPV6; -#endif - } - - oreq->ts_recent = PASS_OPEN_TID_G(ntohl(req->tos_stid)); - sk_setup_caps(newsk, dst); - ctx = tls_get_ctx(lsk); - newsk->sk_destruct = ctx->sk_destruct; - newsk->sk_prot_creator = lsk->sk_prot_creator; - csk->sk = newsk; - csk->passive_reap_next = oreq; - csk->tx_chan = cxgb4_port_chan(ndev); - csk->port_id = port_id; - csk->egress_dev = ndev; - csk->tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid)); - chtls_set_tcp_window(csk); - tp->rcv_wnd = csk->rcv_win; - csk->sndbuf = csk->snd_win; - csk->ulp_mode = ULP_MODE_TLS; - step = cdev->lldi->nrxq / cdev->lldi->nchan; - rxq_idx = port_id * step; - rxq_idx += cdev->round_robin_cnt++ % step; - csk->rss_qid = cdev->lldi->rxq_ids[rxq_idx]; - csk->txq_idx = (rxq_idx < cdev->lldi->ntxq) ? rxq_idx : - port_id * step; - csk->sndbuf = newsk->sk_sndbuf; - csk->smac_idx = ((struct port_info *)netdev_priv(ndev))->smt_idx; - RCV_WSCALE(tp) = select_rcv_wscale(tcp_full_space(newsk), - READ_ONCE(sock_net(newsk)-> - ipv4.sysctl_tcp_window_scaling), - tp->window_clamp); - neigh_release(n); - inet_inherit_port(lsk, newsk); - csk_set_flag(csk, CSK_CONN_INLINE); - bh_unlock_sock(newsk); /* tcp_create_openreq_child ->sk_clone_lock */ - - return newsk; -free_csk: - chtls_sock_release(&csk->kref); -free_dst: - if (n) - neigh_release(n); - dst_release(dst); -free_sk: - inet_csk_prepare_forced_close(newsk); - tcp_done(newsk); -free_oreq: - chtls_reqsk_free(oreq); - return NULL; -} - -/* - * Populate a TID_RELEASE WR. The skb must be already propely sized. - */ -static void mk_tid_release(struct sk_buff *skb, - unsigned int chan, unsigned int tid) -{ - struct cpl_tid_release *req; - unsigned int len; - - len = roundup(sizeof(struct cpl_tid_release), 16); - req = (struct cpl_tid_release *)__skb_put(skb, len); - memset(req, 0, len); - set_wr_txq(skb, CPL_PRIORITY_SETUP, chan); - INIT_TP_WR_CPL(req, CPL_TID_RELEASE, tid); -} - -static int chtls_get_module(struct sock *sk) -{ - struct inet_connection_sock *icsk = inet_csk(sk); - - if (!try_module_get(icsk->icsk_ulp_ops->owner)) - return -1; - - return 0; -} - -static void chtls_pass_accept_request(struct sock *sk, - struct sk_buff *skb) -{ - struct cpl_t5_pass_accept_rpl *rpl; - struct cpl_pass_accept_req *req; - struct listen_ctx *listen_ctx; - struct vlan_ethhdr *vlan_eh; - struct request_sock *oreq; - struct sk_buff *reply_skb; - struct chtls_sock *csk; - struct chtls_dev *cdev; - struct ipv6hdr *ip6h; - struct tcphdr *tcph; - struct sock *newsk; - struct ethhdr *eh; - struct iphdr *iph; - void *network_hdr; - unsigned int stid; - unsigned int len; - unsigned int tid; - bool th_ecn, ect; - __u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */ - u16 eth_hdr_len; - bool ecn_ok; - - req = cplhdr(skb) + RSS_HDR; - tid = GET_TID(req); - cdev = BLOG_SKB_CB(skb)->cdev; - newsk = lookup_tid(cdev->tids, tid); - stid = PASS_OPEN_TID_G(ntohl(req->tos_stid)); - if (newsk) { - pr_info("tid (%d) already in use\n", tid); - return; - } - - len = roundup(sizeof(*rpl), 16); - reply_skb = alloc_skb(len, GFP_ATOMIC); - if (!reply_skb) { - cxgb4_remove_tid(cdev->tids, 0, tid, sk->sk_family); - kfree_skb(skb); - return; - } - - if (sk->sk_state != TCP_LISTEN) - goto reject; - - if (inet_csk_reqsk_queue_is_full(sk)) - goto reject; - - if (sk_acceptq_is_full(sk)) - goto reject; - - - eth_hdr_len = T6_ETH_HDR_LEN_G(ntohl(req->hdr_len)); - if (eth_hdr_len == ETH_HLEN) { - eh = (struct ethhdr *)(req + 1); - iph = (struct iphdr *)(eh + 1); - ip6h = (struct ipv6hdr *)(eh + 1); - network_hdr = (void *)(eh + 1); - } else { - vlan_eh = (struct vlan_ethhdr *)(req + 1); - iph = (struct iphdr *)(vlan_eh + 1); - ip6h = (struct ipv6hdr *)(vlan_eh + 1); - network_hdr = (void *)(vlan_eh + 1); - } - - if (iph->version == 0x4) { - tcph = (struct tcphdr *)(iph + 1); - skb_set_network_header(skb, (void *)iph - (void *)req); - oreq = inet_reqsk_alloc(&chtls_rsk_ops, sk, true); - } else { - tcph = (struct tcphdr *)(ip6h + 1); - skb_set_network_header(skb, (void *)ip6h - (void *)req); - oreq = inet_reqsk_alloc(&chtls_rsk_opsv6, sk, false); - } - - if (!oreq) - goto reject; - - oreq->rsk_rcv_wnd = 0; - oreq->rsk_window_clamp = 0; - oreq->syncookie = 0; - oreq->mss = 0; - oreq->ts_recent = 0; - - tcp_rsk(oreq)->tfo_listener = false; - tcp_rsk(oreq)->rcv_isn = ntohl(tcph->seq); - chtls_set_req_port(oreq, tcph->source, tcph->dest); - if (iph->version == 0x4) { - chtls_set_req_addr(oreq, iph->daddr, iph->saddr); - ip_dsfield = ipv4_get_dsfield(iph); -#if IS_ENABLED(CONFIG_IPV6) - } else { - inet_rsk(oreq)->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; - inet_rsk(oreq)->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; - ip_dsfield = ipv6_get_dsfield(ipv6_hdr(skb)); -#endif - } - if (req->tcpopt.wsf <= 14 && - READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_window_scaling)) { - inet_rsk(oreq)->wscale_ok = 1; - inet_rsk(oreq)->snd_wscale = req->tcpopt.wsf; - } - inet_rsk(oreq)->ir_iif = sk->sk_bound_dev_if; - th_ecn = tcph->ece && tcph->cwr; - if (th_ecn) { - ect = !INET_ECN_is_not_ect(ip_dsfield); - ecn_ok = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_ecn); - if ((!ect && ecn_ok) || tcp_ca_needs_ecn(sk)) - inet_rsk(oreq)->ecn_ok = 1; - } - - newsk = chtls_recv_sock(sk, oreq, network_hdr, req, cdev); - if (!newsk) - goto reject; - - if (chtls_get_module(newsk)) - goto reject; - inet_csk_reqsk_queue_added(sk); - reply_skb->sk = newsk; - chtls_install_cpl_ops(newsk); - cxgb4_insert_tid(cdev->tids, newsk, tid, newsk->sk_family); - csk = rcu_dereference_sk_user_data(newsk); - listen_ctx = (struct listen_ctx *)lookup_stid(cdev->tids, stid); - csk->listen_ctx = listen_ctx; - __skb_queue_tail(&listen_ctx->synq, (struct sk_buff *)&csk->synq); - chtls_pass_accept_rpl(reply_skb, req, tid); - kfree_skb(skb); - return; - -reject: - mk_tid_release(reply_skb, 0, tid); - cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb); - kfree_skb(skb); -} - -/* - * Handle a CPL_PASS_ACCEPT_REQ message. - */ -static int chtls_pass_accept_req(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_pass_accept_req *req = cplhdr(skb) + RSS_HDR; - struct listen_ctx *ctx; - unsigned int stid; - unsigned int tid; - struct sock *lsk; - void *data; - - stid = PASS_OPEN_TID_G(ntohl(req->tos_stid)); - tid = GET_TID(req); - - data = lookup_stid(cdev->tids, stid); - if (!data) - return 1; - - ctx = (struct listen_ctx *)data; - lsk = ctx->lsk; - - if (unlikely(tid_out_of_range(cdev->tids, tid))) { - pr_info("passive open TID %u too large\n", tid); - return 1; - } - - BLOG_SKB_CB(skb)->cdev = cdev; - process_cpl_msg(chtls_pass_accept_request, lsk, skb); - return 0; -} - -/* - * Completes some final bits of initialization for just established connections - * and changes their state to TCP_ESTABLISHED. - * - * snd_isn here is the ISN after the SYN, i.e., the true ISN + 1. - */ -static void make_established(struct sock *sk, u32 snd_isn, unsigned int opt) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tp->pushed_seq = snd_isn; - tp->write_seq = snd_isn; - tp->snd_nxt = snd_isn; - tp->snd_una = snd_isn; - atomic_set(&inet_sk(sk)->inet_id, get_random_u16()); - assign_rxopt(sk, opt); - - if (tp->rcv_wnd > (RCV_BUFSIZ_M << 10)) - tp->rcv_wup -= tp->rcv_wnd - (RCV_BUFSIZ_M << 10); - - smp_mb(); - tcp_set_state(sk, TCP_ESTABLISHED); -} - -static void chtls_abort_conn(struct sock *sk, struct sk_buff *skb) -{ - struct sk_buff *abort_skb; - - abort_skb = alloc_skb(sizeof(struct cpl_abort_req), GFP_ATOMIC); - if (abort_skb) - chtls_send_reset(sk, CPL_ABORT_SEND_RST, abort_skb); -} - -static struct sock *reap_list; -static DEFINE_SPINLOCK(reap_list_lock); - -/* - * Process the reap list. - */ -DECLARE_TASK_FUNC(process_reap_list, task_param) -{ - spin_lock_bh(&reap_list_lock); - while (reap_list) { - struct sock *sk = reap_list; - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - reap_list = csk->passive_reap_next; - csk->passive_reap_next = NULL; - spin_unlock(&reap_list_lock); - sock_hold(sk); - - bh_lock_sock(sk); - chtls_abort_conn(sk, NULL); - sock_orphan(sk); - if (sk->sk_state == TCP_CLOSE) - inet_csk_destroy_sock(sk); - bh_unlock_sock(sk); - sock_put(sk); - spin_lock(&reap_list_lock); - } - spin_unlock_bh(&reap_list_lock); -} - -static DECLARE_WORK(reap_task, process_reap_list); - -static void add_to_reap_list(struct sock *sk) -{ - struct chtls_sock *csk = sk->sk_user_data; - - local_bh_disable(); - release_tcp_port(sk); /* release the port immediately */ - - spin_lock(&reap_list_lock); - csk->passive_reap_next = reap_list; - reap_list = sk; - if (!csk->passive_reap_next) - schedule_work(&reap_task); - spin_unlock(&reap_list_lock); - local_bh_enable(); -} - -static void add_pass_open_to_parent(struct sock *child, struct sock *lsk, - struct chtls_dev *cdev) -{ - struct request_sock *oreq; - struct chtls_sock *csk; - - if (lsk->sk_state != TCP_LISTEN) - return; - - csk = child->sk_user_data; - oreq = csk->passive_reap_next; - csk->passive_reap_next = NULL; - - reqsk_queue_removed(&inet_csk(lsk)->icsk_accept_queue, oreq); - __skb_unlink((struct sk_buff *)&csk->synq, &csk->listen_ctx->synq); - - if (sk_acceptq_is_full(lsk)) { - chtls_reqsk_free(oreq); - add_to_reap_list(child); - } else { - refcount_set(&oreq->rsk_refcnt, 1); - inet_csk_reqsk_queue_add(lsk, oreq, child); - lsk->sk_data_ready(lsk); - } -} - -static void bl_add_pass_open_to_parent(struct sock *lsk, struct sk_buff *skb) -{ - struct sock *child = skb->sk; - - skb->sk = NULL; - add_pass_open_to_parent(child, lsk, BLOG_SKB_CB(skb)->cdev); - kfree_skb(skb); -} - -static int chtls_pass_establish(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_pass_establish *req = cplhdr(skb) + RSS_HDR; - struct chtls_sock *csk; - struct sock *lsk, *sk; - unsigned int hwtid; - - hwtid = GET_TID(req); - sk = lookup_tid(cdev->tids, hwtid); - if (!sk) - return (CPL_RET_UNKNOWN_TID | CPL_RET_BUF_DONE); - - bh_lock_sock(sk); - if (unlikely(sock_owned_by_user(sk))) { - kfree_skb(skb); - } else { - unsigned int stid; - void *data; - - csk = sk->sk_user_data; - csk->wr_max_credits = 64; - csk->wr_credits = 64; - csk->wr_unacked = 0; - make_established(sk, ntohl(req->snd_isn), ntohs(req->tcp_opt)); - stid = PASS_OPEN_TID_G(ntohl(req->tos_stid)); - sk->sk_state_change(sk); - if (unlikely(sk->sk_socket)) - sk_wake_async(sk, 0, POLL_OUT); - - data = lookup_stid(cdev->tids, stid); - if (!data) { - /* listening server close */ - kfree_skb(skb); - goto unlock; - } - lsk = ((struct listen_ctx *)data)->lsk; - - bh_lock_sock(lsk); - if (unlikely(skb_queue_empty(&csk->listen_ctx->synq))) { - /* removed from synq */ - bh_unlock_sock(lsk); - kfree_skb(skb); - goto unlock; - } - - if (likely(!sock_owned_by_user(lsk))) { - kfree_skb(skb); - add_pass_open_to_parent(sk, lsk, cdev); - } else { - skb->sk = sk; - BLOG_SKB_CB(skb)->cdev = cdev; - BLOG_SKB_CB(skb)->backlog_rcv = - bl_add_pass_open_to_parent; - __sk_add_backlog(lsk, skb); - } - bh_unlock_sock(lsk); - } -unlock: - bh_unlock_sock(sk); - return 0; -} - -/* - * Handle receipt of an urgent pointer. - */ -static void handle_urg_ptr(struct sock *sk, u32 urg_seq) -{ - struct tcp_sock *tp = tcp_sk(sk); - - urg_seq--; - if (tp->urg_data && !after(urg_seq, tp->urg_seq)) - return; /* duplicate pointer */ - - sk_send_sigurg(sk); - if (tp->urg_seq == tp->copied_seq && tp->urg_data && - !sock_flag(sk, SOCK_URGINLINE) && - tp->copied_seq != tp->rcv_nxt) { - struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); - - tp->copied_seq++; - if (skb && tp->copied_seq - ULP_SKB_CB(skb)->seq >= skb->len) - chtls_free_skb(sk, skb); - } - - tp->urg_data = TCP_URG_NOTYET; - tp->urg_seq = urg_seq; -} - -static void check_sk_callbacks(struct chtls_sock *csk) -{ - struct sock *sk = csk->sk; - - if (unlikely(sk->sk_user_data && - !csk_flag_nochk(csk, CSK_CALLBACKS_CHKD))) - csk_set_flag(csk, CSK_CALLBACKS_CHKD); -} - -/* - * Handles Rx data that arrives in a state where the socket isn't accepting - * new data. - */ -static void handle_excess_rx(struct sock *sk, struct sk_buff *skb) -{ - if (!csk_flag(sk, CSK_ABORT_SHUTDOWN)) - chtls_abort_conn(sk, skb); - - kfree_skb(skb); -} - -static void chtls_recv_data(struct sock *sk, struct sk_buff *skb) -{ - struct cpl_rx_data *hdr = cplhdr(skb) + RSS_HDR; - struct chtls_sock *csk; - struct tcp_sock *tp; - - csk = rcu_dereference_sk_user_data(sk); - tp = tcp_sk(sk); - - if (unlikely(sk->sk_shutdown & RCV_SHUTDOWN)) { - handle_excess_rx(sk, skb); - return; - } - - ULP_SKB_CB(skb)->seq = ntohl(hdr->seq); - ULP_SKB_CB(skb)->psh = hdr->psh; - skb_ulp_mode(skb) = ULP_MODE_NONE; - - skb_reset_transport_header(skb); - __skb_pull(skb, sizeof(*hdr) + RSS_HDR); - if (!skb->data_len) - __skb_trim(skb, ntohs(hdr->len)); - - if (unlikely(hdr->urg)) - handle_urg_ptr(sk, tp->rcv_nxt + ntohs(hdr->urg)); - if (unlikely(tp->urg_data == TCP_URG_NOTYET && - tp->urg_seq - tp->rcv_nxt < skb->len)) - tp->urg_data = TCP_URG_VALID | - skb->data[tp->urg_seq - tp->rcv_nxt]; - - if (unlikely(hdr->dack_mode != csk->delack_mode)) { - csk->delack_mode = hdr->dack_mode; - csk->delack_seq = tp->rcv_nxt; - } - - tcp_hdr(skb)->fin = 0; - tp->rcv_nxt += skb->len; - - __skb_queue_tail(&sk->sk_receive_queue, skb); - - if (!sock_flag(sk, SOCK_DEAD)) { - check_sk_callbacks(csk); - sk->sk_data_ready(sk); - } -} - -static int chtls_rx_data(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_rx_data *req = cplhdr(skb) + RSS_HDR; - unsigned int hwtid = GET_TID(req); - struct sock *sk; - - sk = lookup_tid(cdev->tids, hwtid); - if (unlikely(!sk)) { - pr_err("can't find conn. for hwtid %u.\n", hwtid); - return -EINVAL; - } - skb_dstref_steal(skb); - process_cpl_msg(chtls_recv_data, sk, skb); - return 0; -} - -static void chtls_recv_pdu(struct sock *sk, struct sk_buff *skb) -{ - struct cpl_tls_data *hdr = cplhdr(skb); - struct chtls_sock *csk; - struct chtls_hws *tlsk; - struct tcp_sock *tp; - - csk = rcu_dereference_sk_user_data(sk); - tlsk = &csk->tlshws; - tp = tcp_sk(sk); - - if (unlikely(sk->sk_shutdown & RCV_SHUTDOWN)) { - handle_excess_rx(sk, skb); - return; - } - - ULP_SKB_CB(skb)->seq = ntohl(hdr->seq); - ULP_SKB_CB(skb)->flags = 0; - skb_ulp_mode(skb) = ULP_MODE_TLS; - - skb_reset_transport_header(skb); - __skb_pull(skb, sizeof(*hdr)); - if (!skb->data_len) - __skb_trim(skb, - CPL_TLS_DATA_LENGTH_G(ntohl(hdr->length_pkd))); - - if (unlikely(tp->urg_data == TCP_URG_NOTYET && tp->urg_seq - - tp->rcv_nxt < skb->len)) - tp->urg_data = TCP_URG_VALID | - skb->data[tp->urg_seq - tp->rcv_nxt]; - - tcp_hdr(skb)->fin = 0; - tlsk->pldlen = CPL_TLS_DATA_LENGTH_G(ntohl(hdr->length_pkd)); - __skb_queue_tail(&tlsk->sk_recv_queue, skb); -} - -static int chtls_rx_pdu(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_tls_data *req = cplhdr(skb); - unsigned int hwtid = GET_TID(req); - struct sock *sk; - - sk = lookup_tid(cdev->tids, hwtid); - if (unlikely(!sk)) { - pr_err("can't find conn. for hwtid %u.\n", hwtid); - return -EINVAL; - } - skb_dstref_steal(skb); - process_cpl_msg(chtls_recv_pdu, sk, skb); - return 0; -} - -static void chtls_set_hdrlen(struct sk_buff *skb, unsigned int nlen) -{ - struct tlsrx_cmp_hdr *tls_cmp_hdr = cplhdr(skb); - - skb->hdr_len = ntohs((__force __be16)tls_cmp_hdr->length); - tls_cmp_hdr->length = ntohs((__force __be16)nlen); -} - -static void chtls_rx_hdr(struct sock *sk, struct sk_buff *skb) -{ - struct tlsrx_cmp_hdr *tls_hdr_pkt; - struct cpl_rx_tls_cmp *cmp_cpl; - struct sk_buff *skb_rec; - struct chtls_sock *csk; - struct chtls_hws *tlsk; - struct tcp_sock *tp; - - cmp_cpl = cplhdr(skb); - csk = rcu_dereference_sk_user_data(sk); - tlsk = &csk->tlshws; - tp = tcp_sk(sk); - - ULP_SKB_CB(skb)->seq = ntohl(cmp_cpl->seq); - ULP_SKB_CB(skb)->flags = 0; - - skb_reset_transport_header(skb); - __skb_pull(skb, sizeof(*cmp_cpl)); - tls_hdr_pkt = (struct tlsrx_cmp_hdr *)skb->data; - if (tls_hdr_pkt->res_to_mac_error & TLSRX_HDR_PKT_ERROR_M) - tls_hdr_pkt->type = CONTENT_TYPE_ERROR; - if (!skb->data_len) - __skb_trim(skb, TLS_HEADER_LENGTH); - - tp->rcv_nxt += - CPL_RX_TLS_CMP_PDULENGTH_G(ntohl(cmp_cpl->pdulength_length)); - - ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_TLS_HDR; - skb_rec = __skb_dequeue(&tlsk->sk_recv_queue); - if (!skb_rec) { - __skb_queue_tail(&sk->sk_receive_queue, skb); - } else { - chtls_set_hdrlen(skb, tlsk->pldlen); - tlsk->pldlen = 0; - __skb_queue_tail(&sk->sk_receive_queue, skb); - __skb_queue_tail(&sk->sk_receive_queue, skb_rec); - } - - if (!sock_flag(sk, SOCK_DEAD)) { - check_sk_callbacks(csk); - sk->sk_data_ready(sk); - } -} - -static int chtls_rx_cmp(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_rx_tls_cmp *req = cplhdr(skb); - unsigned int hwtid = GET_TID(req); - struct sock *sk; - - sk = lookup_tid(cdev->tids, hwtid); - if (unlikely(!sk)) { - pr_err("can't find conn. for hwtid %u.\n", hwtid); - return -EINVAL; - } - skb_dstref_steal(skb); - process_cpl_msg(chtls_rx_hdr, sk, skb); - - return 0; -} - -static void chtls_timewait(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tp->rcv_nxt++; - tp->rx_opt.ts_recent_stamp = ktime_get_seconds(); - tp->srtt_us = 0; - tcp_time_wait(sk, TCP_TIME_WAIT, 0); -} - -static void chtls_peer_close(struct sock *sk, struct sk_buff *skb) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) - goto out; - - sk->sk_shutdown |= RCV_SHUTDOWN; - sock_set_flag(sk, SOCK_DONE); - - switch (sk->sk_state) { - case TCP_SYN_RECV: - case TCP_ESTABLISHED: - tcp_set_state(sk, TCP_CLOSE_WAIT); - break; - case TCP_FIN_WAIT1: - tcp_set_state(sk, TCP_CLOSING); - break; - case TCP_FIN_WAIT2: - chtls_release_resources(sk); - if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) - chtls_conn_done(sk); - else - chtls_timewait(sk); - break; - default: - pr_info("cpl_peer_close in bad state %d\n", sk->sk_state); - } - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - /* Do not send POLL_HUP for half duplex close. */ - - if ((sk->sk_shutdown & SEND_SHUTDOWN) || - sk->sk_state == TCP_CLOSE) - sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_HUP); - else - sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); - } -out: - kfree_skb(skb); -} - -static void chtls_close_con_rpl(struct sock *sk, struct sk_buff *skb) -{ - struct cpl_close_con_rpl *rpl = cplhdr(skb) + RSS_HDR; - struct chtls_sock *csk; - struct tcp_sock *tp; - - csk = rcu_dereference_sk_user_data(sk); - - if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) - goto out; - - tp = tcp_sk(sk); - - tp->snd_una = ntohl(rpl->snd_nxt) - 1; /* exclude FIN */ - - switch (sk->sk_state) { - case TCP_CLOSING: - chtls_release_resources(sk); - if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) - chtls_conn_done(sk); - else - chtls_timewait(sk); - break; - case TCP_LAST_ACK: - chtls_release_resources(sk); - chtls_conn_done(sk); - break; - case TCP_FIN_WAIT1: - tcp_set_state(sk, TCP_FIN_WAIT2); - sk->sk_shutdown |= SEND_SHUTDOWN; - - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - else if (tcp_sk(sk)->linger2 < 0 && - !csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN)) - chtls_abort_conn(sk, skb); - else if (csk_flag_nochk(csk, CSK_TX_DATA_SENT)) - chtls_set_quiesce_ctrl(sk, 0); - break; - default: - pr_info("close_con_rpl in bad state %d\n", sk->sk_state); - } -out: - kfree_skb(skb); -} - -static struct sk_buff *get_cpl_skb(struct sk_buff *skb, - size_t len, gfp_t gfp) -{ - if (likely(!skb_is_nonlinear(skb) && !skb_cloned(skb))) { - WARN_ONCE(skb->len < len, "skb alloc error"); - __skb_trim(skb, len); - skb_get(skb); - } else { - skb = alloc_skb(len, gfp); - if (skb) - __skb_put(skb, len); - } - return skb; -} - -static void set_abort_rpl_wr(struct sk_buff *skb, unsigned int tid, - int cmd) -{ - struct cpl_abort_rpl *rpl = cplhdr(skb); - - INIT_TP_WR_CPL(rpl, CPL_ABORT_RPL, tid); - rpl->cmd = cmd; -} - -static void send_defer_abort_rpl(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_abort_req_rss *req = cplhdr(skb); - struct sk_buff *reply_skb; - - reply_skb = alloc_skb(sizeof(struct cpl_abort_rpl), - GFP_KERNEL | __GFP_NOFAIL); - __skb_put(reply_skb, sizeof(struct cpl_abort_rpl)); - set_abort_rpl_wr(reply_skb, GET_TID(req), - (req->status & CPL_ABORT_NO_RST)); - set_wr_txq(reply_skb, CPL_PRIORITY_DATA, req->status >> 1); - cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb); - kfree_skb(skb); -} - -/* - * Add an skb to the deferred skb queue for processing from process context. - */ -static void t4_defer_reply(struct sk_buff *skb, struct chtls_dev *cdev, - defer_handler_t handler) -{ - DEFERRED_SKB_CB(skb)->handler = handler; - spin_lock_bh(&cdev->deferq.lock); - __skb_queue_tail(&cdev->deferq, skb); - if (skb_queue_len(&cdev->deferq) == 1) - schedule_work(&cdev->deferq_task); - spin_unlock_bh(&cdev->deferq.lock); -} - -static void chtls_send_abort_rpl(struct sock *sk, struct sk_buff *skb, - struct chtls_dev *cdev, - int status, int queue) -{ - struct cpl_abort_req_rss *req = cplhdr(skb) + RSS_HDR; - struct sk_buff *reply_skb; - struct chtls_sock *csk; - unsigned int tid; - - csk = rcu_dereference_sk_user_data(sk); - tid = GET_TID(req); - - reply_skb = get_cpl_skb(skb, sizeof(struct cpl_abort_rpl), gfp_any()); - if (!reply_skb) { - req->status = (queue << 1) | status; - t4_defer_reply(skb, cdev, send_defer_abort_rpl); - return; - } - - set_abort_rpl_wr(reply_skb, tid, status); - kfree_skb(skb); - set_wr_txq(reply_skb, CPL_PRIORITY_DATA, queue); - if (csk_conn_inline(csk)) { - struct l2t_entry *e = csk->l2t_entry; - - if (e && sk->sk_state != TCP_SYN_RECV) { - cxgb4_l2t_send(csk->egress_dev, reply_skb, e); - return; - } - } - cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb); -} - -/* - * This is run from a listener's backlog to abort a child connection in - * SYN_RCV state (i.e., one on the listener's SYN queue). - */ -static void bl_abort_syn_rcv(struct sock *lsk, struct sk_buff *skb) -{ - struct chtls_sock *csk; - struct sock *child; - int queue; - - child = skb->sk; - csk = rcu_dereference_sk_user_data(child); - queue = csk->txq_idx; - - skb->sk = NULL; - chtls_send_abort_rpl(child, skb, BLOG_SKB_CB(skb)->cdev, - CPL_ABORT_NO_RST, queue); - do_abort_syn_rcv(child, lsk); -} - -static int abort_syn_rcv(struct sock *sk, struct sk_buff *skb) -{ - const struct request_sock *oreq; - struct listen_ctx *listen_ctx; - struct chtls_sock *csk; - struct chtls_dev *cdev; - struct sock *psk; - void *ctx; - - csk = sk->sk_user_data; - oreq = csk->passive_reap_next; - cdev = csk->cdev; - - if (!oreq) - return -1; - - ctx = lookup_stid(cdev->tids, oreq->ts_recent); - if (!ctx) - return -1; - - listen_ctx = (struct listen_ctx *)ctx; - psk = listen_ctx->lsk; - - bh_lock_sock(psk); - if (!sock_owned_by_user(psk)) { - int queue = csk->txq_idx; - - chtls_send_abort_rpl(sk, skb, cdev, CPL_ABORT_NO_RST, queue); - do_abort_syn_rcv(sk, psk); - } else { - skb->sk = sk; - BLOG_SKB_CB(skb)->backlog_rcv = bl_abort_syn_rcv; - __sk_add_backlog(psk, skb); - } - bh_unlock_sock(psk); - return 0; -} - -static void chtls_abort_req_rss(struct sock *sk, struct sk_buff *skb) -{ - const struct cpl_abort_req_rss *req = cplhdr(skb) + RSS_HDR; - struct chtls_sock *csk = sk->sk_user_data; - int rst_status = CPL_ABORT_NO_RST; - int queue = csk->txq_idx; - - if (is_neg_adv(req->status)) { - kfree_skb(skb); - return; - } - - csk_reset_flag(csk, CSK_ABORT_REQ_RCVD); - - if (!csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN) && - !csk_flag_nochk(csk, CSK_TX_DATA_SENT)) { - struct tcp_sock *tp = tcp_sk(sk); - - if (send_tx_flowc_wr(sk, 0, tp->snd_nxt, tp->rcv_nxt) < 0) - WARN_ONCE(1, "send_tx_flowc error"); - csk_set_flag(csk, CSK_TX_DATA_SENT); - } - - csk_set_flag(csk, CSK_ABORT_SHUTDOWN); - - if (!csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) { - sk->sk_err = ETIMEDOUT; - - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); - - if (sk->sk_state == TCP_SYN_RECV && !abort_syn_rcv(sk, skb)) - return; - - } - - chtls_send_abort_rpl(sk, skb, BLOG_SKB_CB(skb)->cdev, - rst_status, queue); - chtls_release_resources(sk); - chtls_conn_done(sk); -} - -static void chtls_abort_rpl_rss(struct sock *sk, struct sk_buff *skb) -{ - struct cpl_abort_rpl_rss *rpl = cplhdr(skb) + RSS_HDR; - struct chtls_sock *csk; - struct chtls_dev *cdev; - - csk = rcu_dereference_sk_user_data(sk); - cdev = csk->cdev; - - if (csk_flag_nochk(csk, CSK_ABORT_RPL_PENDING)) { - csk_reset_flag(csk, CSK_ABORT_RPL_PENDING); - if (!csk_flag_nochk(csk, CSK_ABORT_REQ_RCVD)) { - if (sk->sk_state == TCP_SYN_SENT) { - cxgb4_remove_tid(cdev->tids, - csk->port_id, - GET_TID(rpl), - sk->sk_family); - sock_put(sk); - } - chtls_release_resources(sk); - chtls_conn_done(sk); - } - } - kfree_skb(skb); -} - -static int chtls_conn_cpl(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_peer_close *req = cplhdr(skb) + RSS_HDR; - void (*fn)(struct sock *sk, struct sk_buff *skb); - unsigned int hwtid = GET_TID(req); - struct chtls_sock *csk; - struct sock *sk; - u8 opcode; - - opcode = ((const struct rss_header *)cplhdr(skb))->opcode; - - sk = lookup_tid(cdev->tids, hwtid); - if (!sk) - goto rel_skb; - - csk = sk->sk_user_data; - - switch (opcode) { - case CPL_PEER_CLOSE: - fn = chtls_peer_close; - break; - case CPL_CLOSE_CON_RPL: - fn = chtls_close_con_rpl; - break; - case CPL_ABORT_REQ_RSS: - /* - * Save the offload device in the skb, we may process this - * message after the socket has closed. - */ - BLOG_SKB_CB(skb)->cdev = csk->cdev; - fn = chtls_abort_req_rss; - break; - case CPL_ABORT_RPL_RSS: - fn = chtls_abort_rpl_rss; - break; - default: - goto rel_skb; - } - - process_cpl_msg(fn, sk, skb); - return 0; - -rel_skb: - kfree_skb(skb); - return 0; -} - -static void chtls_rx_ack(struct sock *sk, struct sk_buff *skb) -{ - struct cpl_fw4_ack *hdr = cplhdr(skb) + RSS_HDR; - struct chtls_sock *csk = sk->sk_user_data; - struct tcp_sock *tp = tcp_sk(sk); - u32 credits = hdr->credits; - u32 snd_una; - - snd_una = ntohl(hdr->snd_una); - csk->wr_credits += credits; - - if (csk->wr_unacked > csk->wr_max_credits - csk->wr_credits) - csk->wr_unacked = csk->wr_max_credits - csk->wr_credits; - - while (credits) { - struct sk_buff *pskb = csk->wr_skb_head; - u32 csum; - - if (unlikely(!pskb)) { - if (csk->wr_nondata) - csk->wr_nondata -= credits; - break; - } - csum = (__force u32)pskb->csum; - if (unlikely(credits < csum)) { - pskb->csum = (__force __wsum)(csum - credits); - break; - } - dequeue_wr(sk); - credits -= csum; - kfree_skb(pskb); - } - if (hdr->seq_vld & CPL_FW4_ACK_FLAGS_SEQVAL) { - if (unlikely(before(snd_una, tp->snd_una))) { - kfree_skb(skb); - return; - } - - if (tp->snd_una != snd_una) { - tp->snd_una = snd_una; - tp->rcv_tstamp = tcp_jiffies32; - if (tp->snd_una == tp->snd_nxt && - !csk_flag_nochk(csk, CSK_TX_FAILOVER)) - csk_reset_flag(csk, CSK_TX_WAIT_IDLE); - } - } - - if (hdr->seq_vld & CPL_FW4_ACK_FLAGS_CH) { - unsigned int fclen16 = roundup(failover_flowc_wr_len, 16); - - csk->wr_credits -= fclen16; - csk_reset_flag(csk, CSK_TX_WAIT_IDLE); - csk_reset_flag(csk, CSK_TX_FAILOVER); - } - if (skb_queue_len(&csk->txq) && chtls_push_frames(csk, 0)) - sk->sk_write_space(sk); - - kfree_skb(skb); -} - -static int chtls_wr_ack(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_fw4_ack *rpl = cplhdr(skb) + RSS_HDR; - unsigned int hwtid = GET_TID(rpl); - struct sock *sk; - - sk = lookup_tid(cdev->tids, hwtid); - if (unlikely(!sk)) { - pr_err("can't find conn. for hwtid %u.\n", hwtid); - return -EINVAL; - } - process_cpl_msg(chtls_rx_ack, sk, skb); - - return 0; -} - -static int chtls_set_tcb_rpl(struct chtls_dev *cdev, struct sk_buff *skb) -{ - struct cpl_set_tcb_rpl *rpl = cplhdr(skb) + RSS_HDR; - unsigned int hwtid = GET_TID(rpl); - struct sock *sk; - - sk = lookup_tid(cdev->tids, hwtid); - - /* return EINVAL if socket doesn't exist */ - if (!sk) - return -EINVAL; - - /* Reusing the skb as size of cpl_set_tcb_field structure - * is greater than cpl_abort_req - */ - if (TCB_COOKIE_G(rpl->cookie) == TCB_FIELD_COOKIE_TFLAG) - chtls_send_abort(sk, CPL_ABORT_SEND_RST, NULL); - - kfree_skb(skb); - return 0; -} - -chtls_handler_func chtls_handlers[NUM_CPL_CMDS] = { - [CPL_PASS_OPEN_RPL] = chtls_pass_open_rpl, - [CPL_CLOSE_LISTSRV_RPL] = chtls_close_listsrv_rpl, - [CPL_PASS_ACCEPT_REQ] = chtls_pass_accept_req, - [CPL_PASS_ESTABLISH] = chtls_pass_establish, - [CPL_RX_DATA] = chtls_rx_data, - [CPL_TLS_DATA] = chtls_rx_pdu, - [CPL_RX_TLS_CMP] = chtls_rx_cmp, - [CPL_PEER_CLOSE] = chtls_conn_cpl, - [CPL_CLOSE_CON_RPL] = chtls_conn_cpl, - [CPL_ABORT_REQ_RSS] = chtls_conn_cpl, - [CPL_ABORT_RPL_RSS] = chtls_conn_cpl, - [CPL_FW4_ACK] = chtls_wr_ack, - [CPL_SET_TCB_RPL] = chtls_set_tcb_rpl, -}; diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.h b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.h deleted file mode 100644 index 29ceff5a5fcb..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.h +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2018 Chelsio Communications, Inc. - */ - -#ifndef __CHTLS_CM_H__ -#define __CHTLS_CM_H__ - -/* - * TCB settings - */ -/* 3:0 */ -#define TCB_ULP_TYPE_W 0 -#define TCB_ULP_TYPE_S 0 -#define TCB_ULP_TYPE_M 0xfULL -#define TCB_ULP_TYPE_V(x) ((x) << TCB_ULP_TYPE_S) - -/* 11:4 */ -#define TCB_ULP_RAW_W 0 -#define TCB_ULP_RAW_S 4 -#define TCB_ULP_RAW_M 0xffULL -#define TCB_ULP_RAW_V(x) ((x) << TCB_ULP_RAW_S) - -#define TF_TLS_KEY_SIZE_S 7 -#define TF_TLS_KEY_SIZE_V(x) ((x) << TF_TLS_KEY_SIZE_S) - -#define TF_TLS_CONTROL_S 2 -#define TF_TLS_CONTROL_V(x) ((x) << TF_TLS_CONTROL_S) - -#define TF_TLS_ACTIVE_S 1 -#define TF_TLS_ACTIVE_V(x) ((x) << TF_TLS_ACTIVE_S) - -#define TF_TLS_ENABLE_S 0 -#define TF_TLS_ENABLE_V(x) ((x) << TF_TLS_ENABLE_S) - -#define TF_RX_QUIESCE_S 15 -#define TF_RX_QUIESCE_V(x) ((x) << TF_RX_QUIESCE_S) - -/* - * Max receive window supported by HW in bytes. Only a small part of it can - * be set through option0, the rest needs to be set through RX_DATA_ACK. - */ -#define MAX_RCV_WND ((1U << 27) - 1) -#define MAX_MSS 65536 - -/* - * Min receive window. We want it to be large enough to accommodate receive - * coalescing, handle jumbo frames, and not trigger sender SWS avoidance. - */ -#define MIN_RCV_WND (24 * 1024U) -#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000)) - -/* for TX: a skb must have a headroom of at least TX_HEADER_LEN bytes */ -#define TX_HEADER_LEN \ - (sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr)) -#define TX_TLSHDR_LEN \ - (sizeof(struct fw_tlstx_data_wr) + sizeof(struct cpl_tx_tls_sfo) + \ - sizeof(struct sge_opaque_hdr)) -#define TXDATA_SKB_LEN 128 - -enum { - CPL_TX_TLS_SFO_TYPE_CCS, - CPL_TX_TLS_SFO_TYPE_ALERT, - CPL_TX_TLS_SFO_TYPE_HANDSHAKE, - CPL_TX_TLS_SFO_TYPE_DATA, - CPL_TX_TLS_SFO_TYPE_HEARTBEAT, -}; - -enum { - TLS_HDR_TYPE_CCS = 20, - TLS_HDR_TYPE_ALERT, - TLS_HDR_TYPE_HANDSHAKE, - TLS_HDR_TYPE_RECORD, - TLS_HDR_TYPE_HEARTBEAT, -}; - -typedef void (*defer_handler_t)(struct chtls_dev *dev, struct sk_buff *skb); -extern struct request_sock_ops chtls_rsk_ops; -extern struct request_sock_ops chtls_rsk_opsv6; - -struct deferred_skb_cb { - defer_handler_t handler; - struct chtls_dev *dev; -}; - -#define DEFERRED_SKB_CB(skb) ((struct deferred_skb_cb *)(skb)->cb) -#define failover_flowc_wr_len offsetof(struct fw_flowc_wr, mnemval[3]) -#define WR_SKB_CB(skb) ((struct wr_skb_cb *)(skb)->cb) -#define ACCEPT_QUEUE(sk) (&inet_csk(sk)->icsk_accept_queue.rskq_accept_head) - -#define SND_WSCALE(tp) ((tp)->rx_opt.snd_wscale) -#define RCV_WSCALE(tp) ((tp)->rx_opt.rcv_wscale) -#define USER_MSS(tp) (READ_ONCE((tp)->rx_opt.user_mss)) -#define TS_RECENT_STAMP(tp) ((tp)->rx_opt.ts_recent_stamp) -#define WSCALE_OK(tp) ((tp)->rx_opt.wscale_ok) -#define TSTAMP_OK(tp) ((tp)->rx_opt.tstamp_ok) -#define SACK_OK(tp) ((tp)->rx_opt.sack_ok) - -/* TLS SKB */ -#define skb_ulp_tls_inline(skb) (ULP_SKB_CB(skb)->ulp.tls.ofld) -#define skb_ulp_tls_iv_imm(skb) (ULP_SKB_CB(skb)->ulp.tls.iv) - -void chtls_defer_reply(struct sk_buff *skb, struct chtls_dev *dev, - defer_handler_t handler); - -/* - * Returns true if the socket is in one of the supplied states. - */ -static inline unsigned int sk_in_state(const struct sock *sk, - unsigned int states) -{ - return states & (1 << sk->sk_state); -} - -static void chtls_rsk_destructor(struct request_sock *req) -{ - /* do nothing */ -} - -static inline void chtls_init_rsk_ops(struct proto *chtls_tcp_prot, - struct request_sock_ops *chtls_tcp_ops, - struct proto *tcp_prot, int family) -{ - memset(chtls_tcp_ops, 0, sizeof(*chtls_tcp_ops)); - chtls_tcp_ops->family = family; - chtls_tcp_ops->obj_size = sizeof(struct tcp_request_sock); - chtls_tcp_ops->destructor = chtls_rsk_destructor; - chtls_tcp_ops->slab = tcp_prot->rsk_prot->slab; - chtls_tcp_prot->rsk_prot = chtls_tcp_ops; -} - -static inline void chtls_reqsk_free(struct request_sock *req) -{ - if (req->rsk_listener) - sock_put(req->rsk_listener); - kmem_cache_free(req->rsk_ops->slab, req); -} - -#define DECLARE_TASK_FUNC(task, task_param) \ - static void task(struct work_struct *task_param) - -static inline void sk_wakeup_sleepers(struct sock *sk, bool interruptable) -{ - struct socket_wq *wq; - - rcu_read_lock(); - wq = rcu_dereference(sk->sk_wq); - if (skwq_has_sleeper(wq)) { - if (interruptable) - wake_up_interruptible(sk_sleep(sk)); - else - wake_up_all(sk_sleep(sk)); - } - rcu_read_unlock(); -} - -static inline void chtls_set_req_port(struct request_sock *oreq, - __be16 source, __be16 dest) -{ - inet_rsk(oreq)->ir_rmt_port = source; - inet_rsk(oreq)->ir_num = ntohs(dest); -} - -static inline void chtls_set_req_addr(struct request_sock *oreq, - __be32 local_ip, __be32 peer_ip) -{ - inet_rsk(oreq)->ir_loc_addr = local_ip; - inet_rsk(oreq)->ir_rmt_addr = peer_ip; -} - -static inline void chtls_free_skb(struct sock *sk, struct sk_buff *skb) -{ - skb_dstref_steal(skb); - __skb_unlink(skb, &sk->sk_receive_queue); - __kfree_skb(skb); -} - -static inline void chtls_kfree_skb(struct sock *sk, struct sk_buff *skb) -{ - skb_dstref_steal(skb); - __skb_unlink(skb, &sk->sk_receive_queue); - kfree_skb(skb); -} - -static inline void chtls_reset_wr_list(struct chtls_sock *csk) -{ - csk->wr_skb_head = NULL; - csk->wr_skb_tail = NULL; -} - -static inline void enqueue_wr(struct chtls_sock *csk, struct sk_buff *skb) -{ - WR_SKB_CB(skb)->next_wr = NULL; - - skb_get(skb); - - if (!csk->wr_skb_head) - csk->wr_skb_head = skb; - else - WR_SKB_CB(csk->wr_skb_tail)->next_wr = skb; - csk->wr_skb_tail = skb; -} - -static inline struct sk_buff *dequeue_wr(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct sk_buff *skb = NULL; - - skb = csk->wr_skb_head; - - if (likely(skb)) { - /* Don't bother clearing the tail */ - csk->wr_skb_head = WR_SKB_CB(skb)->next_wr; - WR_SKB_CB(skb)->next_wr = NULL; - } - return skb; -} -#endif diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c deleted file mode 100644 index d84473ca844d..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c +++ /dev/null @@ -1,462 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2018 Chelsio Communications, Inc. - * - * Written by: Atul Gupta (atul.gupta@chelsio.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "chtls.h" -#include "chtls_cm.h" - -static void __set_tcb_field_direct(struct chtls_sock *csk, - struct cpl_set_tcb_field *req, u16 word, - u64 mask, u64 val, u8 cookie, int no_reply) -{ - struct ulptx_idata *sc; - - INIT_TP_WR_CPL(req, CPL_SET_TCB_FIELD, csk->tid); - req->wr.wr_mid |= htonl(FW_WR_FLOWID_V(csk->tid)); - req->reply_ctrl = htons(NO_REPLY_V(no_reply) | - QUEUENO_V(csk->rss_qid)); - req->word_cookie = htons(TCB_WORD_V(word) | TCB_COOKIE_V(cookie)); - req->mask = cpu_to_be64(mask); - req->val = cpu_to_be64(val); - sc = (struct ulptx_idata *)(req + 1); - sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP)); - sc->len = htonl(0); -} - -static void __set_tcb_field(struct sock *sk, struct sk_buff *skb, u16 word, - u64 mask, u64 val, u8 cookie, int no_reply) -{ - struct cpl_set_tcb_field *req; - struct chtls_sock *csk; - struct ulptx_idata *sc; - unsigned int wrlen; - - wrlen = roundup(sizeof(*req) + sizeof(*sc), 16); - csk = rcu_dereference_sk_user_data(sk); - - req = (struct cpl_set_tcb_field *)__skb_put(skb, wrlen); - __set_tcb_field_direct(csk, req, word, mask, val, cookie, no_reply); - set_wr_txq(skb, CPL_PRIORITY_CONTROL, csk->port_id); -} - -/* - * Send control message to HW, message go as immediate data and packet - * is freed immediately. - */ -static int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val) -{ - struct cpl_set_tcb_field *req; - unsigned int credits_needed; - struct chtls_sock *csk; - struct ulptx_idata *sc; - struct sk_buff *skb; - unsigned int wrlen; - int ret; - - wrlen = roundup(sizeof(*req) + sizeof(*sc), 16); - - skb = alloc_skb(wrlen, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - credits_needed = DIV_ROUND_UP(wrlen, 16); - csk = rcu_dereference_sk_user_data(sk); - - __set_tcb_field(sk, skb, word, mask, val, 0, 1); - skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA); - csk->wr_credits -= credits_needed; - csk->wr_unacked += credits_needed; - enqueue_wr(csk, skb); - ret = cxgb4_ofld_send(csk->egress_dev, skb); - if (ret < 0) - kfree_skb(skb); - return ret < 0 ? ret : 0; -} - -void chtls_set_tcb_field_rpl_skb(struct sock *sk, u16 word, - u64 mask, u64 val, u8 cookie, - int through_l2t) -{ - struct sk_buff *skb; - unsigned int wrlen; - - wrlen = sizeof(struct cpl_set_tcb_field) + sizeof(struct ulptx_idata); - wrlen = roundup(wrlen, 16); - - skb = alloc_skb(wrlen, GFP_KERNEL | __GFP_NOFAIL); - if (!skb) - return; - - __set_tcb_field(sk, skb, word, mask, val, cookie, 0); - send_or_defer(sk, tcp_sk(sk), skb, through_l2t); -} - -static int chtls_set_tcb_keyid(struct sock *sk, int keyid) -{ - return chtls_set_tcb_field(sk, 31, 0xFFFFFFFFULL, keyid); -} - -static int chtls_set_tcb_seqno(struct sock *sk) -{ - return chtls_set_tcb_field(sk, 28, ~0ULL, 0); -} - -static int chtls_set_tcb_quiesce(struct sock *sk, int val) -{ - return chtls_set_tcb_field(sk, 1, (1ULL << TF_RX_QUIESCE_S), - TF_RX_QUIESCE_V(val)); -} - -void chtls_set_quiesce_ctrl(struct sock *sk, int val) -{ - struct chtls_sock *csk; - struct sk_buff *skb; - unsigned int wrlen; - int ret; - - wrlen = sizeof(struct cpl_set_tcb_field) + sizeof(struct ulptx_idata); - wrlen = roundup(wrlen, 16); - - skb = alloc_skb(wrlen, GFP_ATOMIC); - if (!skb) - return; - - csk = rcu_dereference_sk_user_data(sk); - - __set_tcb_field(sk, skb, 1, TF_RX_QUIESCE_V(1), 0, 0, 1); - set_wr_txq(skb, CPL_PRIORITY_CONTROL, csk->port_id); - ret = cxgb4_ofld_send(csk->egress_dev, skb); - if (ret < 0) - kfree_skb(skb); -} - -/* TLS Key bitmap processing */ -int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi) -{ - unsigned int num_key_ctx, bsize; - int ksize; - - num_key_ctx = (lldi->vr->key.size / TLS_KEY_CONTEXT_SZ); - bsize = BITS_TO_LONGS(num_key_ctx); - - cdev->kmap.size = num_key_ctx; - cdev->kmap.available = bsize; - ksize = sizeof(*cdev->kmap.addr) * bsize; - cdev->kmap.addr = kvzalloc(ksize, GFP_KERNEL); - if (!cdev->kmap.addr) - return -ENOMEM; - - cdev->kmap.start = lldi->vr->key.start; - spin_lock_init(&cdev->kmap.lock); - return 0; -} - -static int get_new_keyid(struct chtls_sock *csk, u32 optname) -{ - struct net_device *dev = csk->egress_dev; - struct chtls_dev *cdev = csk->cdev; - struct chtls_hws *hws; - struct adapter *adap; - int keyid; - - adap = netdev2adap(dev); - hws = &csk->tlshws; - - spin_lock_bh(&cdev->kmap.lock); - keyid = find_first_zero_bit(cdev->kmap.addr, cdev->kmap.size); - if (keyid < cdev->kmap.size) { - __set_bit(keyid, cdev->kmap.addr); - if (optname == TLS_RX) - hws->rxkey = keyid; - else - hws->txkey = keyid; - atomic_inc(&adap->chcr_stats.tls_key); - } else { - keyid = -1; - } - spin_unlock_bh(&cdev->kmap.lock); - return keyid; -} - -void free_tls_keyid(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct net_device *dev = csk->egress_dev; - struct chtls_dev *cdev = csk->cdev; - struct chtls_hws *hws; - struct adapter *adap; - - if (!cdev->kmap.addr) - return; - - adap = netdev2adap(dev); - hws = &csk->tlshws; - - spin_lock_bh(&cdev->kmap.lock); - if (hws->rxkey >= 0) { - __clear_bit(hws->rxkey, cdev->kmap.addr); - atomic_dec(&adap->chcr_stats.tls_key); - hws->rxkey = -1; - } - if (hws->txkey >= 0) { - __clear_bit(hws->txkey, cdev->kmap.addr); - atomic_dec(&adap->chcr_stats.tls_key); - hws->txkey = -1; - } - spin_unlock_bh(&cdev->kmap.lock); -} - -unsigned int keyid_to_addr(int start_addr, int keyid) -{ - return (start_addr + (keyid * TLS_KEY_CONTEXT_SZ)) >> 5; -} - -static void chtls_rxkey_ivauth(struct _key_ctx *kctx) -{ - kctx->iv_to_auth = cpu_to_be64(KEYCTX_TX_WR_IV_V(6ULL) | - KEYCTX_TX_WR_AAD_V(1ULL) | - KEYCTX_TX_WR_AADST_V(5ULL) | - KEYCTX_TX_WR_CIPHER_V(14ULL) | - KEYCTX_TX_WR_CIPHERST_V(0ULL) | - KEYCTX_TX_WR_AUTH_V(14ULL) | - KEYCTX_TX_WR_AUTHST_V(16ULL) | - KEYCTX_TX_WR_AUTHIN_V(16ULL)); -} - -static int chtls_key_info(struct chtls_sock *csk, - struct _key_ctx *kctx, - u32 keylen, u32 optname, - int cipher_type) -{ - unsigned char key[AES_MAX_KEY_SIZE]; - unsigned char *key_p, *salt; - unsigned char ghash_h[AEAD_H_SIZE]; - int ck_size, key_ctx_size, kctx_mackey_size, salt_size; - struct aes_enckey aes; - int ret; - - key_ctx_size = sizeof(struct _key_ctx) + - roundup(keylen, 16) + AEAD_H_SIZE; - - /* GCM mode of AES supports 128 and 256 bit encryption, so - * prepare key context base on GCM cipher type - */ - switch (cipher_type) { - case TLS_CIPHER_AES_GCM_128: { - struct tls12_crypto_info_aes_gcm_128 *gcm_ctx_128 = - (struct tls12_crypto_info_aes_gcm_128 *) - &csk->tlshws.crypto_info; - memcpy(key, gcm_ctx_128->key, keylen); - - key_p = gcm_ctx_128->key; - salt = gcm_ctx_128->salt; - ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_128; - salt_size = TLS_CIPHER_AES_GCM_128_SALT_SIZE; - kctx_mackey_size = CHCR_KEYCTX_MAC_KEY_SIZE_128; - break; - } - case TLS_CIPHER_AES_GCM_256: { - struct tls12_crypto_info_aes_gcm_256 *gcm_ctx_256 = - (struct tls12_crypto_info_aes_gcm_256 *) - &csk->tlshws.crypto_info; - memcpy(key, gcm_ctx_256->key, keylen); - - key_p = gcm_ctx_256->key; - salt = gcm_ctx_256->salt; - ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_256; - salt_size = TLS_CIPHER_AES_GCM_256_SALT_SIZE; - kctx_mackey_size = CHCR_KEYCTX_MAC_KEY_SIZE_256; - break; - } - default: - pr_err("GCM: Invalid key length %d\n", keylen); - return -EINVAL; - } - - /* Calculate the H = CIPH(K, 0 repeated 16 times). - * It will go in key context - */ - ret = aes_prepareenckey(&aes, key, keylen); - if (ret) - return ret; - - memset(ghash_h, 0, AEAD_H_SIZE); - aes_encrypt(&aes, ghash_h, ghash_h); - memzero_explicit(&aes, sizeof(aes)); - csk->tlshws.keylen = key_ctx_size; - - /* Copy the Key context */ - if (optname == TLS_RX) { - int key_ctx; - - key_ctx = ((key_ctx_size >> 4) << 3); - kctx->ctx_hdr = FILL_KEY_CRX_HDR(ck_size, - kctx_mackey_size, - 0, 0, key_ctx); - chtls_rxkey_ivauth(kctx); - } else { - kctx->ctx_hdr = FILL_KEY_CTX_HDR(ck_size, - kctx_mackey_size, - 0, 0, key_ctx_size >> 4); - } - - memcpy(kctx->salt, salt, salt_size); - memcpy(kctx->key, key_p, keylen); - memcpy(kctx->key + keylen, ghash_h, AEAD_H_SIZE); - /* erase key info from driver */ - memset(key_p, 0, keylen); - - return 0; -} - -static void chtls_set_scmd(struct chtls_sock *csk) -{ - struct chtls_hws *hws = &csk->tlshws; - - hws->scmd.seqno_numivs = - SCMD_SEQ_NO_CTRL_V(3) | - SCMD_PROTO_VERSION_V(0) | - SCMD_ENC_DEC_CTRL_V(0) | - SCMD_CIPH_AUTH_SEQ_CTRL_V(1) | - SCMD_CIPH_MODE_V(2) | - SCMD_AUTH_MODE_V(4) | - SCMD_HMAC_CTRL_V(0) | - SCMD_IV_SIZE_V(4) | - SCMD_NUM_IVS_V(1); - - hws->scmd.ivgen_hdrlen = - SCMD_IV_GEN_CTRL_V(1) | - SCMD_KEY_CTX_INLINE_V(0) | - SCMD_TLS_FRAG_ENABLE_V(1); -} - -int chtls_setkey(struct chtls_sock *csk, u32 keylen, - u32 optname, int cipher_type) -{ - struct tls_key_req *kwr; - struct chtls_dev *cdev; - struct _key_ctx *kctx; - int wrlen, klen, len; - struct sk_buff *skb; - struct sock *sk; - int keyid; - int kaddr; - int ret; - - cdev = csk->cdev; - sk = csk->sk; - - klen = roundup((keylen + AEAD_H_SIZE) + sizeof(*kctx), 32); - wrlen = roundup(sizeof(*kwr), 16); - len = klen + wrlen; - - /* Flush out-standing data before new key takes effect */ - if (optname == TLS_TX) { - lock_sock(sk); - if (skb_queue_len(&csk->txq)) - chtls_push_frames(csk, 0); - release_sock(sk); - } - - skb = alloc_skb(len, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - keyid = get_new_keyid(csk, optname); - if (keyid < 0) { - ret = -ENOSPC; - goto out_nokey; - } - - kaddr = keyid_to_addr(cdev->kmap.start, keyid); - kwr = (struct tls_key_req *)__skb_put_zero(skb, len); - kwr->wr.op_to_compl = - cpu_to_be32(FW_WR_OP_V(FW_ULPTX_WR) | FW_WR_COMPL_F | - FW_WR_ATOMIC_V(1U)); - kwr->wr.flowid_len16 = - cpu_to_be32(FW_WR_LEN16_V(DIV_ROUND_UP(len, 16) | - FW_WR_FLOWID_V(csk->tid))); - kwr->wr.protocol = 0; - kwr->wr.mfs = htons(TLS_MFS); - kwr->wr.reneg_to_write_rx = optname; - - /* ulptx command */ - kwr->req.cmd = cpu_to_be32(ULPTX_CMD_V(ULP_TX_MEM_WRITE) | - T5_ULP_MEMIO_ORDER_V(1) | - T5_ULP_MEMIO_IMM_V(1)); - kwr->req.len16 = cpu_to_be32((csk->tid << 8) | - DIV_ROUND_UP(len - sizeof(kwr->wr), 16)); - kwr->req.dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN_V(klen >> 5)); - kwr->req.lock_addr = cpu_to_be32(ULP_MEMIO_ADDR_V(kaddr)); - - /* sub command */ - kwr->sc_imm.cmd_more = cpu_to_be32(ULPTX_CMD_V(ULP_TX_SC_IMM)); - kwr->sc_imm.len = cpu_to_be32(klen); - - lock_sock(sk); - /* key info */ - kctx = (struct _key_ctx *)(kwr + 1); - ret = chtls_key_info(csk, kctx, keylen, optname, cipher_type); - if (ret) - goto out_notcb; - - if (unlikely(csk_flag(sk, CSK_ABORT_SHUTDOWN))) - goto out_notcb; - - set_wr_txq(skb, CPL_PRIORITY_DATA, csk->tlshws.txqid); - csk->wr_credits -= DIV_ROUND_UP(len, 16); - csk->wr_unacked += DIV_ROUND_UP(len, 16); - enqueue_wr(csk, skb); - cxgb4_ofld_send(csk->egress_dev, skb); - skb = NULL; - - chtls_set_scmd(csk); - /* Clear quiesce for Rx key */ - if (optname == TLS_RX) { - ret = chtls_set_tcb_keyid(sk, keyid); - if (ret) - goto out_notcb; - ret = chtls_set_tcb_field(sk, 0, - TCB_ULP_RAW_V(TCB_ULP_RAW_M), - TCB_ULP_RAW_V((TF_TLS_KEY_SIZE_V(1) | - TF_TLS_CONTROL_V(1) | - TF_TLS_ACTIVE_V(1) | - TF_TLS_ENABLE_V(1)))); - if (ret) - goto out_notcb; - ret = chtls_set_tcb_seqno(sk); - if (ret) - goto out_notcb; - ret = chtls_set_tcb_quiesce(sk, 0); - if (ret) - goto out_notcb; - csk->tlshws.rxkey = keyid; - } else { - csk->tlshws.tx_seq_no = 0; - csk->tlshws.txkey = keyid; - } - - release_sock(sk); - return ret; -out_notcb: - release_sock(sk); - free_tls_keyid(sk); -out_nokey: - kfree_skb(skb); - return ret; -} diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c deleted file mode 100644 index c8e99409a52a..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c +++ /dev/null @@ -1,1836 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2018 Chelsio Communications, Inc. - * - * Written by: Atul Gupta (atul.gupta@chelsio.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "chtls.h" -#include "chtls_cm.h" - -static bool is_tls_tx(struct chtls_sock *csk) -{ - return csk->tlshws.txkey >= 0; -} - -static bool is_tls_rx(struct chtls_sock *csk) -{ - return csk->tlshws.rxkey >= 0; -} - -static int data_sgl_len(const struct sk_buff *skb) -{ - unsigned int cnt; - - cnt = skb_shinfo(skb)->nr_frags; - return sgl_len(cnt) * 8; -} - -static int nos_ivs(struct sock *sk, unsigned int size) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - return DIV_ROUND_UP(size, csk->tlshws.mfs); -} - -static int set_ivs_imm(struct sock *sk, const struct sk_buff *skb) -{ - int ivs_size = nos_ivs(sk, skb->len) * CIPHER_BLOCK_SIZE; - int hlen = TLS_WR_CPL_LEN + data_sgl_len(skb); - - if ((hlen + KEY_ON_MEM_SZ + ivs_size) < - MAX_IMM_OFLD_TX_DATA_WR_LEN) { - ULP_SKB_CB(skb)->ulp.tls.iv = 1; - return 1; - } - ULP_SKB_CB(skb)->ulp.tls.iv = 0; - return 0; -} - -static int max_ivs_size(struct sock *sk, int size) -{ - return nos_ivs(sk, size) * CIPHER_BLOCK_SIZE; -} - -static int ivs_size(struct sock *sk, const struct sk_buff *skb) -{ - return set_ivs_imm(sk, skb) ? (nos_ivs(sk, skb->len) * - CIPHER_BLOCK_SIZE) : 0; -} - -static int flowc_wr_credits(int nparams, int *flowclenp) -{ - int flowclen16, flowclen; - - flowclen = offsetof(struct fw_flowc_wr, mnemval[nparams]); - flowclen16 = DIV_ROUND_UP(flowclen, 16); - flowclen = flowclen16 * 16; - - if (flowclenp) - *flowclenp = flowclen; - - return flowclen16; -} - -static struct sk_buff *create_flowc_wr_skb(struct sock *sk, - struct fw_flowc_wr *flowc, - int flowclen) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct sk_buff *skb; - - skb = alloc_skb(flowclen, GFP_ATOMIC); - if (!skb) - return NULL; - - __skb_put_data(skb, flowc, flowclen); - skb_set_queue_mapping(skb, (csk->txq_idx << 1) | CPL_PRIORITY_DATA); - - return skb; -} - -static int send_flowc_wr(struct sock *sk, struct fw_flowc_wr *flowc, - int flowclen) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - int flowclen16; - int ret; - - flowclen16 = flowclen / 16; - - if (csk_flag(sk, CSK_TX_DATA_SENT)) { - skb = create_flowc_wr_skb(sk, flowc, flowclen); - if (!skb) - return -ENOMEM; - - skb_entail(sk, skb, - ULPCB_FLAG_NO_HDR | ULPCB_FLAG_NO_APPEND); - return 0; - } - - ret = cxgb4_immdata_send(csk->egress_dev, - csk->txq_idx, - flowc, flowclen); - if (!ret) - return flowclen16; - skb = create_flowc_wr_skb(sk, flowc, flowclen); - if (!skb) - return -ENOMEM; - send_or_defer(sk, tp, skb, 0); - return flowclen16; -} - -static u8 tcp_state_to_flowc_state(u8 state) -{ - switch (state) { - case TCP_ESTABLISHED: - return FW_FLOWC_MNEM_TCPSTATE_ESTABLISHED; - case TCP_CLOSE_WAIT: - return FW_FLOWC_MNEM_TCPSTATE_CLOSEWAIT; - case TCP_FIN_WAIT1: - return FW_FLOWC_MNEM_TCPSTATE_FINWAIT1; - case TCP_CLOSING: - return FW_FLOWC_MNEM_TCPSTATE_CLOSING; - case TCP_LAST_ACK: - return FW_FLOWC_MNEM_TCPSTATE_LASTACK; - case TCP_FIN_WAIT2: - return FW_FLOWC_MNEM_TCPSTATE_FINWAIT2; - } - - return FW_FLOWC_MNEM_TCPSTATE_ESTABLISHED; -} - -int send_tx_flowc_wr(struct sock *sk, int compl, - u32 snd_nxt, u32 rcv_nxt) -{ - DEFINE_RAW_FLEX(struct fw_flowc_wr, flowc, mnemval, FW_FLOWC_MNEM_MAX); - int nparams, paramidx, flowclen16, flowclen; - struct chtls_sock *csk; - struct tcp_sock *tp; - - csk = rcu_dereference_sk_user_data(sk); - tp = tcp_sk(sk); - -#define FLOWC_PARAM(__m, __v) \ - do { \ - flowc->mnemval[paramidx].mnemonic = FW_FLOWC_MNEM_##__m; \ - flowc->mnemval[paramidx].val = cpu_to_be32(__v); \ - paramidx++; \ - } while (0) - - paramidx = 0; - - FLOWC_PARAM(PFNVFN, FW_PFVF_CMD_PFN_V(csk->cdev->lldi->pf)); - FLOWC_PARAM(CH, csk->tx_chan); - FLOWC_PARAM(PORT, csk->tx_chan); - FLOWC_PARAM(IQID, csk->rss_qid); - FLOWC_PARAM(SNDNXT, tp->snd_nxt); - FLOWC_PARAM(RCVNXT, tp->rcv_nxt); - FLOWC_PARAM(SNDBUF, csk->sndbuf); - FLOWC_PARAM(MSS, tp->mss_cache); - FLOWC_PARAM(TCPSTATE, tcp_state_to_flowc_state(sk->sk_state)); - - if (SND_WSCALE(tp)) - FLOWC_PARAM(RCV_SCALE, SND_WSCALE(tp)); - - if (csk->ulp_mode == ULP_MODE_TLS) - FLOWC_PARAM(ULD_MODE, ULP_MODE_TLS); - - if (csk->tlshws.fcplenmax) - FLOWC_PARAM(TXDATAPLEN_MAX, csk->tlshws.fcplenmax); - - nparams = paramidx; -#undef FLOWC_PARAM - - flowclen16 = flowc_wr_credits(nparams, &flowclen); - flowc->op_to_nparams = - cpu_to_be32(FW_WR_OP_V(FW_FLOWC_WR) | - FW_WR_COMPL_V(compl) | - FW_FLOWC_WR_NPARAMS_V(nparams)); - flowc->flowid_len16 = cpu_to_be32(FW_WR_LEN16_V(flowclen16) | - FW_WR_FLOWID_V(csk->tid)); - - return send_flowc_wr(sk, flowc, flowclen); -} - -/* Copy IVs to WR */ -static int tls_copy_ivs(struct sock *sk, struct sk_buff *skb) - -{ - struct chtls_sock *csk; - unsigned char *iv_loc; - struct chtls_hws *hws; - unsigned char *ivs; - u16 number_of_ivs; - struct page *page; - int err = 0; - - csk = rcu_dereference_sk_user_data(sk); - hws = &csk->tlshws; - number_of_ivs = nos_ivs(sk, skb->len); - - if (number_of_ivs > MAX_IVS_PAGE) { - pr_warn("MAX IVs in PAGE exceeded %d\n", number_of_ivs); - return -ENOMEM; - } - - /* generate the IVs */ - ivs = kmalloc_array(CIPHER_BLOCK_SIZE, number_of_ivs, GFP_ATOMIC); - if (!ivs) - return -ENOMEM; - get_random_bytes(ivs, number_of_ivs * CIPHER_BLOCK_SIZE); - - if (skb_ulp_tls_iv_imm(skb)) { - /* send the IVs as immediate data in the WR */ - iv_loc = (unsigned char *)__skb_push(skb, number_of_ivs * - CIPHER_BLOCK_SIZE); - if (iv_loc) - memcpy(iv_loc, ivs, number_of_ivs * CIPHER_BLOCK_SIZE); - - hws->ivsize = number_of_ivs * CIPHER_BLOCK_SIZE; - } else { - /* Send the IVs as sgls */ - /* Already accounted IV DSGL for credits */ - skb_shinfo(skb)->nr_frags--; - page = alloc_pages(sk->sk_allocation | __GFP_COMP, 0); - if (!page) { - pr_info("%s : Page allocation for IVs failed\n", - __func__); - err = -ENOMEM; - goto out; - } - memcpy(page_address(page), ivs, number_of_ivs * - CIPHER_BLOCK_SIZE); - skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, - number_of_ivs * CIPHER_BLOCK_SIZE); - hws->ivsize = 0; - } -out: - kfree(ivs); - return err; -} - -/* Copy Key to WR */ -static void tls_copy_tx_key(struct sock *sk, struct sk_buff *skb) -{ - struct ulptx_sc_memrd *sc_memrd; - struct chtls_sock *csk; - struct chtls_dev *cdev; - struct ulptx_idata *sc; - struct chtls_hws *hws; - u32 immdlen; - int kaddr; - - csk = rcu_dereference_sk_user_data(sk); - hws = &csk->tlshws; - cdev = csk->cdev; - - immdlen = sizeof(*sc) + sizeof(*sc_memrd); - kaddr = keyid_to_addr(cdev->kmap.start, hws->txkey); - sc = (struct ulptx_idata *)__skb_push(skb, immdlen); - if (sc) { - sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP)); - sc->len = htonl(0); - sc_memrd = (struct ulptx_sc_memrd *)(sc + 1); - sc_memrd->cmd_to_len = - htonl(ULPTX_CMD_V(ULP_TX_SC_MEMRD) | - ULP_TX_SC_MORE_V(1) | - ULPTX_LEN16_V(hws->keylen >> 4)); - sc_memrd->addr = htonl(kaddr); - } -} - -static u64 tlstx_incr_seqnum(struct chtls_hws *hws) -{ - return hws->tx_seq_no++; -} - -static bool is_sg_request(const struct sk_buff *skb) -{ - return skb->peeked || - (skb->len > MAX_IMM_ULPTX_WR_LEN); -} - -/* - * Returns true if an sk_buff carries urgent data. - */ -static bool skb_urgent(struct sk_buff *skb) -{ - return ULP_SKB_CB(skb)->flags & ULPCB_FLAG_URG; -} - -/* TLS content type for CPL SFO */ -static unsigned char tls_content_type(unsigned char content_type) -{ - switch (content_type) { - case TLS_HDR_TYPE_CCS: - return CPL_TX_TLS_SFO_TYPE_CCS; - case TLS_HDR_TYPE_ALERT: - return CPL_TX_TLS_SFO_TYPE_ALERT; - case TLS_HDR_TYPE_HANDSHAKE: - return CPL_TX_TLS_SFO_TYPE_HANDSHAKE; - case TLS_HDR_TYPE_HEARTBEAT: - return CPL_TX_TLS_SFO_TYPE_HEARTBEAT; - } - return CPL_TX_TLS_SFO_TYPE_DATA; -} - -static void tls_tx_data_wr(struct sock *sk, struct sk_buff *skb, - int dlen, int tls_immd, u32 credits, - int expn, int pdus) -{ - struct fw_tlstx_data_wr *req_wr; - struct cpl_tx_tls_sfo *req_cpl; - unsigned int wr_ulp_mode_force; - struct tls_scmd *updated_scmd; - unsigned char data_type; - struct chtls_sock *csk; - struct net_device *dev; - struct chtls_hws *hws; - struct tls_scmd *scmd; - struct adapter *adap; - unsigned char *req; - int immd_len; - int iv_imm; - int len; - - csk = rcu_dereference_sk_user_data(sk); - iv_imm = skb_ulp_tls_iv_imm(skb); - dev = csk->egress_dev; - adap = netdev2adap(dev); - hws = &csk->tlshws; - scmd = &hws->scmd; - len = dlen + expn; - - dlen = (dlen < hws->mfs) ? dlen : hws->mfs; - atomic_inc(&adap->chcr_stats.tls_pdu_tx); - - updated_scmd = scmd; - updated_scmd->seqno_numivs &= 0xffffff80; - updated_scmd->seqno_numivs |= SCMD_NUM_IVS_V(pdus); - hws->scmd = *updated_scmd; - - req = (unsigned char *)__skb_push(skb, sizeof(struct cpl_tx_tls_sfo)); - req_cpl = (struct cpl_tx_tls_sfo *)req; - req = (unsigned char *)__skb_push(skb, (sizeof(struct - fw_tlstx_data_wr))); - - req_wr = (struct fw_tlstx_data_wr *)req; - immd_len = (tls_immd ? dlen : 0); - req_wr->op_to_immdlen = - htonl(FW_WR_OP_V(FW_TLSTX_DATA_WR) | - FW_TLSTX_DATA_WR_COMPL_V(1) | - FW_TLSTX_DATA_WR_IMMDLEN_V(immd_len)); - req_wr->flowid_len16 = htonl(FW_TLSTX_DATA_WR_FLOWID_V(csk->tid) | - FW_TLSTX_DATA_WR_LEN16_V(credits)); - wr_ulp_mode_force = TX_ULP_MODE_V(ULP_MODE_TLS); - - if (is_sg_request(skb)) - wr_ulp_mode_force |= FW_OFLD_TX_DATA_WR_ALIGNPLD_F | - ((tcp_sk(sk)->nonagle & TCP_NAGLE_OFF) ? 0 : - FW_OFLD_TX_DATA_WR_SHOVE_F); - - req_wr->lsodisable_to_flags = - htonl(TX_ULP_MODE_V(ULP_MODE_TLS) | - TX_URG_V(skb_urgent(skb)) | - T6_TX_FORCE_F | wr_ulp_mode_force | - TX_SHOVE_V((!csk_flag(sk, CSK_TX_MORE_DATA)) && - skb_queue_empty(&csk->txq))); - - req_wr->ctxloc_to_exp = - htonl(FW_TLSTX_DATA_WR_NUMIVS_V(pdus) | - FW_TLSTX_DATA_WR_EXP_V(expn) | - FW_TLSTX_DATA_WR_CTXLOC_V(CHTLS_KEY_CONTEXT_DDR) | - FW_TLSTX_DATA_WR_IVDSGL_V(!iv_imm) | - FW_TLSTX_DATA_WR_KEYSIZE_V(hws->keylen >> 4)); - - /* Fill in the length */ - req_wr->plen = htonl(len); - req_wr->mfs = htons(hws->mfs); - req_wr->adjustedplen_pkd = - htons(FW_TLSTX_DATA_WR_ADJUSTEDPLEN_V(hws->adjustlen)); - req_wr->expinplenmax_pkd = - htons(FW_TLSTX_DATA_WR_EXPINPLENMAX_V(hws->expansion)); - req_wr->pdusinplenmax_pkd = - FW_TLSTX_DATA_WR_PDUSINPLENMAX_V(hws->pdus); - req_wr->r10 = 0; - - data_type = tls_content_type(ULP_SKB_CB(skb)->ulp.tls.type); - req_cpl->op_to_seg_len = htonl(CPL_TX_TLS_SFO_OPCODE_V(CPL_TX_TLS_SFO) | - CPL_TX_TLS_SFO_DATA_TYPE_V(data_type) | - CPL_TX_TLS_SFO_CPL_LEN_V(2) | - CPL_TX_TLS_SFO_SEG_LEN_V(dlen)); - req_cpl->pld_len = htonl(len - expn); - - req_cpl->type_protover = htonl(CPL_TX_TLS_SFO_TYPE_V - ((data_type == CPL_TX_TLS_SFO_TYPE_HEARTBEAT) ? - TLS_HDR_TYPE_HEARTBEAT : 0) | - CPL_TX_TLS_SFO_PROTOVER_V(0)); - - /* create the s-command */ - req_cpl->r1_lo = 0; - req_cpl->seqno_numivs = cpu_to_be32(hws->scmd.seqno_numivs); - req_cpl->ivgen_hdrlen = cpu_to_be32(hws->scmd.ivgen_hdrlen); - req_cpl->scmd1 = cpu_to_be64(tlstx_incr_seqnum(hws)); -} - -/* - * Calculate the TLS data expansion size - */ -static int chtls_expansion_size(struct sock *sk, int data_len, - int fullpdu, - unsigned short *pducnt) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct chtls_hws *hws = &csk->tlshws; - struct tls_scmd *scmd = &hws->scmd; - int fragsize = hws->mfs; - int expnsize = 0; - int fragleft; - int fragcnt; - int expppdu; - - if (SCMD_CIPH_MODE_G(scmd->seqno_numivs) == - SCMD_CIPH_MODE_AES_GCM) { - expppdu = GCM_TAG_SIZE + AEAD_EXPLICIT_DATA_SIZE + - TLS_HEADER_LENGTH; - - if (fullpdu) { - *pducnt = data_len / (expppdu + fragsize); - if (*pducnt > 32) - *pducnt = 32; - else if (!*pducnt) - *pducnt = 1; - expnsize = (*pducnt) * expppdu; - return expnsize; - } - fragcnt = (data_len / fragsize); - expnsize = fragcnt * expppdu; - fragleft = data_len % fragsize; - if (fragleft > 0) - expnsize += expppdu; - } - return expnsize; -} - -/* WR with IV, KEY and CPL SFO added */ -static void make_tlstx_data_wr(struct sock *sk, struct sk_buff *skb, - int tls_tx_imm, int tls_len, u32 credits) -{ - unsigned short pdus_per_ulp = 0; - struct chtls_sock *csk; - struct chtls_hws *hws; - int expn_sz; - int pdus; - - csk = rcu_dereference_sk_user_data(sk); - hws = &csk->tlshws; - pdus = DIV_ROUND_UP(tls_len, hws->mfs); - expn_sz = chtls_expansion_size(sk, tls_len, 0, NULL); - if (!hws->compute) { - hws->expansion = chtls_expansion_size(sk, - hws->fcplenmax, - 1, &pdus_per_ulp); - hws->pdus = pdus_per_ulp; - hws->adjustlen = hws->pdus * - ((hws->expansion / hws->pdus) + hws->mfs); - hws->compute = 1; - } - if (tls_copy_ivs(sk, skb)) - return; - tls_copy_tx_key(sk, skb); - tls_tx_data_wr(sk, skb, tls_len, tls_tx_imm, credits, expn_sz, pdus); - hws->tx_seq_no += (pdus - 1); -} - -static void make_tx_data_wr(struct sock *sk, struct sk_buff *skb, - unsigned int immdlen, int len, - u32 credits, u32 compl) -{ - struct fw_ofld_tx_data_wr *req; - unsigned int wr_ulp_mode_force; - struct chtls_sock *csk; - unsigned int opcode; - - csk = rcu_dereference_sk_user_data(sk); - opcode = FW_OFLD_TX_DATA_WR; - - req = (struct fw_ofld_tx_data_wr *)__skb_push(skb, sizeof(*req)); - req->op_to_immdlen = htonl(WR_OP_V(opcode) | - FW_WR_COMPL_V(compl) | - FW_WR_IMMDLEN_V(immdlen)); - req->flowid_len16 = htonl(FW_WR_FLOWID_V(csk->tid) | - FW_WR_LEN16_V(credits)); - - wr_ulp_mode_force = TX_ULP_MODE_V(csk->ulp_mode); - if (is_sg_request(skb)) - wr_ulp_mode_force |= FW_OFLD_TX_DATA_WR_ALIGNPLD_F | - ((tcp_sk(sk)->nonagle & TCP_NAGLE_OFF) ? 0 : - FW_OFLD_TX_DATA_WR_SHOVE_F); - - req->tunnel_to_proxy = htonl(wr_ulp_mode_force | - TX_URG_V(skb_urgent(skb)) | - TX_SHOVE_V((!csk_flag(sk, CSK_TX_MORE_DATA)) && - skb_queue_empty(&csk->txq))); - req->plen = htonl(len); -} - -static int chtls_wr_size(struct chtls_sock *csk, const struct sk_buff *skb, - bool size) -{ - int wr_size; - - wr_size = TLS_WR_CPL_LEN; - wr_size += KEY_ON_MEM_SZ; - wr_size += ivs_size(csk->sk, skb); - - if (size) - return wr_size; - - /* frags counted for IV dsgl */ - if (!skb_ulp_tls_iv_imm(skb)) - skb_shinfo(skb)->nr_frags++; - - return wr_size; -} - -static bool is_ofld_imm(struct chtls_sock *csk, const struct sk_buff *skb) -{ - int length = skb->len; - - if (skb->peeked || skb->len > MAX_IMM_ULPTX_WR_LEN) - return false; - - if (likely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NEED_HDR)) { - /* Check TLS header len for Immediate */ - if (csk->ulp_mode == ULP_MODE_TLS && - skb_ulp_tls_inline(skb)) - length += chtls_wr_size(csk, skb, true); - else - length += sizeof(struct fw_ofld_tx_data_wr); - - return length <= MAX_IMM_OFLD_TX_DATA_WR_LEN; - } - return true; -} - -static unsigned int calc_tx_flits(const struct sk_buff *skb, - unsigned int immdlen) -{ - unsigned int flits, cnt; - - flits = immdlen / 8; /* headers */ - cnt = skb_shinfo(skb)->nr_frags; - if (skb_tail_pointer(skb) != skb_transport_header(skb)) - cnt++; - return flits + sgl_len(cnt); -} - -static void arp_failure_discard(void *handle, struct sk_buff *skb) -{ - kfree_skb(skb); -} - -int chtls_push_frames(struct chtls_sock *csk, int comp) -{ - struct chtls_hws *hws = &csk->tlshws; - struct tcp_sock *tp; - struct sk_buff *skb; - int total_size = 0; - struct sock *sk; - int wr_size; - - wr_size = sizeof(struct fw_ofld_tx_data_wr); - sk = csk->sk; - tp = tcp_sk(sk); - - if (unlikely(sk_in_state(sk, TCPF_SYN_SENT | TCPF_CLOSE))) - return 0; - - if (unlikely(csk_flag(sk, CSK_ABORT_SHUTDOWN))) - return 0; - - while (csk->wr_credits && (skb = skb_peek(&csk->txq)) && - (!(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_HOLD) || - skb_queue_len(&csk->txq) > 1)) { - unsigned int credit_len = skb->len; - unsigned int credits_needed; - unsigned int completion = 0; - int tls_len = skb->len;/* TLS data len before IV/key */ - unsigned int immdlen; - int len = skb->len; /* length [ulp bytes] inserted by hw */ - int flowclen16 = 0; - int tls_tx_imm = 0; - - immdlen = skb->len; - if (!is_ofld_imm(csk, skb)) { - immdlen = skb_transport_offset(skb); - if (skb_ulp_tls_inline(skb)) - wr_size = chtls_wr_size(csk, skb, false); - credit_len = 8 * calc_tx_flits(skb, immdlen); - } else { - if (skb_ulp_tls_inline(skb)) { - wr_size = chtls_wr_size(csk, skb, false); - tls_tx_imm = 1; - } - } - if (likely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NEED_HDR)) - credit_len += wr_size; - credits_needed = DIV_ROUND_UP(credit_len, 16); - if (!csk_flag_nochk(csk, CSK_TX_DATA_SENT)) { - flowclen16 = send_tx_flowc_wr(sk, 1, tp->snd_nxt, - tp->rcv_nxt); - if (flowclen16 <= 0) - break; - csk->wr_credits -= flowclen16; - csk->wr_unacked += flowclen16; - csk->wr_nondata += flowclen16; - csk_set_flag(csk, CSK_TX_DATA_SENT); - } - - if (csk->wr_credits < credits_needed) { - if (skb_ulp_tls_inline(skb) && - !skb_ulp_tls_iv_imm(skb)) - skb_shinfo(skb)->nr_frags--; - break; - } - - __skb_unlink(skb, &csk->txq); - skb_set_queue_mapping(skb, (csk->txq_idx << 1) | - CPL_PRIORITY_DATA); - if (hws->ofld) - hws->txqid = (skb->queue_mapping >> 1); - skb->csum = (__force __wsum)(credits_needed + csk->wr_nondata); - csk->wr_credits -= credits_needed; - csk->wr_unacked += credits_needed; - csk->wr_nondata = 0; - enqueue_wr(csk, skb); - - if (likely(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NEED_HDR)) { - if ((comp && csk->wr_unacked == credits_needed) || - (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_COMPL) || - csk->wr_unacked >= csk->wr_max_credits / 2) { - completion = 1; - csk->wr_unacked = 0; - } - if (skb_ulp_tls_inline(skb)) - make_tlstx_data_wr(sk, skb, tls_tx_imm, - tls_len, credits_needed); - else - make_tx_data_wr(sk, skb, immdlen, len, - credits_needed, completion); - tp->snd_nxt += len; - tp->lsndtime = tcp_jiffies32; - if (completion) - ULP_SKB_CB(skb)->flags &= ~ULPCB_FLAG_NEED_HDR; - } else { - struct cpl_close_con_req *req = cplhdr(skb); - unsigned int cmd = CPL_OPCODE_G(ntohl - (OPCODE_TID(req))); - - if (cmd == CPL_CLOSE_CON_REQ) - csk_set_flag(csk, - CSK_CLOSE_CON_REQUESTED); - - if ((ULP_SKB_CB(skb)->flags & ULPCB_FLAG_COMPL) && - (csk->wr_unacked >= csk->wr_max_credits / 2)) { - req->wr.wr_hi |= htonl(FW_WR_COMPL_F); - csk->wr_unacked = 0; - } - } - total_size += skb->truesize; - if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_BARRIER) - csk_set_flag(csk, CSK_TX_WAIT_IDLE); - t4_set_arp_err_handler(skb, NULL, arp_failure_discard); - cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry); - } - sk->sk_wmem_queued -= total_size; - return total_size; -} - -static void mark_urg(struct tcp_sock *tp, int flags, - struct sk_buff *skb) -{ - if (unlikely(flags & MSG_OOB)) { - tp->snd_up = tp->write_seq; - ULP_SKB_CB(skb)->flags = ULPCB_FLAG_URG | - ULPCB_FLAG_BARRIER | - ULPCB_FLAG_NO_APPEND | - ULPCB_FLAG_NEED_HDR; - } -} - -/* - * Returns true if a connection should send more data to TCP engine - */ -static bool should_push(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct chtls_dev *cdev = csk->cdev; - struct tcp_sock *tp = tcp_sk(sk); - - /* - * If we've released our offload resources there's nothing to do ... - */ - if (!cdev) - return false; - - /* - * If there aren't any work requests in flight, or there isn't enough - * data in flight, or Nagle is off then send the current TX_DATA - * otherwise hold it and wait to accumulate more data. - */ - return csk->wr_credits == csk->wr_max_credits || - (tp->nonagle & TCP_NAGLE_OFF); -} - -/* - * Returns true if a TCP socket is corked. - */ -static bool corked(const struct tcp_sock *tp, int flags) -{ - return (flags & MSG_MORE) || (tp->nonagle & TCP_NAGLE_CORK); -} - -/* - * Returns true if a send should try to push new data. - */ -static bool send_should_push(struct sock *sk, int flags) -{ - return should_push(sk) && !corked(tcp_sk(sk), flags); -} - -void chtls_tcp_push(struct sock *sk, int flags) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - int qlen = skb_queue_len(&csk->txq); - - if (likely(qlen)) { - struct sk_buff *skb = skb_peek_tail(&csk->txq); - struct tcp_sock *tp = tcp_sk(sk); - - mark_urg(tp, flags, skb); - - if (!(ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) && - corked(tp, flags)) { - ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_HOLD; - return; - } - - ULP_SKB_CB(skb)->flags &= ~ULPCB_FLAG_HOLD; - if (qlen == 1 && - ((ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) || - should_push(sk))) - chtls_push_frames(csk, 1); - } -} - -/* - * Calculate the size for a new send sk_buff. It's maximum size so we can - * pack lots of data into it, unless we plan to send it immediately, in which - * case we size it more tightly. - * - * Note: we don't bother compensating for MSS < PAGE_SIZE because it doesn't - * arise in normal cases and when it does we are just wasting memory. - */ -static int select_size(struct sock *sk, int io_len, int flags, int len) -{ - const int pgbreak = SKB_MAX_HEAD(len); - - /* - * If the data wouldn't fit in the main body anyway, put only the - * header in the main body so it can use immediate data and place all - * the payload in page fragments. - */ - if (io_len > pgbreak) - return 0; - - /* - * If we will be accumulating payload get a large main body. - */ - if (!send_should_push(sk, flags)) - return pgbreak; - - return io_len; -} - -void skb_entail(struct sock *sk, struct sk_buff *skb, int flags) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct tcp_sock *tp = tcp_sk(sk); - - ULP_SKB_CB(skb)->seq = tp->write_seq; - ULP_SKB_CB(skb)->flags = flags; - __skb_queue_tail(&csk->txq, skb); - sk->sk_wmem_queued += skb->truesize; - - if (TCP_PAGE(sk) && TCP_OFF(sk)) { - put_page(TCP_PAGE(sk)); - TCP_PAGE(sk) = NULL; - TCP_OFF(sk) = 0; - } -} - -static struct sk_buff *get_tx_skb(struct sock *sk, int size) -{ - struct sk_buff *skb; - - skb = alloc_skb(size + TX_HEADER_LEN, sk->sk_allocation); - if (likely(skb)) { - skb_reserve(skb, TX_HEADER_LEN); - skb_entail(sk, skb, ULPCB_FLAG_NEED_HDR); - skb_reset_transport_header(skb); - } - return skb; -} - -static struct sk_buff *get_record_skb(struct sock *sk, int size, bool zcopy) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct sk_buff *skb; - - skb = alloc_skb(((zcopy ? 0 : size) + TX_TLSHDR_LEN + - KEY_ON_MEM_SZ + max_ivs_size(sk, size)), - sk->sk_allocation); - if (likely(skb)) { - skb_reserve(skb, (TX_TLSHDR_LEN + - KEY_ON_MEM_SZ + max_ivs_size(sk, size))); - skb_entail(sk, skb, ULPCB_FLAG_NEED_HDR); - skb_reset_transport_header(skb); - ULP_SKB_CB(skb)->ulp.tls.ofld = 1; - ULP_SKB_CB(skb)->ulp.tls.type = csk->tlshws.type; - } - return skb; -} - -static void tx_skb_finalize(struct sk_buff *skb) -{ - struct ulp_skb_cb *cb = ULP_SKB_CB(skb); - - if (!(cb->flags & ULPCB_FLAG_NO_HDR)) - cb->flags = ULPCB_FLAG_NEED_HDR; - cb->flags |= ULPCB_FLAG_NO_APPEND; -} - -static void push_frames_if_head(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - - if (skb_queue_len(&csk->txq) == 1) - chtls_push_frames(csk, 1); -} - -static int chtls_skb_copy_to_page_nocache(struct sock *sk, - struct iov_iter *from, - struct sk_buff *skb, - struct page *page, - int off, int copy) -{ - int err; - - err = skb_do_copy_data_nocache(sk, skb, from, page_address(page) + - off, copy, skb->len); - if (err) - return err; - - skb->len += copy; - skb->data_len += copy; - skb->truesize += copy; - sk->sk_wmem_queued += copy; - return 0; -} - -static bool csk_mem_free(struct chtls_dev *cdev, struct sock *sk) -{ - return (cdev->max_host_sndbuf - sk->sk_wmem_queued > 0); -} - -static int csk_wait_memory(struct chtls_dev *cdev, - struct sock *sk, long *timeo_p) -{ - DEFINE_WAIT_FUNC(wait, woken_wake_function); - int ret, err = 0; - long current_timeo; - long vm_wait = 0; - bool noblock; - - current_timeo = *timeo_p; - noblock = (*timeo_p ? false : true); - if (csk_mem_free(cdev, sk)) { - current_timeo = get_random_u32_below(HZ / 5) + 2; - vm_wait = get_random_u32_below(HZ / 5) + 2; - } - - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); - - if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) - goto do_error; - if (!*timeo_p) { - if (noblock) - set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); - goto do_nonblock; - } - if (signal_pending(current)) - goto do_interrupted; - sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk); - if (csk_mem_free(cdev, sk) && !vm_wait) - break; - - set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); - sk->sk_write_pending++; - ret = sk_wait_event(sk, ¤t_timeo, sk->sk_err || - (sk->sk_shutdown & SEND_SHUTDOWN) || - (csk_mem_free(cdev, sk) && !vm_wait), - &wait); - sk->sk_write_pending--; - if (ret < 0) - goto do_error; - - if (vm_wait) { - vm_wait -= current_timeo; - current_timeo = *timeo_p; - if (current_timeo != MAX_SCHEDULE_TIMEOUT) { - current_timeo -= vm_wait; - if (current_timeo < 0) - current_timeo = 0; - } - vm_wait = 0; - } - *timeo_p = current_timeo; - } -do_rm_wq: - remove_wait_queue(sk_sleep(sk), &wait); - return err; -do_error: - err = -EPIPE; - goto do_rm_wq; -do_nonblock: - err = -EAGAIN; - goto do_rm_wq; -do_interrupted: - err = sock_intr_errno(*timeo_p); - goto do_rm_wq; -} - -static int chtls_proccess_cmsg(struct sock *sk, struct msghdr *msg, - unsigned char *record_type) -{ - struct cmsghdr *cmsg; - int rc = -EINVAL; - - for_each_cmsghdr(cmsg, msg) { - if (!CMSG_OK(msg, cmsg)) - return -EINVAL; - if (cmsg->cmsg_level != SOL_TLS) - continue; - - switch (cmsg->cmsg_type) { - case TLS_SET_RECORD_TYPE: - if (cmsg->cmsg_len < CMSG_LEN(sizeof(*record_type))) - return -EINVAL; - - if (msg->msg_flags & MSG_MORE) - return -EINVAL; - - *record_type = *(unsigned char *)CMSG_DATA(cmsg); - rc = 0; - break; - default: - return -EINVAL; - } - } - - return rc; -} - -int chtls_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct chtls_dev *cdev = csk->cdev; - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - int mss, flags, err; - int recordsz = 0; - int copied = 0; - long timeo; - - lock_sock(sk); - flags = msg->msg_flags; - timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); - - if (!sk_in_state(sk, TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) { - err = sk_stream_wait_connect(sk, &timeo); - if (err) - goto out_err; - } - - sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk); - err = -EPIPE; - if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) - goto out_err; - - mss = csk->mss; - csk_set_flag(csk, CSK_TX_MORE_DATA); - - while (msg_data_left(msg)) { - int copy = 0; - - skb = skb_peek_tail(&csk->txq); - if (skb) { - copy = mss - skb->len; - skb->ip_summed = CHECKSUM_UNNECESSARY; - } - if (!csk_mem_free(cdev, sk)) - goto wait_for_sndbuf; - - if (is_tls_tx(csk) && !csk->tlshws.txleft) { - unsigned char record_type = TLS_RECORD_TYPE_DATA; - - if (unlikely(msg->msg_controllen)) { - err = chtls_proccess_cmsg(sk, msg, - &record_type); - if (err) - goto out_err; - - /* Avoid appending tls handshake, alert to tls data */ - if (skb) - tx_skb_finalize(skb); - } - - recordsz = size; - csk->tlshws.txleft = recordsz; - csk->tlshws.type = record_type; - } - - if (!skb || (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) || - copy <= 0) { -new_buf: - if (skb) { - tx_skb_finalize(skb); - push_frames_if_head(sk); - } - - if (is_tls_tx(csk)) { - skb = get_record_skb(sk, - select_size(sk, - recordsz, - flags, - TX_TLSHDR_LEN), - false); - } else { - skb = get_tx_skb(sk, - select_size(sk, size, flags, - TX_HEADER_LEN)); - } - if (unlikely(!skb)) - goto wait_for_memory; - - skb->ip_summed = CHECKSUM_UNNECESSARY; - copy = mss; - } - if (copy > size) - copy = size; - - if (msg->msg_flags & MSG_SPLICE_PAGES) { - err = skb_splice_from_iter(skb, &msg->msg_iter, copy); - if (err < 0) { - if (err == -EMSGSIZE) - goto new_buf; - goto do_fault; - } - copy = err; - sk_wmem_queued_add(sk, copy); - } else if (skb_tailroom(skb) > 0) { - copy = min(copy, skb_tailroom(skb)); - if (is_tls_tx(csk)) - copy = min_t(int, copy, csk->tlshws.txleft); - err = skb_add_data_nocache(sk, skb, - &msg->msg_iter, copy); - if (err) - goto do_fault; - } else { - int i = skb_shinfo(skb)->nr_frags; - struct page *page = TCP_PAGE(sk); - int pg_size = PAGE_SIZE; - int off = TCP_OFF(sk); - bool merge; - - if (page) - pg_size = page_size(page); - if (off < pg_size && - skb_can_coalesce(skb, i, page, off)) { - merge = true; - goto copy; - } - merge = false; - if (i == (is_tls_tx(csk) ? (MAX_SKB_FRAGS - 1) : - MAX_SKB_FRAGS)) - goto new_buf; - - if (page && off == pg_size) { - put_page(page); - TCP_PAGE(sk) = page = NULL; - pg_size = PAGE_SIZE; - } - - if (!page) { - gfp_t gfp = sk->sk_allocation; - int order = cdev->send_page_order; - - if (order) { - page = alloc_pages(gfp | __GFP_COMP | - __GFP_NOWARN | - __GFP_NORETRY, - order); - if (page) - pg_size <<= order; - } - if (!page) { - page = alloc_page(gfp); - pg_size = PAGE_SIZE; - } - if (!page) - goto wait_for_memory; - off = 0; - } -copy: - if (copy > pg_size - off) - copy = pg_size - off; - if (is_tls_tx(csk)) - copy = min_t(int, copy, csk->tlshws.txleft); - - err = chtls_skb_copy_to_page_nocache(sk, &msg->msg_iter, - skb, page, - off, copy); - if (unlikely(err)) { - if (!TCP_PAGE(sk)) { - TCP_PAGE(sk) = page; - TCP_OFF(sk) = 0; - } - goto do_fault; - } - /* Update the skb. */ - if (merge) { - skb_frag_size_add( - &skb_shinfo(skb)->frags[i - 1], - copy); - } else { - skb_fill_page_desc(skb, i, page, off, copy); - if (off + copy < pg_size) { - /* space left keep page */ - get_page(page); - TCP_PAGE(sk) = page; - } else { - TCP_PAGE(sk) = NULL; - } - } - TCP_OFF(sk) = off + copy; - } - if (unlikely(skb->len == mss)) - tx_skb_finalize(skb); - tp->write_seq += copy; - copied += copy; - size -= copy; - - if (is_tls_tx(csk)) - csk->tlshws.txleft -= copy; - - if (corked(tp, flags) && - (sk_stream_wspace(sk) < sk_stream_min_wspace(sk))) - ULP_SKB_CB(skb)->flags |= ULPCB_FLAG_NO_APPEND; - - if (size == 0) - goto out; - - if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_NO_APPEND) - push_frames_if_head(sk); - continue; -wait_for_sndbuf: - set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); -wait_for_memory: - err = csk_wait_memory(cdev, sk, &timeo); - if (err) - goto do_error; - } -out: - csk_reset_flag(csk, CSK_TX_MORE_DATA); - if (copied) - chtls_tcp_push(sk, flags); -done: - release_sock(sk); - return copied; -do_fault: - if (!skb->len) { - __skb_unlink(skb, &csk->txq); - sk->sk_wmem_queued -= skb->truesize; - __kfree_skb(skb); - } -do_error: - if (copied) - goto out; -out_err: - if (csk_conn_inline(csk)) - csk_reset_flag(csk, CSK_TX_MORE_DATA); - copied = sk_stream_error(sk, flags, err); - goto done; -} - -void chtls_splice_eof(struct socket *sock) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - chtls_tcp_push(sk, 0); - release_sock(sk); -} - -static void chtls_select_window(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct tcp_sock *tp = tcp_sk(sk); - unsigned int wnd = tp->rcv_wnd; - - wnd = max_t(unsigned int, wnd, tcp_full_space(sk)); - wnd = max_t(unsigned int, MIN_RCV_WND, wnd); - - if (wnd > MAX_RCV_WND) - wnd = MAX_RCV_WND; - -/* - * Check if we need to grow the receive window in response to an increase in - * the socket's receive buffer size. Some applications increase the buffer - * size dynamically and rely on the window to grow accordingly. - */ - - if (wnd > tp->rcv_wnd) { - tp->rcv_wup -= wnd - tp->rcv_wnd; - tp->rcv_wnd = wnd; - /* Mark the receive window as updated */ - csk_reset_flag(csk, CSK_UPDATE_RCV_WND); - } -} - -/* - * Send RX credits through an RX_DATA_ACK CPL message. We are permitted - * to return without sending the message in case we cannot allocate - * an sk_buff. Returns the number of credits sent. - */ -static u32 send_rx_credits(struct chtls_sock *csk, u32 credits) -{ - struct cpl_rx_data_ack *req; - struct sk_buff *skb; - - skb = alloc_skb(sizeof(*req), GFP_ATOMIC); - if (!skb) - return 0; - __skb_put(skb, sizeof(*req)); - req = (struct cpl_rx_data_ack *)skb->head; - - set_wr_txq(skb, CPL_PRIORITY_ACK, csk->port_id); - INIT_TP_WR(req, csk->tid); - OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_RX_DATA_ACK, - csk->tid)); - req->credit_dack = cpu_to_be32(RX_CREDITS_V(credits) | - RX_FORCE_ACK_F); - cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb); - return credits; -} - -#define CREDIT_RETURN_STATE (TCPF_ESTABLISHED | \ - TCPF_FIN_WAIT1 | \ - TCPF_FIN_WAIT2) - -/* - * Called after some received data has been read. It returns RX credits - * to the HW for the amount of data processed. - */ -static void chtls_cleanup_rbuf(struct sock *sk, int copied) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct tcp_sock *tp; - int must_send; - u32 credits; - u32 thres; - - thres = 15 * 1024; - - if (!sk_in_state(sk, CREDIT_RETURN_STATE)) - return; - - chtls_select_window(sk); - tp = tcp_sk(sk); - credits = tp->copied_seq - tp->rcv_wup; - if (unlikely(!credits)) - return; - -/* - * For coalescing to work effectively ensure the receive window has - * at least 16KB left. - */ - must_send = credits + 16384 >= tp->rcv_wnd; - - if (must_send || credits >= thres) - tp->rcv_wup += send_rx_credits(csk, credits); -} - -static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct chtls_hws *hws = &csk->tlshws; - struct net_device *dev = csk->egress_dev; - struct adapter *adap = netdev2adap(dev); - struct tcp_sock *tp = tcp_sk(sk); - unsigned long avail; - int buffers_freed; - int copied = 0; - int target; - long timeo; - int ret; - - buffers_freed = 0; - - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); - - if (unlikely(csk_flag(sk, CSK_UPDATE_RCV_WND))) - chtls_cleanup_rbuf(sk, copied); - - do { - struct sk_buff *skb; - u32 offset = 0; - - if (unlikely(tp->urg_data && - tp->urg_seq == tp->copied_seq)) { - if (copied) - break; - if (signal_pending(current)) { - copied = timeo ? sock_intr_errno(timeo) : - -EAGAIN; - break; - } - } - skb = skb_peek(&sk->sk_receive_queue); - if (skb) - goto found_ok_skb; - if (csk->wr_credits && - skb_queue_len(&csk->txq) && - chtls_push_frames(csk, csk->wr_credits == - csk->wr_max_credits)) - sk->sk_write_space(sk); - - if (copied >= target && !READ_ONCE(sk->sk_backlog.tail)) - break; - - if (copied) { - if (sk->sk_err || sk->sk_state == TCP_CLOSE || - (sk->sk_shutdown & RCV_SHUTDOWN) || - signal_pending(current)) - break; - - if (!timeo) - break; - } else { - if (sock_flag(sk, SOCK_DONE)) - break; - if (sk->sk_err) { - copied = sock_error(sk); - break; - } - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - if (sk->sk_state == TCP_CLOSE) { - copied = -ENOTCONN; - break; - } - if (!timeo) { - copied = -EAGAIN; - break; - } - if (signal_pending(current)) { - copied = sock_intr_errno(timeo); - break; - } - } - if (READ_ONCE(sk->sk_backlog.tail)) { - release_sock(sk); - lock_sock(sk); - chtls_cleanup_rbuf(sk, copied); - continue; - } - - if (copied >= target) - break; - chtls_cleanup_rbuf(sk, copied); - ret = sk_wait_data(sk, &timeo, NULL); - if (ret < 0) { - copied = copied ? : ret; - goto unlock; - } - continue; -found_ok_skb: - if (!skb->len) { - skb_dstref_steal(skb); - __skb_unlink(skb, &sk->sk_receive_queue); - kfree_skb(skb); - - if (!copied && !timeo) { - copied = -EAGAIN; - break; - } - - if (copied < target) { - release_sock(sk); - lock_sock(sk); - continue; - } - break; - } - offset = hws->copied_seq; - avail = skb->len - offset; - if (len < avail) - avail = len; - - if (unlikely(tp->urg_data)) { - u32 urg_offset = tp->urg_seq - tp->copied_seq; - - if (urg_offset < avail) { - if (urg_offset) { - avail = urg_offset; - } else if (!sock_flag(sk, SOCK_URGINLINE)) { - /* First byte is urgent, skip */ - tp->copied_seq++; - offset++; - avail--; - if (!avail) - goto skip_copy; - } - } - } - /* Set record type if not already done. For a non-data record, - * do not proceed if record type could not be copied. - */ - if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_TLS_HDR) { - struct tls_hdr *thdr = (struct tls_hdr *)skb->data; - int cerr = 0; - - cerr = put_cmsg(msg, SOL_TLS, TLS_GET_RECORD_TYPE, - sizeof(thdr->type), &thdr->type); - - if (cerr && thdr->type != TLS_RECORD_TYPE_DATA) { - copied = -EIO; - break; - } - /* don't send tls header, skip copy */ - goto skip_copy; - } - - if (skb_copy_datagram_msg(skb, offset, msg, avail)) { - if (!copied) { - copied = -EFAULT; - break; - } - } - - copied += avail; - len -= avail; - hws->copied_seq += avail; -skip_copy: - if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) - tp->urg_data = 0; - - if ((avail + offset) >= skb->len) { - struct sk_buff *next_skb; - if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_TLS_HDR) { - tp->copied_seq += skb->len; - hws->rcvpld = skb->hdr_len; - } else { - atomic_inc(&adap->chcr_stats.tls_pdu_rx); - tp->copied_seq += hws->rcvpld; - } - chtls_free_skb(sk, skb); - buffers_freed++; - hws->copied_seq = 0; - next_skb = skb_peek(&sk->sk_receive_queue); - if (copied >= target && !next_skb) - break; - if (ULP_SKB_CB(next_skb)->flags & ULPCB_FLAG_TLS_HDR) - break; - } - } while (len > 0); - - if (buffers_freed) - chtls_cleanup_rbuf(sk, copied); - -unlock: - release_sock(sk); - return copied; -} - -/* - * Peek at data in a socket's receive buffer. - */ -static int peekmsg(struct sock *sk, struct msghdr *msg, - size_t len, int flags) -{ - struct tcp_sock *tp = tcp_sk(sk); - u32 peek_seq, offset; - struct sk_buff *skb; - int copied = 0; - size_t avail; /* amount of available data in current skb */ - long timeo; - int ret; - - lock_sock(sk); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - peek_seq = tp->copied_seq; - - do { - if (unlikely(tp->urg_data && tp->urg_seq == peek_seq)) { - if (copied) - break; - if (signal_pending(current)) { - copied = timeo ? sock_intr_errno(timeo) : - -EAGAIN; - break; - } - } - - skb_queue_walk(&sk->sk_receive_queue, skb) { - offset = peek_seq - ULP_SKB_CB(skb)->seq; - if (offset < skb->len) - goto found_ok_skb; - } - - /* empty receive queue */ - if (copied) - break; - if (sock_flag(sk, SOCK_DONE)) - break; - if (sk->sk_err) { - copied = sock_error(sk); - break; - } - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - if (sk->sk_state == TCP_CLOSE) { - copied = -ENOTCONN; - break; - } - if (!timeo) { - copied = -EAGAIN; - break; - } - if (signal_pending(current)) { - copied = sock_intr_errno(timeo); - break; - } - - if (READ_ONCE(sk->sk_backlog.tail)) { - /* Do not sleep, just process backlog. */ - release_sock(sk); - lock_sock(sk); - } else { - ret = sk_wait_data(sk, &timeo, NULL); - if (ret < 0) { - /* here 'copied' is 0 due to previous checks */ - copied = ret; - break; - } - } - - if (unlikely(peek_seq != tp->copied_seq)) { - if (net_ratelimit()) - pr_info("TCP(%s:%d), race in MSG_PEEK.\n", - current->comm, current->pid); - peek_seq = tp->copied_seq; - } - continue; - -found_ok_skb: - avail = skb->len - offset; - if (len < avail) - avail = len; - /* - * Do we have urgent data here? We need to skip over the - * urgent byte. - */ - if (unlikely(tp->urg_data)) { - u32 urg_offset = tp->urg_seq - peek_seq; - - if (urg_offset < avail) { - /* - * The amount of data we are preparing to copy - * contains urgent data. - */ - if (!urg_offset) { /* First byte is urgent */ - if (!sock_flag(sk, SOCK_URGINLINE)) { - peek_seq++; - offset++; - avail--; - } - if (!avail) - continue; - } else { - /* stop short of the urgent data */ - avail = urg_offset; - } - } - } - - /* - * If MSG_TRUNC is specified the data is discarded. - */ - if (likely(!(flags & MSG_TRUNC))) - if (skb_copy_datagram_msg(skb, offset, msg, len)) { - if (!copied) { - copied = -EFAULT; - break; - } - } - peek_seq += avail; - copied += avail; - len -= avail; - } while (len > 0); - - release_sock(sk); - return copied; -} - -int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int flags) -{ - struct tcp_sock *tp = tcp_sk(sk); - struct chtls_sock *csk; - unsigned long avail; /* amount of available data in current skb */ - int buffers_freed; - int copied = 0; - long timeo; - int target; /* Read at least this many bytes */ - int ret; - - buffers_freed = 0; - - if (unlikely(flags & MSG_OOB)) - return tcp_prot.recvmsg(sk, msg, len, flags); - - if (unlikely(flags & MSG_PEEK)) - return peekmsg(sk, msg, len, flags); - - if (sk_can_busy_loop(sk) && - skb_queue_empty_lockless(&sk->sk_receive_queue) && - sk->sk_state == TCP_ESTABLISHED) - sk_busy_loop(sk, flags & MSG_DONTWAIT); - - lock_sock(sk); - csk = rcu_dereference_sk_user_data(sk); - - if (is_tls_rx(csk)) - return chtls_pt_recvmsg(sk, msg, len, flags); - - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); - - if (unlikely(csk_flag(sk, CSK_UPDATE_RCV_WND))) - chtls_cleanup_rbuf(sk, copied); - - do { - struct sk_buff *skb; - u32 offset; - - if (unlikely(tp->urg_data && tp->urg_seq == tp->copied_seq)) { - if (copied) - break; - if (signal_pending(current)) { - copied = timeo ? sock_intr_errno(timeo) : - -EAGAIN; - break; - } - } - - skb = skb_peek(&sk->sk_receive_queue); - if (skb) - goto found_ok_skb; - - if (csk->wr_credits && - skb_queue_len(&csk->txq) && - chtls_push_frames(csk, csk->wr_credits == - csk->wr_max_credits)) - sk->sk_write_space(sk); - - if (copied >= target && !READ_ONCE(sk->sk_backlog.tail)) - break; - - if (copied) { - if (sk->sk_err || sk->sk_state == TCP_CLOSE || - (sk->sk_shutdown & RCV_SHUTDOWN) || - signal_pending(current)) - break; - } else { - if (sock_flag(sk, SOCK_DONE)) - break; - if (sk->sk_err) { - copied = sock_error(sk); - break; - } - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - if (sk->sk_state == TCP_CLOSE) { - copied = -ENOTCONN; - break; - } - if (!timeo) { - copied = -EAGAIN; - break; - } - if (signal_pending(current)) { - copied = sock_intr_errno(timeo); - break; - } - } - - if (READ_ONCE(sk->sk_backlog.tail)) { - release_sock(sk); - lock_sock(sk); - chtls_cleanup_rbuf(sk, copied); - continue; - } - - if (copied >= target) - break; - chtls_cleanup_rbuf(sk, copied); - ret = sk_wait_data(sk, &timeo, NULL); - if (ret < 0) { - copied = copied ? : ret; - goto unlock; - } - continue; - -found_ok_skb: - if (!skb->len) { - chtls_kfree_skb(sk, skb); - if (!copied && !timeo) { - copied = -EAGAIN; - break; - } - - if (copied < target) - continue; - - break; - } - - offset = tp->copied_seq - ULP_SKB_CB(skb)->seq; - avail = skb->len - offset; - if (len < avail) - avail = len; - - if (unlikely(tp->urg_data)) { - u32 urg_offset = tp->urg_seq - tp->copied_seq; - - if (urg_offset < avail) { - if (urg_offset) { - avail = urg_offset; - } else if (!sock_flag(sk, SOCK_URGINLINE)) { - tp->copied_seq++; - offset++; - avail--; - if (!avail) - goto skip_copy; - } - } - } - - if (likely(!(flags & MSG_TRUNC))) { - if (skb_copy_datagram_msg(skb, offset, - msg, avail)) { - if (!copied) { - copied = -EFAULT; - break; - } - } - } - - tp->copied_seq += avail; - copied += avail; - len -= avail; - -skip_copy: - if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) - tp->urg_data = 0; - - if (avail + offset >= skb->len) { - chtls_free_skb(sk, skb); - buffers_freed++; - - if (copied >= target && - !skb_peek(&sk->sk_receive_queue)) - break; - } - } while (len > 0); - - if (buffers_freed) - chtls_cleanup_rbuf(sk, copied); - -unlock: - release_sock(sk); - return copied; -} diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c deleted file mode 100644 index 2570575434f9..000000000000 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c +++ /dev/null @@ -1,642 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2018 Chelsio Communications, Inc. - * - * Written by: Atul Gupta (atul.gupta@chelsio.com) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "chtls.h" -#include "chtls_cm.h" - -#define DRV_NAME "chtls" - -/* - * chtls device management - * maintains a list of the chtls devices - */ -static LIST_HEAD(cdev_list); -static DEFINE_MUTEX(cdev_mutex); - -static DEFINE_MUTEX(notify_mutex); -static RAW_NOTIFIER_HEAD(listen_notify_list); -static struct proto chtls_cpl_prot, chtls_cpl_protv6; -struct request_sock_ops chtls_rsk_ops, chtls_rsk_opsv6; -static uint send_page_order = (14 - PAGE_SHIFT < 0) ? 0 : 14 - PAGE_SHIFT; - -static void register_listen_notifier(struct notifier_block *nb) -{ - mutex_lock(¬ify_mutex); - raw_notifier_chain_register(&listen_notify_list, nb); - mutex_unlock(¬ify_mutex); -} - -static void unregister_listen_notifier(struct notifier_block *nb) -{ - mutex_lock(¬ify_mutex); - raw_notifier_chain_unregister(&listen_notify_list, nb); - mutex_unlock(¬ify_mutex); -} - -static int listen_notify_handler(struct notifier_block *this, - unsigned long event, void *data) -{ - struct chtls_listen *clisten; - int ret = NOTIFY_DONE; - - clisten = (struct chtls_listen *)data; - - switch (event) { - case CHTLS_LISTEN_START: - ret = chtls_listen_start(clisten->cdev, clisten->sk); - kfree(clisten); - break; - case CHTLS_LISTEN_STOP: - chtls_listen_stop(clisten->cdev, clisten->sk); - kfree(clisten); - break; - } - return ret; -} - -static struct notifier_block listen_notifier = { - .notifier_call = listen_notify_handler -}; - -static int listen_backlog_rcv(struct sock *sk, struct sk_buff *skb) -{ - if (likely(skb_transport_header(skb) != skb_network_header(skb))) - return tcp_v4_do_rcv(sk, skb); - BLOG_SKB_CB(skb)->backlog_rcv(sk, skb); - return 0; -} - -static int chtls_start_listen(struct chtls_dev *cdev, struct sock *sk) -{ - struct chtls_listen *clisten; - - if (sk->sk_protocol != IPPROTO_TCP) - return -EPROTONOSUPPORT; - - if (sk->sk_family == PF_INET && - LOOPBACK(inet_sk(sk)->inet_rcv_saddr)) - return -EADDRNOTAVAIL; - - sk->sk_backlog_rcv = listen_backlog_rcv; - clisten = kmalloc_obj(*clisten); - if (!clisten) - return -ENOMEM; - clisten->cdev = cdev; - clisten->sk = sk; - mutex_lock(¬ify_mutex); - raw_notifier_call_chain(&listen_notify_list, - CHTLS_LISTEN_START, clisten); - mutex_unlock(¬ify_mutex); - return 0; -} - -static void chtls_stop_listen(struct chtls_dev *cdev, struct sock *sk) -{ - struct chtls_listen *clisten; - - if (sk->sk_protocol != IPPROTO_TCP) - return; - - clisten = kmalloc_obj(*clisten); - if (!clisten) - return; - clisten->cdev = cdev; - clisten->sk = sk; - mutex_lock(¬ify_mutex); - raw_notifier_call_chain(&listen_notify_list, - CHTLS_LISTEN_STOP, clisten); - mutex_unlock(¬ify_mutex); -} - -static int chtls_inline_feature(struct tls_toe_device *dev) -{ - struct net_device *netdev; - struct chtls_dev *cdev; - int i; - - cdev = to_chtls_dev(dev); - - for (i = 0; i < cdev->lldi->nports; i++) { - netdev = cdev->ports[i]; - if (netdev->features & NETIF_F_HW_TLS_RECORD) - return 1; - } - return 0; -} - -static int chtls_create_hash(struct tls_toe_device *dev, struct sock *sk) -{ - struct chtls_dev *cdev = to_chtls_dev(dev); - - if (sk->sk_state == TCP_LISTEN) - return chtls_start_listen(cdev, sk); - return 0; -} - -static void chtls_destroy_hash(struct tls_toe_device *dev, struct sock *sk) -{ - struct chtls_dev *cdev = to_chtls_dev(dev); - - if (sk->sk_state == TCP_LISTEN) - chtls_stop_listen(cdev, sk); -} - -static void chtls_free_uld(struct chtls_dev *cdev) -{ - int i; - - tls_toe_unregister_device(&cdev->tlsdev); - kvfree(cdev->kmap.addr); - idr_destroy(&cdev->hwtid_idr); - for (i = 0; i < (1 << RSPQ_HASH_BITS); i++) - kfree_skb(cdev->rspq_skb_cache[i]); - kfree(cdev->lldi); - kfree_skb(cdev->askb); - kfree(cdev); -} - -static inline void chtls_dev_release(struct kref *kref) -{ - struct tls_toe_device *dev; - struct chtls_dev *cdev; - struct adapter *adap; - - dev = container_of(kref, struct tls_toe_device, kref); - cdev = to_chtls_dev(dev); - - /* Reset tls rx/tx stats */ - adap = pci_get_drvdata(cdev->pdev); - atomic_set(&adap->chcr_stats.tls_pdu_tx, 0); - atomic_set(&adap->chcr_stats.tls_pdu_rx, 0); - - chtls_free_uld(cdev); -} - -static void chtls_register_dev(struct chtls_dev *cdev) -{ - struct tls_toe_device *tlsdev = &cdev->tlsdev; - - strscpy(tlsdev->name, "chtls", TLS_TOE_DEVICE_NAME_MAX); - strlcat(tlsdev->name, cdev->lldi->ports[0]->name, - TLS_TOE_DEVICE_NAME_MAX); - tlsdev->feature = chtls_inline_feature; - tlsdev->hash = chtls_create_hash; - tlsdev->unhash = chtls_destroy_hash; - tlsdev->release = chtls_dev_release; - kref_init(&tlsdev->kref); - tls_toe_register_device(tlsdev); - cdev->cdev_state = CHTLS_CDEV_STATE_UP; -} - -static void process_deferq(struct work_struct *task_param) -{ - struct chtls_dev *cdev = container_of(task_param, - struct chtls_dev, deferq_task); - struct sk_buff *skb; - - spin_lock_bh(&cdev->deferq.lock); - while ((skb = __skb_dequeue(&cdev->deferq)) != NULL) { - spin_unlock_bh(&cdev->deferq.lock); - DEFERRED_SKB_CB(skb)->handler(cdev, skb); - spin_lock_bh(&cdev->deferq.lock); - } - spin_unlock_bh(&cdev->deferq.lock); -} - -static int chtls_get_skb(struct chtls_dev *cdev) -{ - cdev->askb = alloc_skb(sizeof(struct tcphdr), GFP_KERNEL); - if (!cdev->askb) - return -ENOMEM; - - skb_put(cdev->askb, sizeof(struct tcphdr)); - skb_reset_transport_header(cdev->askb); - memset(cdev->askb->data, 0, cdev->askb->len); - return 0; -} - -static void *chtls_uld_add(const struct cxgb4_lld_info *info) -{ - struct cxgb4_lld_info *lldi; - struct chtls_dev *cdev; - int i, j; - - cdev = kzalloc_obj(*cdev); - if (!cdev) - goto out; - - lldi = kzalloc_obj(*lldi); - if (!lldi) - goto out_lldi; - - if (chtls_get_skb(cdev)) - goto out_skb; - - *lldi = *info; - cdev->lldi = lldi; - cdev->pdev = lldi->pdev; - cdev->tids = lldi->tids; - cdev->ports = lldi->ports; - cdev->mtus = lldi->mtus; - cdev->tids = lldi->tids; - cdev->pfvf = FW_VIID_PFN_G(cxgb4_port_viid(lldi->ports[0])) - << FW_VIID_PFN_S; - - for (i = 0; i < (1 << RSPQ_HASH_BITS); i++) { - unsigned int size = 64 - sizeof(struct rsp_ctrl) - 8; - - cdev->rspq_skb_cache[i] = __alloc_skb(size, - gfp_any(), 0, - lldi->nodeid); - if (unlikely(!cdev->rspq_skb_cache[i])) - goto out_rspq_skb; - } - - idr_init(&cdev->hwtid_idr); - INIT_WORK(&cdev->deferq_task, process_deferq); - spin_lock_init(&cdev->listen_lock); - spin_lock_init(&cdev->idr_lock); - cdev->send_page_order = min_t(uint, get_order(32768), - send_page_order); - cdev->max_host_sndbuf = 48 * 1024; - - if (lldi->vr->key.size) - if (chtls_init_kmap(cdev, lldi)) - goto out_rspq_skb; - - mutex_lock(&cdev_mutex); - list_add_tail(&cdev->list, &cdev_list); - mutex_unlock(&cdev_mutex); - - return cdev; -out_rspq_skb: - for (j = 0; j < i; j++) - kfree_skb(cdev->rspq_skb_cache[j]); - kfree_skb(cdev->askb); -out_skb: - kfree(lldi); -out_lldi: - kfree(cdev); -out: - return NULL; -} - -static void chtls_free_all_uld(void) -{ - struct chtls_dev *cdev, *tmp; - - mutex_lock(&cdev_mutex); - list_for_each_entry_safe(cdev, tmp, &cdev_list, list) { - if (cdev->cdev_state == CHTLS_CDEV_STATE_UP) { - list_del(&cdev->list); - kref_put(&cdev->tlsdev.kref, cdev->tlsdev.release); - } - } - mutex_unlock(&cdev_mutex); -} - -static int chtls_uld_state_change(void *handle, enum cxgb4_state new_state) -{ - struct chtls_dev *cdev = handle; - - switch (new_state) { - case CXGB4_STATE_UP: - chtls_register_dev(cdev); - break; - case CXGB4_STATE_DOWN: - break; - case CXGB4_STATE_START_RECOVERY: - break; - case CXGB4_STATE_DETACH: - mutex_lock(&cdev_mutex); - list_del(&cdev->list); - mutex_unlock(&cdev_mutex); - kref_put(&cdev->tlsdev.kref, cdev->tlsdev.release); - break; - default: - break; - } - return 0; -} - -static struct sk_buff *copy_gl_to_skb_pkt(const struct pkt_gl *gl, - const __be64 *rsp, - u32 pktshift) -{ - struct sk_buff *skb; - - /* Allocate space for cpl_pass_accept_req which will be synthesized by - * driver. Once driver synthesizes cpl_pass_accept_req the skb will go - * through the regular cpl_pass_accept_req processing in TOM. - */ - skb = alloc_skb(size_add(gl->tot_len, - sizeof(struct cpl_pass_accept_req)) - - pktshift, GFP_ATOMIC); - if (unlikely(!skb)) - return NULL; - __skb_put(skb, gl->tot_len + sizeof(struct cpl_pass_accept_req) - - pktshift); - /* For now we will copy cpl_rx_pkt in the skb */ - skb_copy_to_linear_data(skb, rsp, sizeof(struct cpl_rx_pkt)); - skb_copy_to_linear_data_offset(skb, sizeof(struct cpl_pass_accept_req) - , gl->va + pktshift, - gl->tot_len - pktshift); - - return skb; -} - -static int chtls_recv_packet(struct chtls_dev *cdev, - const struct pkt_gl *gl, const __be64 *rsp) -{ - unsigned int opcode = *(u8 *)rsp; - struct sk_buff *skb; - int ret; - - skb = copy_gl_to_skb_pkt(gl, rsp, cdev->lldi->sge_pktshift); - if (!skb) - return -ENOMEM; - - ret = chtls_handlers[opcode](cdev, skb); - if (ret & CPL_RET_BUF_DONE) - kfree_skb(skb); - - return 0; -} - -static int chtls_recv_rsp(struct chtls_dev *cdev, const __be64 *rsp) -{ - unsigned long rspq_bin; - unsigned int opcode; - struct sk_buff *skb; - unsigned int len; - int ret; - - len = 64 - sizeof(struct rsp_ctrl) - 8; - opcode = *(u8 *)rsp; - - rspq_bin = hash_ptr((void *)rsp, RSPQ_HASH_BITS); - skb = cdev->rspq_skb_cache[rspq_bin]; - if (skb && !skb_is_nonlinear(skb) && - !skb_shared(skb) && !skb_cloned(skb)) { - refcount_inc(&skb->users); - if (refcount_read(&skb->users) == 2) { - __skb_trim(skb, 0); - if (skb_tailroom(skb) >= len) - goto copy_out; - } - refcount_dec(&skb->users); - } - skb = alloc_skb(len, GFP_ATOMIC); - if (unlikely(!skb)) - return -ENOMEM; - -copy_out: - __skb_put(skb, len); - skb_copy_to_linear_data(skb, rsp, len); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - ret = chtls_handlers[opcode](cdev, skb); - - if (ret & CPL_RET_BUF_DONE) - kfree_skb(skb); - return 0; -} - -static void chtls_recv(struct chtls_dev *cdev, - struct sk_buff **skbs, const __be64 *rsp) -{ - struct sk_buff *skb = *skbs; - unsigned int opcode; - int ret; - - opcode = *(u8 *)rsp; - - __skb_push(skb, sizeof(struct rss_header)); - skb_copy_to_linear_data(skb, rsp, sizeof(struct rss_header)); - - ret = chtls_handlers[opcode](cdev, skb); - if (ret & CPL_RET_BUF_DONE) - kfree_skb(skb); -} - -static int chtls_uld_rx_handler(void *handle, const __be64 *rsp, - const struct pkt_gl *gl) -{ - struct chtls_dev *cdev = handle; - unsigned int opcode; - struct sk_buff *skb; - - opcode = *(u8 *)rsp; - - if (unlikely(opcode == CPL_RX_PKT)) { - if (chtls_recv_packet(cdev, gl, rsp) < 0) - goto nomem; - return 0; - } - - if (!gl) - return chtls_recv_rsp(cdev, rsp); - -#define RX_PULL_LEN 128 - skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN); - if (unlikely(!skb)) - goto nomem; - chtls_recv(cdev, &skb, rsp); - return 0; - -nomem: - return -ENOMEM; -} - -static int do_chtls_getsockopt(struct sock *sk, char __user *optval, - int __user *optlen) -{ - struct tls_crypto_info crypto_info = { 0 }; - - crypto_info.version = TLS_1_2_VERSION; - if (copy_to_user(optval, &crypto_info, sizeof(struct tls_crypto_info))) - return -EFAULT; - return 0; -} - -static int chtls_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct tls_context *ctx = tls_get_ctx(sk); - - if (level != SOL_TLS) - return ctx->sk_proto->getsockopt(sk, level, - optname, optval, optlen); - - return do_chtls_getsockopt(sk, optval, optlen); -} - -static int do_chtls_setsockopt(struct sock *sk, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct tls_crypto_info *crypto_info, tmp_crypto_info; - struct chtls_sock *csk; - int keylen; - int cipher_type; - int rc = 0; - - csk = rcu_dereference_sk_user_data(sk); - - if (sockptr_is_null(optval) || optlen < sizeof(*crypto_info)) { - rc = -EINVAL; - goto out; - } - - rc = copy_from_sockptr(&tmp_crypto_info, optval, sizeof(*crypto_info)); - if (rc) { - rc = -EFAULT; - goto out; - } - - /* check version */ - if (tmp_crypto_info.version != TLS_1_2_VERSION) { - rc = -ENOTSUPP; - goto out; - } - - crypto_info = (struct tls_crypto_info *)&csk->tlshws.crypto_info; - - /* GCM mode of AES supports 128 and 256 bit encryption, so - * copy keys from user based on GCM cipher type. - */ - switch (tmp_crypto_info.cipher_type) { - case TLS_CIPHER_AES_GCM_128: { - /* Obtain version and type from previous copy */ - crypto_info[0] = tmp_crypto_info; - /* Now copy the following data */ - rc = copy_from_sockptr_offset((char *)crypto_info + - sizeof(*crypto_info), - optval, sizeof(*crypto_info), - sizeof(struct tls12_crypto_info_aes_gcm_128) - - sizeof(*crypto_info)); - - if (rc) { - rc = -EFAULT; - goto out; - } - - keylen = TLS_CIPHER_AES_GCM_128_KEY_SIZE; - cipher_type = TLS_CIPHER_AES_GCM_128; - break; - } - case TLS_CIPHER_AES_GCM_256: { - crypto_info[0] = tmp_crypto_info; - rc = copy_from_sockptr_offset((char *)crypto_info + - sizeof(*crypto_info), - optval, sizeof(*crypto_info), - sizeof(struct tls12_crypto_info_aes_gcm_256) - - sizeof(*crypto_info)); - - if (rc) { - rc = -EFAULT; - goto out; - } - - keylen = TLS_CIPHER_AES_GCM_256_KEY_SIZE; - cipher_type = TLS_CIPHER_AES_GCM_256; - break; - } - default: - rc = -EINVAL; - goto out; - } - rc = chtls_setkey(csk, keylen, optname, cipher_type); -out: - return rc; -} - -static int chtls_setsockopt(struct sock *sk, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct tls_context *ctx = tls_get_ctx(sk); - - if (level != SOL_TLS) - return ctx->sk_proto->setsockopt(sk, level, - optname, optval, optlen); - - return do_chtls_setsockopt(sk, optname, optval, optlen); -} - -static struct cxgb4_uld_info chtls_uld_info = { - .name = DRV_NAME, - .nrxq = MAX_ULD_QSETS, - .ntxq = MAX_ULD_QSETS, - .rxq_size = 1024, - .add = chtls_uld_add, - .state_change = chtls_uld_state_change, - .rx_handler = chtls_uld_rx_handler, -}; - -void chtls_install_cpl_ops(struct sock *sk) -{ - if (sk->sk_family == AF_INET) - sk->sk_prot = &chtls_cpl_prot; - else - sk->sk_prot = &chtls_cpl_protv6; -} - -static void __init chtls_init_ulp_ops(void) -{ - chtls_cpl_prot = tcp_prot; - chtls_init_rsk_ops(&chtls_cpl_prot, &chtls_rsk_ops, - &tcp_prot, PF_INET); - chtls_cpl_prot.close = chtls_close; - chtls_cpl_prot.disconnect = chtls_disconnect; - chtls_cpl_prot.destroy = chtls_destroy_sock; - chtls_cpl_prot.shutdown = chtls_shutdown; - chtls_cpl_prot.sendmsg = chtls_sendmsg; - chtls_cpl_prot.splice_eof = chtls_splice_eof; - chtls_cpl_prot.recvmsg = chtls_recvmsg; - chtls_cpl_prot.setsockopt = chtls_setsockopt; - chtls_cpl_prot.getsockopt = chtls_getsockopt; -#if IS_ENABLED(CONFIG_IPV6) - chtls_cpl_protv6 = chtls_cpl_prot; - chtls_init_rsk_ops(&chtls_cpl_protv6, &chtls_rsk_opsv6, - &tcpv6_prot, PF_INET6); -#endif -} - -static int __init chtls_register(void) -{ - chtls_init_ulp_ops(); - register_listen_notifier(&listen_notifier); - cxgb4_register_uld(CXGB4_ULD_TLS, &chtls_uld_info); - return 0; -} - -static void __exit chtls_unregister(void) -{ - unregister_listen_notifier(&listen_notifier); - chtls_free_all_uld(); - cxgb4_unregister_uld(CXGB4_ULD_TLS); -} - -module_init(chtls_register); -module_exit(chtls_unregister); - -MODULE_DESCRIPTION("Chelsio TLS Inline driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Chelsio Communications"); -MODULE_VERSION(CHTLS_DRV_VERSION); diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig index 5bdf731d9503..d76f9cd6bc2d 100644 --- a/drivers/net/ethernet/cirrus/Kconfig +++ b/drivers/net/ethernet/cirrus/Kconfig @@ -6,7 +6,7 @@ config NET_VENDOR_CIRRUS bool "Cirrus devices" default y - depends on ISA || EISA || ARM || MAC || COMPILE_TEST + depends on ARM || MAC || COMPILE_TEST help If you have a network (Ethernet) card belonging to this class, say Y. @@ -20,25 +20,9 @@ if NET_VENDOR_CIRRUS config CS89x0 tristate -config CS89x0_ISA - tristate "CS89x0 ISA driver support" - depends on HAS_IOPORT_MAP - depends on ISA - depends on !PPC32 - depends on CS89x0_PLATFORM=n - select NETDEV_LEGACY_INIT - select CS89x0 - help - Support for CS89x0 chipset based Ethernet cards. If you have a - network (Ethernet) card of this type, say Y and read the file - . - - To compile this driver as a module, choose M here. The module - will be called cs89x0. - config CS89x0_PLATFORM tristate "CS89x0 platform driver support" - depends on ARM || (COMPILE_TEST && !PPC) + depends on ARM || COMPILE_TEST select CS89x0 help Say Y to compile the cs89x0 platform driver. This makes this driver diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index b4bfd6c174e7..fc8b1cb950a1 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -22,19 +22,6 @@ * Domenico Andreoli : cavokz@gmail.com */ - -/* - * Set this to zero to disable DMA code - * - * Note that even if DMA is turned off we still support the 'dma' and 'use_dma' - * module options so we don't break any startup scripts. - */ -#ifndef CONFIG_ISA_DMA_API -#define ALLOW_DMA 0 -#else -#define ALLOW_DMA 1 -#endif - /* * Set this to zero to remove all the debug statements via * dead code elimination @@ -71,13 +58,8 @@ #include #include -#include - #include #include -#if ALLOW_DMA -#include -#endif #include "cs89x0.h" @@ -91,30 +73,6 @@ static char version[] __initdata = "v2.4.3-pre1 Russell Nelson , Andrew Morton"; #define DRV_NAME "cs89x0" - -/* First, a few definitions that the brave might change. - * A zero-terminated list of I/O addresses to be probed. Some special flags.. - * Addr & 1 = Read back the address port, look for signature and reset - * the page window before probing - * Addr & 3 = Reset the page window and probe - * The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space, - * but it is possible that a Cirrus board could be plugged into the ISA - * slots. - */ -/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps - * them to system IRQ numbers. This mapping is card specific and is set to - * the configuration of the Cirrus Eval board for this chip. - */ -#if IS_ENABLED(CONFIG_CS89x0_ISA) -static unsigned int netcard_portlist[] __used __initdata = { - 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, - 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0 -}; -static unsigned int cs8900_irq_map[] = { - 10, 11, 12, 5 -}; -#endif - #if DEBUGGING static unsigned int net_debug = DEBUGGING; #else @@ -149,14 +107,6 @@ struct net_local { int force; /* force various values; see FORCE* above. */ spinlock_t lock; void __iomem *virt_addr;/* CS89x0 virtual address. */ -#if ALLOW_DMA - int use_dma; /* Flag: we're using dma */ - int dma; /* DMA channel */ - int dmasize; /* 16 or 64 */ - unsigned char *dma_buff; /* points to the beginning of the buffer */ - unsigned char *end_dma_buff; /* points to the end of the buffer */ - unsigned char *rx_dma_ptr; /* points to the next packet */ -#endif }; /* Example routines you must write ;->. */ @@ -166,18 +116,6 @@ struct net_local { * Permit 'cs89x0_dma=N' in the kernel boot environment */ #if !defined(MODULE) -#if ALLOW_DMA -static int g_cs89x0_dma; - -static int __init dma_fn(char *str) -{ - g_cs89x0_dma = simple_strtol(str, NULL, 0); - return 1; -} - -__setup("cs89x0_dma=", dma_fn); -#endif /* ALLOW_DMA */ - static int g_cs89x0_media__force; static int __init media_fn(char *str) @@ -290,22 +228,9 @@ get_eeprom_cksum(int off, int len, int *buffer) static void write_irq(struct net_device *dev, int chip_type, int irq) { - int i; - if (chip_type == CS8900) { -#if IS_ENABLED(CONFIG_CS89x0_ISA) - /* Search the mapping table for the corresponding IRQ pin. */ - for (i = 0; i != ARRAY_SIZE(cs8900_irq_map); i++) - if (cs8900_irq_map[i] == irq) - break; - /* Not found */ - if (i == ARRAY_SIZE(cs8900_irq_map)) - i = 3; -#else /* INTRQ0 pin is used for interrupt generation. */ - i = 0; -#endif - writereg(dev, PP_CS8900_ISAINT, i); + writereg(dev, PP_CS8900_ISAINT, 0); } else { writereg(dev, PP_CS8920_ISAINT, irq); } @@ -326,163 +251,6 @@ count_rx_errors(int status, struct net_device *dev) dev->stats.rx_frame_errors++; } -/********************************* - * This page contains DMA routines - *********************************/ - -#if ALLOW_DMA - -#define dma_page_eq(ptr1, ptr2) ((long)(ptr1) >> 17 == (long)(ptr2) >> 17) - -static void -get_dma_channel(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - - if (lp->dma) { - dev->dma = lp->dma; - lp->isa_config |= ISA_RxDMA; - } else { - if ((lp->isa_config & ANY_ISA_DMA) == 0) - return; - dev->dma = lp->isa_config & DMA_NO_MASK; - if (lp->chip_type == CS8900) - dev->dma += 5; - if (dev->dma < 5 || dev->dma > 7) { - lp->isa_config &= ~ANY_ISA_DMA; - return; - } - } -} - -static void -write_dma(struct net_device *dev, int chip_type, int dma) -{ - struct net_local *lp = netdev_priv(dev); - if ((lp->isa_config & ANY_ISA_DMA) == 0) - return; - if (chip_type == CS8900) - writereg(dev, PP_CS8900_ISADMA, dma - 5); - else - writereg(dev, PP_CS8920_ISADMA, dma); -} - -static void -set_dma_cfg(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - - if (lp->use_dma) { - if ((lp->isa_config & ANY_ISA_DMA) == 0) { - cs89_dbg(3, err, "set_dma_cfg(): no DMA\n"); - return; - } - if (lp->isa_config & ISA_RxDMA) { - lp->curr_rx_cfg |= RX_DMA_ONLY; - cs89_dbg(3, info, "set_dma_cfg(): RX_DMA_ONLY\n"); - } else { - lp->curr_rx_cfg |= AUTO_RX_DMA; /* not that we support it... */ - cs89_dbg(3, info, "set_dma_cfg(): AUTO_RX_DMA\n"); - } - } -} - -static int -dma_bufcfg(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - if (lp->use_dma) - return (lp->isa_config & ANY_ISA_DMA) ? RX_DMA_ENBL : 0; - else - return 0; -} - -static int -dma_busctl(struct net_device *dev) -{ - int retval = 0; - struct net_local *lp = netdev_priv(dev); - if (lp->use_dma) { - if (lp->isa_config & ANY_ISA_DMA) - retval |= RESET_RX_DMA; /* Reset the DMA pointer */ - if (lp->isa_config & DMA_BURST) - retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */ - if (lp->dmasize == 64) - retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */ - retval |= MEMORY_ON; /* we need memory enabled to use DMA. */ - } - return retval; -} - -static void -dma_rx(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - struct sk_buff *skb; - int status, length; - unsigned char *bp = lp->rx_dma_ptr; - - status = bp[0] + (bp[1] << 8); - length = bp[2] + (bp[3] << 8); - bp += 4; - - cs89_dbg(5, debug, "%s: receiving DMA packet at %lx, status %x, length %x\n", - dev->name, (unsigned long)bp, status, length); - - if ((status & RX_OK) == 0) { - count_rx_errors(status, dev); - goto skip_this_frame; - } - - /* Malloc up new buffer. */ - skb = netdev_alloc_skb(dev, length + 2); - if (skb == NULL) { - dev->stats.rx_dropped++; - - /* AKPM: advance bp to the next frame */ -skip_this_frame: - bp += (length + 3) & ~3; - if (bp >= lp->end_dma_buff) - bp -= lp->dmasize * 1024; - lp->rx_dma_ptr = bp; - return; - } - skb_reserve(skb, 2); /* longword align L3 header */ - - if (bp + length > lp->end_dma_buff) { - int semi_cnt = lp->end_dma_buff - bp; - skb_put_data(skb, bp, semi_cnt); - skb_put_data(skb, lp->dma_buff, length - semi_cnt); - } else { - skb_put_data(skb, bp, length); - } - bp += (length + 3) & ~3; - if (bp >= lp->end_dma_buff) - bp -= lp->dmasize*1024; - lp->rx_dma_ptr = bp; - - cs89_dbg(3, info, "%s: received %d byte DMA packet of type %x\n", - dev->name, length, - ((skb->data[ETH_ALEN + ETH_ALEN] << 8) | - skb->data[ETH_ALEN + ETH_ALEN + 1])); - - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; -} - -static void release_dma_buff(struct net_local *lp) -{ - if (lp->dma_buff) { - free_pages((unsigned long)(lp->dma_buff), - get_order(lp->dmasize * 1024)); - lp->dma_buff = NULL; - } -} - -#endif /* ALLOW_DMA */ - static void control_dc_dc(struct net_device *dev, int on_not_off) { @@ -781,27 +549,6 @@ static irqreturn_t net_interrupt(int irq, void *dev_id) */ netif_wake_queue(dev); /* Inform upper layers. */ } -#if ALLOW_DMA - if (lp->use_dma && (status & RX_DMA)) { - int count = readreg(dev, PP_DmaFrameCnt); - while (count) { - cs89_dbg(5, debug, - "%s: receiving %d DMA frames\n", - dev->name, count); - if (count > 1) - cs89_dbg(2, debug, - "%s: receiving %d DMA frames\n", - dev->name, count); - dma_rx(dev); - if (--count == 0) - count = readreg(dev, PP_DmaFrameCnt); - if (count > 0) - cs89_dbg(2, debug, - "%s: continuing with %d DMA frames\n", - dev->name, count); - } - } -#endif break; case ISQ_RX_MISS_EVENT: dev->stats.rx_missed_errors += (status >> 6); @@ -860,14 +607,6 @@ net_open(struct net_device *dev) goto bad_out; } } else { -#if IS_ENABLED(CONFIG_CS89x0_ISA) - if (((1 << dev->irq) & lp->irq_map) == 0) { - pr_err("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", - dev->name, dev->irq, lp->irq_map); - ret = -EAGAIN; - goto bad_out; - } -#endif /* FIXME: Cirrus' release had this: */ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ); /* And 2.3.47 had this: */ @@ -882,46 +621,6 @@ net_open(struct net_device *dev) } } -#if ALLOW_DMA - if (lp->use_dma && (lp->isa_config & ANY_ISA_DMA)) { - unsigned long flags; - lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL, - get_order(lp->dmasize * 1024)); - if (!lp->dma_buff) { - pr_err("%s: cannot get %dK memory for DMA\n", - dev->name, lp->dmasize); - goto release_irq; - } - cs89_dbg(1, debug, "%s: dma %lx %lx\n", - dev->name, - (unsigned long)lp->dma_buff, - (unsigned long)isa_virt_to_bus(lp->dma_buff)); - if ((unsigned long)lp->dma_buff >= MAX_DMA_ADDRESS || - !dma_page_eq(lp->dma_buff, - lp->dma_buff + lp->dmasize * 1024 - 1)) { - pr_err("%s: not usable as DMA buffer\n", dev->name); - goto release_irq; - } - memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */ - if (request_dma(dev->dma, dev->name)) { - pr_err("%s: cannot get dma channel %d\n", - dev->name, dev->dma); - goto release_irq; - } - write_dma(dev, lp->chip_type, dev->dma); - lp->rx_dma_ptr = lp->dma_buff; - lp->end_dma_buff = lp->dma_buff + lp->dmasize * 1024; - spin_lock_irqsave(&lp->lock, flags); - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - set_dma_mode(dev->dma, DMA_RX_MODE); /* auto_init as well */ - set_dma_addr(dev->dma, isa_virt_to_bus(lp->dma_buff)); - set_dma_count(dev->dma, lp->dmasize * 1024); - enable_dma(dev->dma); - spin_unlock_irqrestore(&lp->lock, flags); - } -#endif /* ALLOW_DMA */ - /* set the Ethernet address */ for (i = 0; i < ETH_ALEN / 2; i++) writereg(dev, PP_IA + i * 2, @@ -958,11 +657,6 @@ net_open(struct net_device *dev) pr_err("%s: EEPROM is configured for unavailable media\n", dev->name); release_dma: -#if ALLOW_DMA - free_dma(dev->dma); -release_irq: - release_dma_buff(lp); -#endif writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON)); free_irq(dev->irq, dev); @@ -1048,9 +742,6 @@ net_open(struct net_device *dev) if (lp->isa_config & STREAM_TRANSFER) lp->curr_rx_cfg |= RX_STREAM_ENBL; -#if ALLOW_DMA - set_dma_cfg(dev); -#endif writereg(dev, PP_RxCFG, lp->curr_rx_cfg); writereg(dev, PP_TxCFG, (TX_LOST_CRS_ENBL | @@ -1063,19 +754,12 @@ net_open(struct net_device *dev) writereg(dev, PP_BufCFG, (READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | -#if ALLOW_DMA - dma_bufcfg(dev) | -#endif TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL)); /* now that we've got our act together, enable everything */ - writereg(dev, PP_BusCTL, (ENABLE_IRQ - | (dev->mem_start ? MEMORY_ON : 0) /* turn memory on */ -#if ALLOW_DMA - | dma_busctl(dev) -#endif - )); + writereg(dev, PP_BusCTL, + (ENABLE_IRQ | (dev->mem_start ? MEMORY_ON : 0))); /* turn memory on */ netif_start_queue(dev); cs89_dbg(1, debug, "net_open() succeeded\n"); return 0; @@ -1087,10 +771,6 @@ net_open(struct net_device *dev) static int net_close(struct net_device *dev) { -#if ALLOW_DMA - struct net_local *lp = netdev_priv(dev); -#endif - netif_stop_queue(dev); writereg(dev, PP_RxCFG, 0); @@ -1100,13 +780,6 @@ net_close(struct net_device *dev) free_irq(dev->irq, dev); -#if ALLOW_DMA - if (lp->use_dma && lp->dma) { - free_dma(dev->dma); - release_dma_buff(lp); - } -#endif - /* Update the statistics here. */ return 0; } @@ -1321,13 +994,6 @@ cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular) memset(lp, 0, sizeof(*lp)); spin_lock_init(&lp->lock); #ifndef MODULE -#if ALLOW_DMA - if (g_cs89x0_dma) { - lp->use_dma = 1; - lp->dma = g_cs89x0_dma; - lp->dmasize = 16; /* Could make this an option... */ - } -#endif lp->force = g_cs89x0_media__force; #endif } @@ -1525,40 +1191,12 @@ cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular) dev->irq = i; } else { i = lp->isa_config & INT_NO_MASK; -#if IS_ENABLED(CONFIG_CS89x0_ISA) - if (lp->chip_type == CS8900) { - /* Translate the IRQ using the IRQ mapping table. */ - if (i >= ARRAY_SIZE(cs8900_irq_map)) - pr_err("invalid ISA interrupt number %d\n", i); - else - i = cs8900_irq_map[i]; - - lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */ - } else { - int irq_map_buff[IRQ_MAP_LEN/2]; - - if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA, - IRQ_MAP_LEN / 2, - irq_map_buff) >= 0) { - if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT) - lp->irq_map = ((irq_map_buff[0] >> 8) | - (irq_map_buff[1] << 8)); - } - } -#endif if (!dev->irq) dev->irq = i; } pr_cont(" IRQ %d", dev->irq); - -#if ALLOW_DMA - if (lp->use_dma) { - get_dma_channel(dev); - pr_cont(", DMA %d", dev->dma); - } else -#endif - pr_cont(", programmed I/O"); + pr_cont(", programmed I/O"); /* print the ethernet address. */ pr_cont(", MAC %pM\n", dev->dev_addr); @@ -1578,270 +1216,6 @@ cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular) return retval; } -#if IS_ENABLED(CONFIG_CS89x0_ISA) -/* - * This function converts the I/O port address used by the cs89x0_probe() and - * init_module() functions to the I/O memory address used by the - * cs89x0_probe1() function. - */ -static int __init -cs89x0_ioport_probe(struct net_device *dev, unsigned long ioport, int modular) -{ - struct net_local *lp = netdev_priv(dev); - int ret; - void __iomem *io_mem; - - if (!lp) - return -ENOMEM; - - dev->base_addr = ioport; - - if (!request_region(ioport, NETCARD_IO_EXTENT, DRV_NAME)) { - ret = -EBUSY; - goto out; - } - - io_mem = ioport_map(ioport & ~3, NETCARD_IO_EXTENT); - if (!io_mem) { - ret = -ENOMEM; - goto release; - } - - /* if they give us an odd I/O address, then do ONE write to - * the address port, to get it back to address zero, where we - * expect to find the EISA signature word. An IO with a base of 0x3 - * will skip the test for the ADD_PORT. - */ - if (ioport & 1) { - cs89_dbg(1, info, "%s: odd ioaddr 0x%lx\n", dev->name, ioport); - if ((ioport & 2) != 2) { - if ((ioread16(io_mem + ADD_PORT) & ADD_MASK) != - ADD_SIG) { - pr_err("%s: bad signature 0x%x\n", - dev->name, ioread16(io_mem + ADD_PORT)); - ret = -ENODEV; - goto unmap; - } - } - } - - ret = cs89x0_probe1(dev, io_mem, modular); - if (!ret) - goto out; -unmap: - ioport_unmap(io_mem); -release: - release_region(ioport, NETCARD_IO_EXTENT); -out: - return ret; -} - -#ifndef MODULE -/* Check for a network adaptor of this type, and return '0' iff one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). - * Return 0 on success. - */ - -struct net_device * __init cs89x0_probe(int unit) -{ - struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); - unsigned *port; - int err = 0; - int irq; - int io; - - if (!dev) - return ERR_PTR(-ENODEV); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - io = dev->base_addr; - irq = dev->irq; - - cs89_dbg(0, info, "cs89x0_probe(0x%x)\n", io); - - if (io > 0x1ff) { /* Check a single specified location. */ - err = cs89x0_ioport_probe(dev, io, 0); - } else if (io != 0) { /* Don't probe at all. */ - err = -ENXIO; - } else { - for (port = netcard_portlist; *port; port++) { - if (cs89x0_ioport_probe(dev, *port, 0) == 0) - break; - dev->irq = irq; - } - if (!*port) - err = -ENODEV; - } - if (err) - goto out; - return dev; -out: - free_netdev(dev); - pr_warn("no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n"); - return ERR_PTR(err); -} -#else -static struct net_device *dev_cs89x0; - -/* Support the 'debug' module parm even if we're compiled for non-debug to - * avoid breaking someone's startup scripts - */ - -static int io; -static int irq; -static int debug; -static char media[8]; -static int duplex = -1; - -static int use_dma; /* These generate unused var warnings if ALLOW_DMA = 0 */ -static int dma; -static int dmasize = 16; /* or 64 */ - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param(debug, int, 0); -module_param_string(media, media, sizeof(media), 0); -module_param(duplex, int, 0); -module_param_hw(dma , int, dma, 0); -module_param(dmasize , int, 0); -module_param(use_dma , int, 0); -MODULE_PARM_DESC(io, "cs89x0 I/O base address"); -MODULE_PARM_DESC(irq, "cs89x0 IRQ number"); -#if DEBUGGING -MODULE_PARM_DESC(debug, "cs89x0 debug level (0-6)"); -#else -MODULE_PARM_DESC(debug, "(ignored)"); -#endif -MODULE_PARM_DESC(media, "Set cs89x0 adapter(s) media type(s) (rj45,bnc,aui)"); -/* No other value than -1 for duplex seems to be currently interpreted */ -MODULE_PARM_DESC(duplex, "(ignored)"); -#if ALLOW_DMA -MODULE_PARM_DESC(dma , "cs89x0 ISA DMA channel; ignored if use_dma=0"); -MODULE_PARM_DESC(dmasize , "cs89x0 DMA size in kB (16,64); ignored if use_dma=0"); -MODULE_PARM_DESC(use_dma , "cs89x0 using DMA (0-1)"); -#else -MODULE_PARM_DESC(dma , "(ignored)"); -MODULE_PARM_DESC(dmasize , "(ignored)"); -MODULE_PARM_DESC(use_dma , "(ignored)"); -#endif - -MODULE_AUTHOR("Mike Cruse, Russwll Nelson , Andrew Morton"); -MODULE_LICENSE("GPL"); - -/* - * media=t - specify media type - * or media=2 - * or media=aui - * or medai=auto - * duplex=0 - specify forced half/full/autonegotiate duplex - * debug=# - debug level - * - * Default Chip Configuration: - * DMA Burst = enabled - * IOCHRDY Enabled = enabled - * UseSA = enabled - * CS8900 defaults to half-duplex if not specified on command-line - * CS8920 defaults to autoneg if not specified on command-line - * Use reset defaults for other config parameters - * - * Assumptions: - * media type specified is supported (circuitry is present) - * if memory address is > 1MB, then required mem decode hw is present - * if 10B-2, then agent other than driver will enable DC/DC converter - * (hw or software util) - */ - -static int __init cs89x0_isa_init_module(void) -{ - struct net_device *dev; - struct net_local *lp; - int ret = 0; - -#if DEBUGGING - net_debug = debug; -#else - debug = 0; -#endif - dev = alloc_etherdev(sizeof(struct net_local)); - if (!dev) - return -ENOMEM; - - dev->irq = irq; - dev->base_addr = io; - lp = netdev_priv(dev); - -#if ALLOW_DMA - if (use_dma) { - lp->use_dma = use_dma; - lp->dma = dma; - lp->dmasize = dmasize; - } -#endif - - spin_lock_init(&lp->lock); - - /* boy, they'd better get these right */ - if (!strcmp(media, "rj45")) - lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T; - else if (!strcmp(media, "aui")) - lp->adapter_cnf = A_CNF_MEDIA_AUI | A_CNF_AUI; - else if (!strcmp(media, "bnc")) - lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2; - else - lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T; - - if (duplex == -1) - lp->auto_neg_cnf = AUTO_NEG_ENABLE; - - if (io == 0) { - pr_err("Module autoprobing not allowed\n"); - pr_err("Append io=0xNNN\n"); - ret = -EPERM; - goto out; - } else if (io <= 0x1ff) { - ret = -ENXIO; - goto out; - } - -#if ALLOW_DMA - if (use_dma && dmasize != 16 && dmasize != 64) { - pr_err("dma size must be either 16K or 64K, not %dK\n", - dmasize); - ret = -EPERM; - goto out; - } -#endif - ret = cs89x0_ioport_probe(dev, io, 1); - if (ret) - goto out; - - dev_cs89x0 = dev; - return 0; -out: - free_netdev(dev); - return ret; -} -module_init(cs89x0_isa_init_module); - -static void __exit cs89x0_isa_cleanup_module(void) -{ - struct net_local *lp = netdev_priv(dev_cs89x0); - - unregister_netdev(dev_cs89x0); - iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT); - ioport_unmap(lp->virt_addr); - release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT); - free_netdev(dev_cs89x0); -} -module_exit(cs89x0_isa_cleanup_module); -#endif /* MODULE */ -#endif /* CONFIG_CS89x0_ISA */ - -#if IS_ENABLED(CONFIG_CS89x0_PLATFORM) static int __init cs89x0_platform_probe(struct platform_device *pdev) { struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); @@ -1906,8 +1280,6 @@ static struct platform_driver cs89x0_driver = { module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe); -#endif /* CONFIG_CS89x0_PLATFORM */ - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Crystal Semiconductor (Now Cirrus Logic) CS89[02]0 network driver"); MODULE_AUTHOR("Russell Nelson "); diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index f9504f340c4a..f38a10c79907 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -333,10 +333,8 @@ static unsigned int de_ok_to_advertise (struct de_private *de, u32 new_media); static const struct pci_device_id de_pci_tbl[] = { - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VDEVICE(DEC, PCI_DEVICE_ID_DEC_TULIP), .driver_data = 0 }, + { PCI_VDEVICE(DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS), .driver_data = 1 }, { }, }; MODULE_DEVICE_TABLE(pci, de_pci_tbl); diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index 2d3bd343b6e6..dabc091a0c66 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -2076,11 +2076,11 @@ static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) static const struct pci_device_id dmfe_pci_tbl[] = { - { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, - { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, - { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, - { 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID }, - { 0, } + { PCI_DEVICE(0x1282, 0x9132), .driver_data = PCI_DM9132_ID }, + { PCI_DEVICE(0x1282, 0x9102), .driver_data = PCI_DM9102_ID }, + { PCI_DEVICE(0x1282, 0x9100), .driver_data = PCI_DM9100_ID }, + { PCI_DEVICE(0x1282, 0x9009), .driver_data = PCI_DM9009_ID }, + { } }; MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); @@ -2101,7 +2101,7 @@ static int __maybe_unused dmfe_suspend(struct device *dev_d) dw32(DCR7, 0); dw32(DCR5, dr32(DCR5)); - /* Fre RX buffers */ + /* Free RX buffers */ dmfe_free_rxbuffer(db); /* Enable WOL */ diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index b608585f1954..a7200eb66594 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -196,45 +196,45 @@ const struct tulip_chip_table tulip_tbl[] = { static const struct pci_device_id tulip_pci_tbl[] = { - { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, - { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21143 }, - { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 }, - { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 }, - { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, -/* { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },*/ - { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 }, - { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 }, - { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1317, 0x1985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1317, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x13D1, 0xAB02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x13D1, 0xAB03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x13D1, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x104A, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x104A, 0x2774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1259, 0xa120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 }, - { 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 }, + { PCI_DEVICE(0x1011, 0x0009), .driver_data = DC21140 }, + { PCI_DEVICE(0x1011, 0x0019), .driver_data = DC21143 }, + { PCI_DEVICE(0x11AD, 0x0002), .driver_data = LC82C168 }, + { PCI_DEVICE(0x10d9, 0x0512), .driver_data = MX98713 }, + { PCI_DEVICE(0x10d9, 0x0531), .driver_data = MX98715 }, +/* { PCI_DEVICE(0x10d9, 0x0531), .driver_data = MX98725 },*/ + { PCI_DEVICE(0x125B, 0x1400), .driver_data = AX88140 }, + { PCI_DEVICE(0x11AD, 0xc115), .driver_data = PNIC2 }, + { PCI_DEVICE(0x1317, 0x0981), .driver_data = COMET }, + { PCI_DEVICE(0x1317, 0x0985), .driver_data = COMET }, + { PCI_DEVICE(0x1317, 0x1985), .driver_data = COMET }, + { PCI_DEVICE(0x1317, 0x9511), .driver_data = COMET }, + { PCI_DEVICE(0x13D1, 0xAB02), .driver_data = COMET }, + { PCI_DEVICE(0x13D1, 0xAB03), .driver_data = COMET }, + { PCI_DEVICE(0x13D1, 0xAB08), .driver_data = COMET }, + { PCI_DEVICE(0x104A, 0x0981), .driver_data = COMET }, + { PCI_DEVICE(0x104A, 0x2774), .driver_data = COMET }, + { PCI_DEVICE(0x1259, 0xa120), .driver_data = COMET }, + { PCI_DEVICE(0x11F6, 0x9881), .driver_data = COMPEX9881 }, + { PCI_DEVICE(0x8086, 0x0039), .driver_data = I21145 }, #ifdef CONFIG_TULIP_DM910X - { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X }, - { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X }, + { PCI_DEVICE(0x1282, 0x9100), .driver_data = DM910X }, + { PCI_DEVICE(0x1282, 0x9102), .driver_data = DM910X }, #endif - { 0x1113, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, - { 0x1113, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1186, 0x1541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1186, 0x1561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1186, 0x1591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x14f1, 0x1803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CONEXANT }, - { 0x1626, 0x8410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1737, 0xAB09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x1737, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x17B3, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, - { 0x10b7, 0x9300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* 3Com 3CSOHO100B-TX */ - { 0x14ea, 0xab08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Planex FNW-3602-TX */ - { 0x1414, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, /* Microsoft MN-120 */ - { 0x1414, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { PCI_DEVICE(0x1113, 0x1216), .driver_data = COMET }, + { PCI_DEVICE(0x1113, 0x1217), .driver_data = MX98715 }, + { PCI_DEVICE(0x1113, 0x9511), .driver_data = COMET }, + { PCI_DEVICE(0x1186, 0x1541), .driver_data = COMET }, + { PCI_DEVICE(0x1186, 0x1561), .driver_data = COMET }, + { PCI_DEVICE(0x1186, 0x1591), .driver_data = COMET }, + { PCI_DEVICE(0x14f1, 0x1803), .driver_data = CONEXANT }, + { PCI_DEVICE(0x1626, 0x8410), .driver_data = COMET }, + { PCI_DEVICE(0x1737, 0xAB09), .driver_data = COMET }, + { PCI_DEVICE(0x1737, 0xAB08), .driver_data = COMET }, + { PCI_DEVICE(0x17B3, 0xAB08), .driver_data = COMET }, + { PCI_DEVICE(0x10b7, 0x9300), .driver_data = COMET }, /* 3Com 3CSOHO100B-TX */ + { PCI_DEVICE(0x14ea, 0xab08), .driver_data = COMET }, /* Planex FNW-3602-TX */ + { PCI_DEVICE(0x1414, 0x0001), .driver_data = COMET }, /* Microsoft MN-120 */ + { PCI_DEVICE(0x1414, 0x0002), .driver_data = COMET }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, tulip_pci_tbl); @@ -1279,8 +1279,8 @@ static const struct net_device_ops tulip_netdev_ops = { }; static const struct pci_device_id early_486_chipsets[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424) }, - { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82424) }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_496) }, { }, }; diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index 6e4d8d31aba9..34b234b6b159 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -1728,9 +1728,9 @@ static u16 phy_read_1bit(struct uli526x_board_info *db) static const struct pci_device_id uli526x_pci_tbl[] = { - { 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5261_ID }, - { 0x10B9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5263_ID }, - { 0, } + { PCI_DEVICE(0x10B9, 0x5261), .driver_data = PCI_ULI5261_ID }, + { PCI_DEVICE(0x10B9, 0x5263), .driver_data = PCI_ULI5263_ID }, + { } }; MODULE_DEVICE_TABLE(pci, uli526x_pci_tbl); diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index a24a25a5f73d..a5581f1d0141 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -210,9 +210,16 @@ enum chip_capability_flags { }; static const struct pci_device_id w840_pci_tbl[] = { - { 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 }, - { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { + PCI_DEVICE_SUB(0x1050, 0x0840, PCI_ANY_ID, 0x8153), + .driver_data = 0, + }, { + PCI_DEVICE(0x1050, 0x0840), + .driver_data = 1, + }, { + PCI_DEVICE(0x11f6, 0x2011), + .driver_data = 2, + }, { } }; MODULE_DEVICE_TABLE(pci, w840_pci_tbl); diff --git a/drivers/net/ethernet/dlink/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h index 9ebf7a6db93e..00ef7d03b4d6 100644 --- a/drivers/net/ethernet/dlink/dl2k.h +++ b/drivers/net/ethernet/dlink/dl2k.h @@ -418,12 +418,12 @@ struct netdev_private { #define CHIP_IP1000A 1 static const struct pci_device_id rio_pci_tbl[] = { - {0x1186, 0x4000, PCI_ANY_ID, PCI_ANY_ID, }, - {0x13f0, 0x1021, PCI_ANY_ID, PCI_ANY_ID, }, - { PCI_VDEVICE(SUNDANCE, 0x1023), CHIP_IP1000A }, - { PCI_VDEVICE(SUNDANCE, 0x2021), CHIP_IP1000A }, - { PCI_VDEVICE(DLINK, 0x9021), CHIP_IP1000A }, - { PCI_VDEVICE(DLINK, 0x4020), CHIP_IP1000A }, + { PCI_DEVICE(0x1186, 0x4000) }, + { PCI_DEVICE(0x13f0, 0x1021) }, + { PCI_VDEVICE(SUNDANCE, 0x1023), .driver_data = CHIP_IP1000A }, + { PCI_VDEVICE(SUNDANCE, 0x2021), .driver_data = CHIP_IP1000A }, + { PCI_VDEVICE(DLINK, 0x9021), .driver_data = CHIP_IP1000A }, + { PCI_VDEVICE(DLINK, 0x4020), .driver_data = CHIP_IP1000A }, { } }; MODULE_DEVICE_TABLE (pci, rio_pci_tbl); diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index 277c50ef773f..58873fcd3eaf 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -192,13 +192,13 @@ IVc. Errata #endif static const struct pci_device_id sundance_pci_tbl[] = { - { 0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0 }, - { 0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1 }, - { 0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2 }, - { 0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3 }, - { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, - { 0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, + { PCI_DEVICE_SUB(0x1186, 0x1002, 0x1186, 0x1002), .driver_data = 0 }, + { PCI_DEVICE_SUB(0x1186, 0x1002, 0x1186, 0x1003), .driver_data = 1 }, + { PCI_DEVICE_SUB(0x1186, 0x1002, 0x1186, 0x1012), .driver_data = 2 }, + { PCI_DEVICE_SUB(0x1186, 0x1002, 0x1186, 0x1040), .driver_data = 3 }, + { PCI_DEVICE(0x1186, 0x1002), .driver_data = 4 }, + { PCI_DEVICE(0x13F0, 0x0201), .driver_data = 5 }, + { PCI_DEVICE(0x13F0, 0x0200), .driver_data = 6 }, { } }; MODULE_DEVICE_TABLE(pci, sundance_pci_tbl); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index eab81e073e1e..230c91559322 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -2508,7 +2508,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd, req->desired_read_len = cpu_to_le32(data_size); req->read_offset = cpu_to_le32(data_offset); - strcpy(req->object_name, obj_name); + strscpy(req->object_name, obj_name); req->descriptor_count = cpu_to_le32(1); req->buf_len = cpu_to_le32(data_size); req->addr_low = cpu_to_le32((cmd->dma & 0xFFFFFFFF)); diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index 3c9961806f75..bdc38aac5850 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -1935,10 +1935,10 @@ static int netdev_close(struct net_device *dev) } static const struct pci_device_id fealnx_pci_tbl[] = { - {0x1516, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0x1516, 0x0803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - {0x1516, 0x0891, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, - {} /* terminate list */ + { PCI_DEVICE(0x1516, 0x0800), .driver_data = 0 }, + { PCI_DEVICE(0x1516, 0x0803), .driver_data = 1 }, + { PCI_DEVICE(0x1516, 0x0891), .driver_data = 2 }, + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, fealnx_pci_tbl); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index 59f5c778df38..fcc0a30a9d57 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -10,7 +10,7 @@ #include "dpaa2-eth.h" /* To be kept in sync with DPNI statistics */ -static char dpaa2_ethtool_stats[][ETH_GSTRING_LEN] = { +static const char dpaa2_ethtool_stats[][ETH_GSTRING_LEN] = { "[hw] rx frames", "[hw] rx bytes", "[hw] rx mcast frames", @@ -37,7 +37,7 @@ static char dpaa2_ethtool_stats[][ETH_GSTRING_LEN] = { #define DPAA2_ETH_NUM_STATS ARRAY_SIZE(dpaa2_ethtool_stats) -static char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { +static const char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = { /* per-cpu stats */ "[drv] tx conf frames", "[drv] tx conf bytes", diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 52c1cb9cb7e0..45f276c2c3ec 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -51,36 +51,30 @@ dpaa2_switch_filter_block_get_unused(struct ethsw_core *ethsw) return NULL; } -static u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv, - struct net_device *bridge_dev) +static bool dpaa2_switch_fdb_in_use_by_others(struct ethsw_core *ethsw, + struct dpaa2_switch_fdb *fdb, + struct ethsw_port_priv *except) { - struct ethsw_port_priv *other_port_priv = NULL; - struct dpaa2_switch_fdb *fdb; + int i; + + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { + if (!ethsw->ports[i] || ethsw->ports[i] == except) + continue; + if (ethsw->ports[i]->fdb == fdb) + return true; + } + + return false; +} + +static struct dpaa2_switch_fdb * +dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv, + struct net_device *upper_dev) +{ + struct ethsw_port_priv *other_port_priv; struct net_device *other_dev; struct list_head *iter; - /* If we leave a bridge (bridge_dev is NULL), find an unused - * FDB and use that. - */ - if (!bridge_dev) { - fdb = dpaa2_switch_fdb_get_unused(port_priv->ethsw_data); - - /* If there is no unused FDB, we must be the last port that - * leaves the last bridge, all the others are standalone. We - * can just keep the FDB that we already have. - */ - - if (!fdb) { - port_priv->fdb->bridge_dev = NULL; - return 0; - } - - port_priv->fdb = fdb; - port_priv->fdb->in_use = true; - port_priv->fdb->bridge_dev = NULL; - return 0; - } - /* The below call to netdev_for_each_lower_dev() demands the RTNL lock * being held. Assert on it so that it's easier to catch new code * paths that reach this point without the RTNL lock. @@ -90,7 +84,7 @@ static u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv, /* If part of a bridge, use the FDB of the first dpaa2 switch interface * to be present in that bridge */ - netdev_for_each_lower_dev(bridge_dev, other_dev, iter) { + netdev_for_each_lower_dev(upper_dev, other_dev, iter) { if (!dpaa2_switch_port_dev_check(other_dev)) continue; @@ -98,27 +92,62 @@ static u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv, continue; other_port_priv = netdev_priv(other_dev); - break; + return other_port_priv->fdb; } - /* The current port is about to change its FDB to the one used by the - * first port that joined the bridge. - */ - if (other_port_priv) { - /* The previous FDB is about to become unused, since the - * interface is no longer standalone. - */ - port_priv->fdb->in_use = false; - port_priv->fdb->bridge_dev = NULL; + return port_priv->fdb; +} - /* Get a reference to the new FDB */ - port_priv->fdb = other_port_priv->fdb; +static struct dpaa2_switch_fdb * +dpaa2_switch_fdb_for_leave(struct ethsw_port_priv *port_priv) +{ + struct ethsw_core *ethsw = port_priv->ethsw_data; + + /* If this is the last user of the FDB, just keep using it. */ + if (!dpaa2_switch_fdb_in_use_by_others(ethsw, port_priv->fdb, + port_priv)) { + return port_priv->fdb; + } + + /* Since we are not the last port which leaves a bridge, + * acquire a new FDB and use it. + */ + return dpaa2_switch_fdb_get_unused(ethsw); +} + +static void dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv, + struct net_device *upper_dev, + bool linking) +{ + struct dpaa2_switch_fdb *old_fdb = port_priv->fdb; + struct ethsw_core *ethsw = port_priv->ethsw_data; + struct dpaa2_switch_fdb *new_fdb; + + new_fdb = linking ? dpaa2_switch_fdb_for_join(port_priv, upper_dev) : + dpaa2_switch_fdb_for_leave(port_priv); + /* The number of FDBs is sized to accommodate all switch ports as + * standalone, each with its private FDB, which means that FDB must be + * valid here even on the leave path. WARN if not. + */ + if (WARN_ON(!new_fdb)) + return; + + if (old_fdb != new_fdb) { + /* The previous FDB is about to become unused, release + * it if we are the last user. + */ + if (!dpaa2_switch_fdb_in_use_by_others(ethsw, port_priv->fdb, + port_priv)) { + old_fdb->in_use = false; + old_fdb->bridge_dev = NULL; + } + + new_fdb->in_use = true; + port_priv->fdb = new_fdb; } /* Keep track of the new upper bridge device */ - port_priv->fdb->bridge_dev = bridge_dev; - - return 0; + port_priv->fdb->bridge_dev = linking ? upper_dev : NULL; } static void dpaa2_switch_fdb_get_flood_cfg(struct ethsw_core *ethsw, u16 fdb_id, @@ -281,49 +310,68 @@ static int dpaa2_switch_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvi } static int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv, - u16 vid, u16 flags) + u16 vid, u16 flags, bool changed) { struct ethsw_core *ethsw = port_priv->ethsw_data; struct net_device *netdev = port_priv->netdev; struct dpsw_vlan_if_cfg vcfg = {0}; int err; - if (port_priv->vlans[vid]) { - netdev_err(netdev, "VLAN %d already configured\n", vid); - return -EEXIST; - } - - /* If hit, this VLAN rule will lead the packet into the FDB table - * specified in the vlan configuration below - */ - vcfg.num_ifs = 1; - vcfg.if_id[0] = port_priv->idx; - vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); - vcfg.options |= DPSW_VLAN_ADD_IF_OPT_FDB_ID; - err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid, &vcfg); - if (err) { - netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err); - return err; - } - - port_priv->vlans[vid] = ETHSW_VLAN_MEMBER; - - if (flags & BRIDGE_VLAN_INFO_UNTAGGED) { - err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0, - ethsw->dpsw_handle, - vid, &vcfg); + if (!port_priv->vlans[vid]) { + /* If hit, this VLAN rule will lead the packet into the FDB + * table specified in the vlan configuration below + */ + vcfg.num_ifs = 1; + vcfg.if_id[0] = port_priv->idx; + vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); + vcfg.options |= DPSW_VLAN_ADD_IF_OPT_FDB_ID; + err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, + vid, &vcfg); if (err) { - netdev_err(netdev, - "dpsw_vlan_add_if_untagged err %d\n", err); + netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err); return err; } - port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED; + + port_priv->vlans[vid] = ETHSW_VLAN_MEMBER; + } + + memset(&vcfg, 0, sizeof(vcfg)); + vcfg.num_ifs = 1; + vcfg.if_id[0] = port_priv->idx; + + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) { + if (!(port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED)) { + err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0, + ethsw->dpsw_handle, + vid, &vcfg); + if (err) { + netdev_err(netdev, + "dpsw_vlan_add_if_untagged err %d\n", + err); + return err; + } + port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED; + } + } else if (changed && (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED)) { + err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, + ethsw->dpsw_handle, + vid, &vcfg); + if (err) { + netdev_err(netdev, + "dpsw_vlan_remove_if_untagged err %d\n", + err); + } + port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED; } if (flags & BRIDGE_VLAN_INFO_PVID) { err = dpaa2_switch_port_set_pvid(port_priv, vid); if (err) return err; + } else if (changed && port_priv->vlans[vid] & ETHSW_VLAN_PVID) { + err = dpaa2_switch_port_set_pvid(port_priv, 4095); + if (err) + return err; } return 0; @@ -953,6 +1001,7 @@ static int dpaa2_switch_port_vlan_add(struct net_device *netdev, __be16 proto, .obj.orig_dev = netdev, /* This API only allows programming tagged, non-PVID VIDs */ .flags = 0, + .changed = false, }; return dpaa2_switch_port_vlans_add(netdev, &vlan); @@ -1786,35 +1835,19 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev, struct dpsw_attr *attr = ðsw->sw_attr; int err = 0; - /* Make sure that the VLAN is not already configured - * on the switch port - */ - if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER) { - netdev_err(netdev, "VLAN %d already configured\n", vlan->vid); - return -EEXIST; - } - - /* Check if there is space for a new VLAN */ - err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, - ðsw->sw_attr); - if (err) { - netdev_err(netdev, "dpsw_get_attributes err %d\n", err); - return err; - } - if (attr->max_vlans - attr->num_vlans < 1) - return -ENOSPC; - - /* Check if there is space for a new VLAN */ - err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, - ðsw->sw_attr); - if (err) { - netdev_err(netdev, "dpsw_get_attributes err %d\n", err); - return err; - } - if (attr->max_vlans - attr->num_vlans < 1) - return -ENOSPC; - if (!port_priv->ethsw_data->vlans[vlan->vid]) { + /* Only check for space in case this is a new VLAN from the + * DPSW perspective + */ + err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, + ðsw->sw_attr); + if (err) { + netdev_err(netdev, "dpsw_get_attributes err %d\n", err); + return err; + } + if (attr->max_vlans - attr->num_vlans < 1) + return -ENOSPC; + /* this is a new VLAN */ err = dpaa2_switch_add_vlan(port_priv, vlan->vid); if (err) @@ -1823,7 +1856,8 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev, port_priv->ethsw_data->vlans[vlan->vid] |= ETHSW_VLAN_GLOBAL; } - return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags); + return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags, + vlan->changed); } static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc, @@ -2029,7 +2063,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, if (err) return err; - dpaa2_switch_port_set_fdb(port_priv, upper_dev); + dpaa2_switch_port_set_fdb(port_priv, upper_dev, true); /* Inherit the initial bridge port learning state */ learn_ena = br_port_flag_is_set(netdev, BR_LEARNING); @@ -2055,7 +2089,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, err_switchdev_offload: err_egress_flood: - dpaa2_switch_port_set_fdb(port_priv, NULL); + dpaa2_switch_port_set_fdb(port_priv, upper_dev, false); return err; } @@ -2102,7 +2136,7 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) if (err) netdev_err(netdev, "Unable to clear RX VLANs from old FDB table, err (%d)\n", err); - dpaa2_switch_port_set_fdb(port_priv, NULL); + dpaa2_switch_port_set_fdb(port_priv, port_priv->fdb->bridge_dev, false); /* Restore all RX VLANs into the new FDB table that we just joined */ err = vlan_for_each(netdev, dpaa2_switch_port_restore_rxvlan, netdev); @@ -2139,7 +2173,8 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) * the dpaa2 switch interfaces are not capable to be VLAN unaware */ return dpaa2_switch_port_add_vlan(port_priv, DEFAULT_VLAN_ID, - BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID); + BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID, + false); } static int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev) @@ -2412,18 +2447,13 @@ static int dpaa2_switch_port_blocking_event(struct notifier_block *nb, /* Build a linear skb based on a single-buffer frame descriptor */ static struct sk_buff *dpaa2_switch_build_linear_skb(struct ethsw_core *ethsw, - const struct dpaa2_fd *fd) + const struct dpaa2_fd *fd, + void *fd_vaddr) { u16 fd_offset = dpaa2_fd_get_offset(fd); - dma_addr_t addr = dpaa2_fd_get_addr(fd); u32 fd_length = dpaa2_fd_get_len(fd); struct device *dev = ethsw->dev; struct sk_buff *skb = NULL; - void *fd_vaddr; - - fd_vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, addr); - dma_unmap_page(dev, addr, DPAA2_SWITCH_RX_BUF_SIZE, - DMA_FROM_DEVICE); skb = build_skb(fd_vaddr, DPAA2_SWITCH_RX_BUF_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); @@ -2449,6 +2479,7 @@ static void dpaa2_switch_tx_conf(struct dpaa2_switch_fq *fq, static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, const struct dpaa2_fd *fd) { + dma_addr_t addr = dpaa2_fd_get_addr(fd); struct ethsw_core *ethsw = fq->ethsw; struct ethsw_port_priv *port_priv; struct net_device *netdev; @@ -2456,10 +2487,14 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, struct sk_buff *skb; u16 vlan_tci, vid; int if_id, err; + void *vaddr; + + vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, addr); + dma_unmap_page(ethsw->dev, addr, DPAA2_SWITCH_RX_BUF_SIZE, + DMA_FROM_DEVICE); /* get switch ingress interface ID */ if_id = upper_32_bits(dpaa2_fd_get_flc(fd)) & 0x0000FFFF; - if (if_id >= ethsw->sw_attr.num_ifs) { dev_err(ethsw->dev, "Frame received from unknown interface!\n"); goto err_free_fd; @@ -2475,7 +2510,7 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, } } - skb = dpaa2_switch_build_linear_skb(ethsw, fd); + skb = dpaa2_switch_build_linear_skb(ethsw, fd, vaddr); if (unlikely(!skb)) goto err_free_fd; @@ -2493,7 +2528,8 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, err = __skb_vlan_pop(skb, &vlan_tci); if (err) { dev_info(ethsw->dev, "__skb_vlan_pop() returned %d", err); - goto err_free_fd; + kfree_skb(skb); + return; } } @@ -2508,7 +2544,7 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, return; err_free_fd: - dpaa2_switch_free_fd(ethsw, fd); + free_pages((unsigned long)vaddr, 0); } static void dpaa2_switch_detect_features(struct ethsw_core *ethsw) @@ -3280,7 +3316,6 @@ static void dpaa2_switch_teardown(struct fsl_mc_device *sw_dev) static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev) { - struct ethsw_port_priv *port_priv; struct ethsw_core *ethsw; struct device *dev; int i; @@ -3292,11 +3327,17 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev) dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle); - for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { - port_priv = ethsw->ports[i]; - unregister_netdev(port_priv->netdev); + /* Unregister all the netdevs so that they are brought down and the + * shared NAPI instances gets disabled. + */ + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) + unregister_netdev(ethsw->ports[i]->netdev); + + for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) + netif_napi_del(ðsw->fq[i].napi); + + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) dpaa2_switch_remove_port(ethsw, i); - } kfree(ethsw->fdbs); kfree(ethsw->filter_blocks); diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig index 117038104b69..db5c17a44613 100644 --- a/drivers/net/ethernet/freescale/enetc/Kconfig +++ b/drivers/net/ethernet/freescale/enetc/Kconfig @@ -10,6 +10,7 @@ config FSL_ENETC_CORE config NXP_ENETC_PF_COMMON tristate + select CRC_ITU_T help This module supports common functionality between drivers of different versions of NXP ENETC PF controllers. @@ -70,6 +71,7 @@ config FSL_ENETC_VF select FSL_ENETC_MDIO select PHYLINK select DIMLIB + select CRC_ITU_T help This driver supports NXP ENETC gigabit ethernet controller PCIe virtual function (VF) devices enabled by the ENETC PF driver. diff --git a/drivers/net/ethernet/freescale/enetc/Makefile b/drivers/net/ethernet/freescale/enetc/Makefile index f1c5ad45fd76..10ab6694c314 100644 --- a/drivers/net/ethernet/freescale/enetc/Makefile +++ b/drivers/net/ethernet/freescale/enetc/Makefile @@ -5,13 +5,13 @@ fsl-enetc-core-y := enetc.o enetc_cbdr.o enetc_ethtool.o obj-$(CONFIG_NXP_ENETC_PF_COMMON) += nxp-enetc-pf-common.o nxp-enetc-pf-common-y := enetc_pf_common.o +nxp-enetc-pf-common-$(CONFIG_PCI_IOV) += enetc_msg.o obj-$(CONFIG_NXP_NETC_LIB) += nxp-netc-lib.o nxp-netc-lib-y := ntmp.o obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o fsl-enetc-y := enetc_pf.o -fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o obj-$(CONFIG_NXP_ENETC4) += nxp-enetc4.o diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e691144e8756..04a5dd5ea6c7 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -16,6 +16,7 @@ #include "enetc_hw.h" #include "enetc4_hw.h" +#include "enetc_mailbox.h" #define ENETC_MAC_MAXFRM_SIZE 9600 #define ENETC_MAX_MTU (ENETC_MAC_MAXFRM_SIZE - \ @@ -257,12 +258,6 @@ static inline union enetc_rx_bd *enetc_rxbd_ext(union enetc_rx_bd *rxbd) return ++rxbd; } -struct enetc_msg_swbd { - void *vaddr; - dma_addr_t dma; - int size; -}; - #define ENETC_REV1 0x1 #define ENETC_REV4 0x4 @@ -302,6 +297,8 @@ struct enetc_si; struct enetc_si_ops { int (*get_rss_table)(struct enetc_si *si, u32 *table, int count); int (*set_rss_table)(struct enetc_si *si, const u32 *table, int count); + int (*setup_cbdr)(struct enetc_si *si); + void (*teardown_cbdr)(struct enetc_si *si); }; /* PCI IEP device data */ @@ -490,14 +487,6 @@ struct enetc_ndev_priv { u64 sysclk_freq; /* NETC system clock frequency */ }; -/* Messaging */ - -/* VF-PF set primary MAC address message format */ -struct enetc_msg_cmd_set_primary_mac { - struct enetc_msg_cmd_header header; - struct sockaddr mac; -}; - #define ENETC_CBD(R, i) (&(((struct enetc_cbd *)((R).bd_base))[i])) #define ENETC_CBDR_TIMEOUT 1000 /* usecs */ @@ -550,9 +539,8 @@ void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link); void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv); /* control buffer descriptor ring (CBDR) */ -int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count, - struct enetc_cbdr *cbdr); -void enetc_teardown_cbdr(struct enetc_cbdr *cbdr); +int enetc_setup_cbdr(struct enetc_si *si); +void enetc_teardown_cbdr(struct enetc_si *si); int enetc4_setup_cbdr(struct enetc_si *si); void enetc4_teardown_cbdr(struct enetc_si *si); int enetc_set_mac_flt_entry(struct enetc_si *si, int index, diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 56899f2254aa..4e771f852358 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -17,10 +17,6 @@ #define ENETC_MAC_FILTER_TYPE_ALL (ENETC_MAC_FILTER_TYPE_UC | \ ENETC_MAC_FILTER_TYPE_MC) -struct enetc_mac_addr { - u8 addr[ETH_ALEN]; -}; - static void enetc4_get_port_caps(struct enetc_pf *pf) { struct enetc_hw *hw = &pf->si->hw; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c index a635bfdc30af..e4a393a8a58e 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c @@ -3,10 +3,12 @@ #include "enetc.h" -int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count, - struct enetc_cbdr *cbdr) +int enetc_setup_cbdr(struct enetc_si *si) { - int size = bd_count * sizeof(struct enetc_cbd); + int size = ENETC_CBDR_DEFAULT_SIZE * sizeof(struct enetc_cbd); + struct enetc_cbdr *cbdr = &si->cbd_ring; + struct device *dev = &si->pdev->dev; + struct enetc_hw *hw = &si->hw; cbdr->bd_base = dma_alloc_coherent(dev, size, &cbdr->bd_dma_base, GFP_KERNEL); @@ -23,7 +25,7 @@ int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count, cbdr->next_to_clean = 0; cbdr->next_to_use = 0; cbdr->dma_dev = dev; - cbdr->bd_count = bd_count; + cbdr->bd_count = ENETC_CBDR_DEFAULT_SIZE; cbdr->pir = hw->reg + ENETC_SICBDRPIR; cbdr->cir = hw->reg + ENETC_SICBDRCIR; @@ -46,13 +48,15 @@ int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count, } EXPORT_SYMBOL_GPL(enetc_setup_cbdr); -void enetc_teardown_cbdr(struct enetc_cbdr *cbdr) +void enetc_teardown_cbdr(struct enetc_si *si) { - int size = cbdr->bd_count * sizeof(struct enetc_cbd); + struct enetc_cbdr *cbdr = &si->cbd_ring; + int size; /* disable ring */ enetc_wr_reg(cbdr->mr, 0); + size = cbdr->bd_count * sizeof(struct enetc_cbd); dma_free_coherent(cbdr->dma_dev, size, cbdr->bd_base, cbdr->bd_dma_base); cbdr->bd_base = NULL; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index e58cc81d199d..bf99b65d7598 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -681,7 +681,6 @@ union enetc_rx_bd { #define ENETC_MAC_ADDR_FILT_CNT 8 /* # of supported entries per port */ #define EMETC_MAC_ADDR_FILT_RES 3 /* # of reserved entries at the beginning */ -#define ENETC_MAX_NUM_VFS 2 #define ENETC_CBD_FLAGS_SF BIT(7) /* short format */ #define ENETC_CBD_STATUS_MASK 0xf diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h new file mode 100644 index 000000000000..d9677da38989 --- /dev/null +++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright 2025-2026 NXP + * + * The VSI-to-PSI message generic format: + * + * OFFSET 0 16 24 31 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x0 | CRC16 (big-endian) | CLASS ID | CMD ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x4 | PROTO VER | LEN | RESV | COOKIE| RESV | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x8 | RESV | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0xc | RESV | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x10 | | + * 0x14 | | + * 0x18 | Message Body | + * 0x1c | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0x20 | | + * ~ | Extended Message Body: LEN x 32B | + * 0x3e0 | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Descriptions: + * CRC16 (16-bit): Big endian, CRC16 CCITT-FALSE algorithm, It provides the + * equivalent data integrity check functionality as the FCS for standard + * Ethernet frames. + * + * CLASS ID (8-bit) and CMD ID (8-bit): These are 8-bit fields identifying + * the command class and the class-specific operations supported. For more + * details, please refer to the definitions of the relevant class ID and + * cmd ID in this document. + * + * PROTO VER (8-bit): Supported VSI-PSI command protocol version. Currently + * only support version 0. To be incremented for future protocol extensions. + * + * LEN (8-bit): Extended message body length in increments of 32B. The upper + * limit is given by the physical implementation of the NETC VSI-PSI Messaging + * mechanism that supports message sizes of up to 1024B (including headers), + * that are multiple of 32B. + * + * COOKIE (4-bit): Optional parameter, which, if not 0, indicates that the + * command should be execute asynchronously on PSI side. If COOKIE is not 0 + * and the command cannot be executed instantly on the PSI side (it would + * take longer time to complete), the PSI may enqueue the request in a command + * queue of up to 15 entries per VSI and, later after command execution, the + * PSI returns the COOKIE to VSI as part of an asynchronous notification + * message that indicates the command completion status. If COOKIE is 0 then + * the command is considered as blocking, the PSI will wait for the execution + * of the command to complete before updating the PSIMSGRR[MC] field with the + * corresponding return code. + * + * The PSI-to-VSI message generic format: + * 0 4 8 12 15 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | COOKIE | CLASS CODE | CLASS ID | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * The PSI to VSI message format is mapped to the following PSI message + * registers/fields, depending on use case: + * 1) PSI_RX_control: PSIMSGRR[MC] - for VSI command return code messages + * (blocking requests), and + * 2) PSI_TX_control: PSIMSGSR[MC] - for PSI to VSI notification messages + * (async mode) + * + * Note that for some GET messages, there is no COOKIE field, and the CLASS + * CODE field is expanded to 8 bits. + */ + +#ifndef __ENETC_MAILBOX_H +#define __ENETC_MAILBOX_H + +#include + +#define ENETC_CRC_INIT 0xffff +#define ENETC_MSG_ALIGN 32 +/* s indicates the size of the message */ +#define ENETC_MSG_EXT_BODY_LEN(s) ((s) / ENETC_MSG_ALIGN - 1) +/* l indicates the extended body len (LEN field) of the message */ +#define ENETC_MSG_SIZE(l) (((l) + 1) * ENETC_MSG_ALIGN) + +/* The cookie filed of VSI-to-PSI message */ +#define ENETC_VF_MSG_COOKIE GENMASK(3, 0) +/* The fileds of PSI-to-VSI message, the message is only 16-bit */ +#define ENETC_PF_MSG_COOKIE GENMASK(3, 0) +#define ENETC_PF_MSG_CLASS_CODE GENMASK(7, 4) +/* Extend the class code to 8-bit for GET messages without COOKIE */ +#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0) +#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8) + +enum enetc_msg_class_id { + /* Class ID for PSI-to-VSI messages */ + ENETC_MSG_CLASS_ID_CMD_SUCCESS = 1, + ENETC_MSG_CLASS_ID_PERMISSION_DENY, + ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT, + ENETC_MSG_CLASS_ID_PSI_BUSY, + ENETC_MSG_CLASS_ID_CRC_ERROR, + ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT, + ENETC_MSG_CLASS_ID_INVALID_MSG_LEN, + ENETC_MSG_CLASS_ID_CMD_TIMEOUT, + ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED, + ENETC_MSG_CLASS_ID_CMD_FAIL, /* Generic error code for failure */ + ENETC_MSG_CLASS_ID_CMD_DEFERRED = 0xf, + + /* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */ + ENETC_MSG_CLASS_ID_MAC_FILTER = 0x20, + ENETC_MSG_CLASS_ID_IP_REVISION = 0xf0, +}; + +enum enetc_msg_mac_filter_cmd_id { + ENETC_MSG_SET_PRIMARY_MAC, +}; + +enum enetc_msg_ip_revision_cmd_id { + ENETC_MSG_GET_IP_MN = 1, +}; + +/* Class-specific error return codes of MAC filter */ +enum enetc_mac_filter_class_code { + ENETC_MF_CLASS_CODE_INVALID_MAC, +}; + +struct enetc_msg_swbd { + void *vaddr; + dma_addr_t dma; + int size; +}; + +/* The generic VSI-to-PSI message header */ +struct enetc_msg_header { + __be16 crc16; + u8 class_id; + u8 cmd_id; + u8 proto_ver; + u8 len; + u8 resv0; + u8 cookie; + u8 resv2[8]; +}; + +struct enetc_mac_addr { + u8 addr[ETH_ALEN]; /* Network byte order */ +}; + +/* Message format of class_id 0x20 for exact MAC filter. + * cmd_id 0x0: set primary MAC + * cmd_id 0x1: Add entries to MAC address filter table + * cmd_id 0x2: Delete entries from MAC address filter table + * Note that cmd_id 0x1 and 0x2 are not supported yet. + */ +struct enetc_msg_mac_exact_filter { + struct enetc_msg_header hdr; + u8 mac_cnt; /* No need to set for cmd_id 0 */ + u8 resv[3]; + struct enetc_mac_addr mac[]; +}; + +/* The generic message format applies to the following messages: + * Get IP revision message, class_id 0xf0. + * cmd_id 1: get IP minor revision + */ +struct enetc_msg_generic { + struct enetc_msg_header hdr; + u8 resv[16]; +}; + +#endif diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c index c09635e7eb3d..edc1277bb586 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c @@ -1,7 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2017-2019 NXP */ -#include "enetc_pf.h" +#include "enetc_pf_common.h" + +#define ENETC_PF_MSG_SUCCESS FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \ + ENETC_MSG_CLASS_ID_CMD_SUCCESS) +#define ENETC_PF_MSG_NOTSUPP FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \ + ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT) static void enetc_msg_disable_mr_int(struct enetc_pf *pf) { @@ -35,6 +40,167 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data) return IRQ_HANDLED; } +/* Messaging */ +static bool enetc_msg_check_crc16(void *msg_addr, u32 msg_size) +{ + u32 data_size = msg_size - 2; + u8 *data_buf = msg_addr + 2; + u16 verify_val; + + verify_val = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size); + verify_val = crc_itu_t(verify_val, msg_addr, 2); + if (verify_val) + return false; + + return true; +} + +static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id, + void *vf_msg) +{ + struct enetc_vf_state *vf_state = &pf->vf_state[vf_id]; + struct enetc_msg_mac_exact_filter *msg = vf_msg; + struct device *dev = &pf->si->pdev->dev; + char *addr = msg->mac[0].addr; + + if (!is_valid_ether_addr(addr)) { + dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n", + vf_id); + return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_MAC_FILTER) | + FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, + ENETC_MF_CLASS_CODE_INVALID_MAC)); + } + + mutex_lock(&vf_state->lock); + if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) { + mutex_unlock(&vf_state->lock); + dev_err_ratelimited(dev, + "VF%d attempted to override PF set MAC\n", + vf_id); + return FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED); + } + + enetc_set_si_hw_addr(pf, vf_id + 1, addr); + mutex_unlock(&vf_state->lock); + + return ENETC_PF_MSG_SUCCESS; +} + +static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id, + void *vf_msg) +{ + struct enetc_msg_header *msg_hdr = vf_msg; + + switch (msg_hdr->cmd_id) { + case ENETC_MSG_SET_PRIMARY_MAC: + return enetc_msg_set_vf_primary_mac_addr(pf, vf_id, vf_msg); + default: + return ENETC_PF_MSG_NOTSUPP; + } +} + +static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg) +{ + struct enetc_msg_header *msg_hdr = vf_msg; + + switch (msg_hdr->cmd_id) { + case ENETC_MSG_GET_IP_MN: + return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_IP_REVISION) | + FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, + pf->si->revision)); + default: + return ENETC_PF_MSG_NOTSUPP; + } +} + +static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, + u16 *pf_msg) +{ + struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id]; + struct enetc_msg_header *msg_hdr = msg_swbd->vaddr; + u32 msg_size = ENETC_MSG_SIZE(msg_hdr->len); + struct device *dev = &pf->si->pdev->dev; + u8 *msg; + + if (msg_size > ENETC_DEFAULT_MSG_SIZE) { + dev_err_ratelimited(dev, + "Invalid message size: %u\n", msg_size); + *pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_INVALID_MSG_LEN); + return; + } + + /* To prevent malicious VF from tampering with the original data by + * sending new messages after passing the check, the DMA buffer data + * is copied to the msg buffer before validation. + */ + msg = kzalloc_objs(*msg, msg_size); + if (!msg) { + dev_err_ratelimited(dev, + "Failed to allocate message buffer\n"); + *pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_CMD_FAIL); + return; + } + + memcpy(msg, msg_swbd->vaddr, msg_size); + if (!enetc_msg_check_crc16(msg, msg_size)) { + dev_err_ratelimited(dev, "VSI to PSI Message CRC16 error\n"); + *pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_CRC_ERROR); + + goto free_msg; + } + + /* Default to not supported */ + *pf_msg = ENETC_PF_MSG_NOTSUPP; + msg_hdr = (struct enetc_msg_header *)msg; + + /* Currently, asynchronous actions are not supported */ + if (FIELD_GET(ENETC_VF_MSG_COOKIE, msg_hdr->cookie)) { + dev_err_ratelimited(dev, + "Cookie field is not supported yet\n"); + goto free_msg; + } + + /* Currently only support protocol version 0 */ + if (msg_hdr->proto_ver) { + dev_err_ratelimited(dev, "Unsupported protocol version %u\n", + msg_hdr->proto_ver); + goto free_msg; + } + + /* The new messages are currently only supported on ENETC v4. If v1 + * requires them, the current restriction can be lifted. + */ + if (is_enetc_rev1(pf->si) && + !(msg_hdr->class_id == ENETC_MSG_CLASS_ID_MAC_FILTER && + msg_hdr->cmd_id == ENETC_MSG_SET_PRIMARY_MAC)) { + dev_err_ratelimited(dev, "Unsupported message for ENETC v1\n"); + + goto free_msg; + } + + switch (msg_hdr->class_id) { + case ENETC_MSG_CLASS_ID_MAC_FILTER: + *pf_msg = enetc_msg_handle_mac_filter(pf, vf_id, msg); + break; + case ENETC_MSG_CLASS_ID_IP_REVISION: + *pf_msg = enetc_msg_handle_ip_revision(pf, msg); + break; + default: + dev_err_ratelimited(dev, + "Unsupported message class ID: 0x%x\n", + msg_hdr->class_id); + } + +free_msg: + kfree(msg); +} + static void enetc_msg_task(struct work_struct *work) { struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task); @@ -113,7 +279,7 @@ static void enetc_msg_free_mbx(struct enetc_si *si, int idx) memset(msg, 0, sizeof(*msg)); } -int enetc_msg_psi_init(struct enetc_pf *pf) +static int enetc_msg_psi_init(struct enetc_pf *pf) { struct enetc_si *si = pf->si; int vector, i, err; @@ -153,7 +319,7 @@ int enetc_msg_psi_init(struct enetc_pf *pf) return err; } -void enetc_msg_psi_free(struct enetc_pf *pf) +static void enetc_msg_psi_free(struct enetc_pf *pf) { struct enetc_si *si = pf->si; int i; @@ -172,3 +338,40 @@ void enetc_msg_psi_free(struct enetc_pf *pf) for (i = 0; i < pf->num_vfs; i++) enetc_msg_free_mbx(si, i); } + +int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + struct enetc_si *si = pci_get_drvdata(pdev); + struct enetc_pf *pf = enetc_si_priv(si); + int err; + + if (!num_vfs) { + pci_disable_sriov(pdev); + enetc_msg_psi_free(pf); + pf->num_vfs = 0; + } else { + pf->num_vfs = num_vfs; + + err = enetc_msg_psi_init(pf); + if (err) { + dev_err(&pdev->dev, "enetc_msg_psi_init (%d)\n", err); + goto err_msg_psi; + } + + err = pci_enable_sriov(pdev, num_vfs); + if (err) { + dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err); + goto err_en_sriov; + } + } + + return num_vfs; + +err_en_sriov: + enetc_msg_psi_free(pf); +err_msg_psi: + pf->num_vfs = 0; + + return err; +} +EXPORT_SYMBOL_GPL(enetc_sriov_configure); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index 3206b3daa1a0..2d687bb8c3a0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -480,121 +480,6 @@ static void enetc_configure_port(struct enetc_pf *pf) enetc_port_wr(hw, ENETC_PMR, ENETC_PMR_EN); } -/* Messaging */ -static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf, - int vf_id, void *msg) -{ - struct enetc_vf_state *vf_state = &pf->vf_state[vf_id]; - struct enetc_msg_cmd_set_primary_mac *cmd = msg; - struct device *dev = &pf->si->pdev->dev; - u16 cmd_id = cmd->header.id; - char *addr; - - if (cmd_id != ENETC_MSG_CMD_MNG_ADD) - return ENETC_MSG_CMD_STATUS_FAIL; - - addr = cmd->mac.sa_data; - if (!is_valid_ether_addr(addr)) { - dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n", - vf_id); - return ENETC_MSG_CMD_STATUS_FAIL; - } - - mutex_lock(&vf_state->lock); - if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) { - mutex_unlock(&vf_state->lock); - dev_err_ratelimited(dev, - "VF%d attempted to override PF set MAC\n", - vf_id); - return ENETC_MSG_CMD_STATUS_FAIL; - } - - enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr); - mutex_unlock(&vf_state->lock); - - return ENETC_MSG_CMD_STATUS_OK; -} - -void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, u16 *status) -{ - struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id]; - struct device *dev = &pf->si->pdev->dev; - struct enetc_msg_cmd_header *cmd_hdr; - u16 cmd_type; - u8 *msg; - - msg = kzalloc_objs(*msg, msg_swbd->size); - if (!msg) { - dev_err_ratelimited(dev, - "Failed to allocate message buffer\n"); - *status = ENETC_MSG_CMD_STATUS_FAIL; - return; - } - - /* Currently, only ENETC_MSG_CMD_MNG_MAC command is supported, so - * only sizeof(struct enetc_msg_cmd_set_primary_mac) bytes need to - * be copied. This data already includes the cmd_type field, so it - * can correctly return an error code. - */ - memcpy(msg, msg_swbd->vaddr, - sizeof(struct enetc_msg_cmd_set_primary_mac)); - cmd_hdr = (struct enetc_msg_cmd_header *)msg; - cmd_type = cmd_hdr->type; - - switch (cmd_type) { - case ENETC_MSG_CMD_MNG_MAC: - *status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id, msg); - break; - default: - *status = ENETC_MSG_CMD_STATUS_FAIL; - dev_err_ratelimited(dev, - "command not supported (cmd_type: 0x%x)\n", - cmd_type); - } - - kfree(msg); -} - -#ifdef CONFIG_PCI_IOV -static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs) -{ - struct enetc_si *si = pci_get_drvdata(pdev); - struct enetc_pf *pf = enetc_si_priv(si); - int err; - - if (!num_vfs) { - pci_disable_sriov(pdev); - enetc_msg_psi_free(pf); - pf->num_vfs = 0; - } else { - pf->num_vfs = num_vfs; - - err = enetc_msg_psi_init(pf); - if (err) { - dev_err(&pdev->dev, "enetc_msg_psi_init (%d)\n", err); - goto err_msg_psi; - } - - err = pci_enable_sriov(pdev, num_vfs); - if (err) { - dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err); - goto err_en_sriov; - } - } - - return num_vfs; - -err_en_sriov: - enetc_msg_psi_free(pf); -err_msg_psi: - pf->num_vfs = 0; - - return err; -} -#else -#define enetc_sriov_configure(pdev, num_vfs) (void)0 -#endif - static int enetc_pf_set_features(struct net_device *ndev, netdev_features_t features) { @@ -917,8 +802,7 @@ static struct enetc_si *enetc_psi_create(struct pci_dev *pdev) goto out_pci_remove; } - err = enetc_setup_cbdr(&pdev->dev, &si->hw, ENETC_CBDR_DEFAULT_SIZE, - &si->cbd_ring); + err = enetc_setup_cbdr(si); if (err) goto out_pci_remove; @@ -937,7 +821,7 @@ static struct enetc_si *enetc_psi_create(struct pci_dev *pdev) return si; out_teardown_cbdr: - enetc_teardown_cbdr(&si->cbd_ring); + enetc_teardown_cbdr(si); out_pci_remove: enetc_pci_remove(pdev); out: @@ -948,7 +832,7 @@ static void enetc_psi_destroy(struct pci_dev *pdev) { struct enetc_si *si = pci_get_drvdata(pdev); - enetc_teardown_cbdr(&si->cbd_ring); + enetc_teardown_cbdr(si); enetc_pci_remove(pdev); } @@ -988,18 +872,9 @@ static int enetc_pf_probe(struct pci_dev *pdev, pf->si = si; pf->ops = &enetc_pf_ops; - pf->total_vfs = pci_sriov_get_totalvfs(pdev); - if (pf->total_vfs) { - pf->vf_state = kzalloc_objs(struct enetc_vf_state, - pf->total_vfs); - if (!pf->vf_state) { - err = -ENOMEM; - goto err_alloc_vf_state; - } - - for (int i = 0; i < pf->total_vfs; i++) - mutex_init(&pf->vf_state[i].lock); - } + err = enetc_init_sriov_resources(pf); + if (err) + goto err_init_sriov_resources; err = enetc_setup_mac_addresses(node, pf); if (err) @@ -1077,8 +952,7 @@ static int enetc_pf_probe(struct pci_dev *pdev, free_netdev(ndev); err_alloc_netdev: err_setup_mac_addresses: - kfree(pf->vf_state); -err_alloc_vf_state: +err_init_sriov_resources: enetc_psi_destroy(pdev); err_psi_create: return err; @@ -1105,8 +979,6 @@ static void enetc_pf_remove(struct pci_dev *pdev) enetc_free_si_resources(priv); free_netdev(si->ndev); - kfree(pf->vf_state); - enetc_psi_destroy(pdev); } diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h index 35d484858c7b..285b7e5c48fd 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h @@ -5,8 +5,6 @@ #include #define ENETC_PF_NUM_RINGS 8 -#define ENETC_MAX_NUM_MAC_FLT ((ENETC_MAX_NUM_VFS + 1) * MADDR_TYPE) - #define ENETC_VLAN_HT_SIZE 64 enum enetc_vf_flags { @@ -43,9 +41,9 @@ struct enetc_pf { int total_vfs; /* max number of VFs, set for PF at probe */ struct enetc_vf_state *vf_state; - struct enetc_mac_filter mac_filter[ENETC_MAX_NUM_MAC_FLT]; + struct enetc_mac_filter mac_filter[MADDR_TYPE]; - struct enetc_msg_swbd rxmsg[ENETC_MAX_NUM_VFS]; + struct enetc_msg_swbd *rxmsg; struct work_struct msg_task; char msg_int_name[ENETC_INT_NAME_MAX]; @@ -68,7 +66,3 @@ struct enetc_pf { #define phylink_to_enetc_pf(config) \ container_of((config), struct enetc_pf, phylink_config) - -int enetc_msg_psi_init(struct enetc_pf *pf); -void enetc_msg_psi_free(struct enetc_pf *pf); -void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int mbox_id, u16 *status); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c index 76263b8566bb..6e5d2f869915 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c @@ -7,13 +7,13 @@ #include "enetc_pf_common.h" -static void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, - const u8 *mac_addr) +void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, const u8 *mac_addr) { struct enetc_hw *hw = &pf->si->hw; pf->ops->set_si_primary_mac(hw, si, mac_addr); } +EXPORT_SYMBOL_GPL(enetc_set_si_hw_addr); static void enetc_get_si_hw_addr(struct enetc_pf *pf, int si, u8 *mac_addr) { @@ -435,5 +435,32 @@ int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid) } EXPORT_SYMBOL_GPL(enetc_vlan_rx_del_vid); +int enetc_init_sriov_resources(struct enetc_pf *pf) +{ + struct device *dev = &pf->si->pdev->dev; + + pf->total_vfs = pci_sriov_get_totalvfs(pf->si->pdev); + if (!pf->total_vfs) + return 0; + + pf->rxmsg = devm_kcalloc(dev, pf->total_vfs, + sizeof(struct enetc_msg_swbd), + GFP_KERNEL); + if (!pf->rxmsg) + return -ENOMEM; + + pf->vf_state = devm_kcalloc(dev, pf->total_vfs, + sizeof(struct enetc_vf_state), + GFP_KERNEL); + if (!pf->vf_state) + return -ENOMEM; + + for (int i = 0; i < pf->total_vfs; i++) + mutex_init(&pf->vf_state[i].lock); + + return 0; +} +EXPORT_SYMBOL_GPL(enetc_init_sriov_resources); + MODULE_DESCRIPTION("NXP ENETC PF common functionality driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h index 96d4840a3107..57d2e0ebd2b0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h @@ -3,6 +3,7 @@ #include "enetc_pf.h" +void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, const u8 *mac_addr); int enetc_pf_set_mac_addr(struct net_device *ndev, void *addr); int enetc_setup_mac_addresses(struct device_node *np, struct enetc_pf *pf); void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev, @@ -15,8 +16,18 @@ void enetc_phylink_destroy(struct enetc_ndev_priv *priv); void enetc_set_default_rss_key(struct enetc_pf *pf); int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid); int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid); +int enetc_init_sriov_resources(struct enetc_pf *pf); static inline u16 enetc_get_ip_revision(struct enetc_hw *hw) { return enetc_global_rd(hw, ENETC_G_EIPBRR0) & EIPBRR0_REVISION; } + +#if IS_ENABLED(CONFIG_PCI_IOV) +int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs); +#else +static inline int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + return 0; +} +#endif diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index df8e95cc47d0..9cdb0a4d6baf 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -1,12 +1,35 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2017-2019 NXP */ +#include #include #include "enetc.h" #define ENETC_DRV_NAME_STR "ENETC VF driver" -/* Messaging */ +/* Note: This function should be called after filling the message body, + * because the CRC16 needs to be calculated after all the data has been + * filled. + */ +static void enetc_msg_fill_common_hdr(struct enetc_msg_swbd *msg_swbd, + u8 class_id, u8 cmd_id, u8 proto_ver, + u8 cookie) +{ + struct enetc_msg_header *hdr = msg_swbd->vaddr; + u8 *data_buf = ((u8 *)msg_swbd->vaddr) + 2; /* skip crc16 field */ + u32 data_size = msg_swbd->size - 2; + u16 crc16; + + hdr->class_id = class_id; + hdr->cmd_id = cmd_id; + hdr->len = ENETC_MSG_EXT_BODY_LEN(msg_swbd->size); + hdr->proto_ver = proto_ver; + hdr->cookie = FIELD_PREP(ENETC_VF_MSG_COOKIE, cookie); + + crc16 = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size); + hdr->crc16 = htons(crc16); +} + static void enetc_msg_vsi_write_msg(struct enetc_hw *hw, struct enetc_msg_swbd *msg) { @@ -28,8 +51,9 @@ static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg) static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) { struct device *dev = &si->pdev->dev; - int timeout = 100; u32 vsimsgsr; + u16 pf_msg; + int err; /* The VSI mailbox may be busy if last message was not yet processed * by PSI. So need to check the mailbox status before sending. @@ -48,53 +72,104 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) enetc_msg_dma_free(dev, &si->msg); si->msg = *msg; enetc_msg_vsi_write_msg(&si->hw, msg); - - do { - vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR); - if (!(vsimsgsr & ENETC_VSIMSGSR_MB)) - break; - - usleep_range(1000, 2000); - } while (--timeout); - - if (!timeout) { + err = read_poll_timeout(enetc_rd, vsimsgsr, + !(vsimsgsr & ENETC_VSIMSGSR_MB), + 1000, 200000, false, &si->hw, ENETC_VSIMSGSR); + if (err) { dev_err(dev, "VSI mailbox timeout\n"); - return -ETIMEDOUT; + return err; } /* check for message delivery error */ if (vsimsgsr & ENETC_VSIMSGSR_MS) { - dev_err(dev, "VSI command execute error: %d\n", - ENETC_SIMSGSR_GET_MC(vsimsgsr)); + dev_err(dev, "Transfer error when copying the data\n"); return -EIO; } - return 0; + pf_msg = ENETC_SIMSGSR_GET_MC(vsimsgsr); + /* Check the user-defined completion status. */ + if (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg) != + ENETC_MSG_CLASS_ID_CMD_SUCCESS) { + switch (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg)) { + case ENETC_MSG_CLASS_ID_PERMISSION_DENY: + /* Intentionally returning early to prevent excessive + * error logs due to permission issues. + */ + return -EACCES; + case ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT: + case ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT: + err = -EOPNOTSUPP; + break; + case ENETC_MSG_CLASS_ID_PSI_BUSY: + err = -EBUSY; + break; + case ENETC_MSG_CLASS_ID_CMD_TIMEOUT: + err = -ETIME; + break; + case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN: + case ENETC_MSG_CLASS_ID_MAC_FILTER: + err = -EINVAL; + break; + case ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED: + err = -EPERM; + break; + case ENETC_MSG_CLASS_ID_IP_REVISION: + err = FIELD_GET(ENETC_PF_MSG_CLASS_CODE_U8, pf_msg); + break; + case ENETC_MSG_CLASS_ID_CMD_FAIL: + case ENETC_MSG_CLASS_ID_CRC_ERROR: + case ENETC_MSG_CLASS_ID_CMD_DEFERRED: + default: + err = -EIO; + } + } + + if (err < 0) + dev_err(dev, "Return error code from PSI: 0x%04x\n", pf_msg); + + return err; } static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv, struct sockaddr *saddr) { - struct enetc_msg_cmd_set_primary_mac *cmd; - struct enetc_msg_swbd msg; + struct enetc_msg_mac_exact_filter *msg; + struct enetc_msg_swbd msg_swbd; + u32 msg_size; - msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64); - msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma, - GFP_KERNEL); - if (!msg.vaddr) { - dev_err(priv->dev, "Failed to alloc Tx msg (size: %d)\n", - msg.size); + msg_size = struct_size(msg, mac, 1); + msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN); + msg_swbd.vaddr = dma_alloc_coherent(priv->dev, msg_swbd.size, + &msg_swbd.dma, GFP_KERNEL); + if (!msg_swbd.vaddr) return -ENOMEM; - } - cmd = (struct enetc_msg_cmd_set_primary_mac *)msg.vaddr; - cmd->header.type = ENETC_MSG_CMD_MNG_MAC; - cmd->header.id = ENETC_MSG_CMD_MNG_ADD; - memcpy(&cmd->mac, saddr, sizeof(struct sockaddr)); + msg = (struct enetc_msg_mac_exact_filter *)msg_swbd.vaddr; + memcpy(&msg->mac[0].addr, saddr->sa_data, ETH_ALEN); + enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER, + ENETC_MSG_SET_PRIMARY_MAC, 0, 0); /* send the command and wait */ - return enetc_msg_vsi_send(priv->si, &msg); + return enetc_msg_vsi_send(priv->si, &msg_swbd); +} + +static int enetc_vf_get_ip_minor_revision(struct enetc_si *si) +{ + struct device *dev = &si->pdev->dev; + struct enetc_msg_swbd msg_swbd; + + msg_swbd.size = ALIGN(sizeof(struct enetc_msg_generic), + ENETC_MSG_ALIGN); + msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size, + &msg_swbd.dma, GFP_KERNEL); + if (!msg_swbd.vaddr) + return -ENOMEM; + + enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_IP_REVISION, + ENETC_MSG_GET_IP_MN, 0, 0); + + return enetc_msg_vsi_send(si, &msg_swbd); } static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr) @@ -148,6 +223,27 @@ static const struct net_device_ops enetc_ndev_ops = { .ndo_hwtstamp_set = enetc_hwtstamp_set, }; +static void enetc_vf_get_revision(struct enetc_si *si) +{ + int ip_mn; + + if (is_enetc_rev1(si)) { + si->revision = ENETC_REV_1_0; + return; + } + + ip_mn = enetc_vf_get_ip_minor_revision(si); + if (ip_mn >= 0) { + si->revision = (si->pdev->revision << 8) | ip_mn; + return; + } + + si->revision = ENETC_REV_4_1; + dev_info(&si->pdev->dev, + "Failed to get revision, use compatible revision: 0x%04x\n", + si->revision); +} + static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev, const struct net_device_ops *ndev_ops) { @@ -192,12 +288,15 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev, static const struct enetc_si_ops enetc_vsi_ops = { .get_rss_table = enetc_get_rss_table, .set_rss_table = enetc_set_rss_table, + .setup_cbdr = enetc_setup_cbdr, + .teardown_cbdr = enetc_teardown_cbdr, }; static int enetc_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct enetc_ndev_priv *priv; + struct enetc_msg_swbd msg; struct net_device *ndev; struct enetc_si *si; int err; @@ -207,13 +306,13 @@ static int enetc_vf_probe(struct pci_dev *pdev, return dev_err_probe(&pdev->dev, err, "PCI probing failed\n"); si = pci_get_drvdata(pdev); - si->revision = ENETC_REV_1_0; + enetc_vf_get_revision(si); si->ops = &enetc_vsi_ops; err = enetc_get_driver_data(si); if (err) { dev_err_probe(&pdev->dev, err, "Could not get VF driver data\n"); - goto err_alloc_netdev; + goto err_get_driver_data; } enetc_get_si_caps(si); @@ -231,8 +330,7 @@ static int enetc_vf_probe(struct pci_dev *pdev, enetc_init_si_rings_params(priv); - err = enetc_setup_cbdr(priv->dev, &si->hw, ENETC_CBDR_DEFAULT_SIZE, - &si->cbd_ring); + err = si->ops->setup_cbdr(si); if (err) goto err_setup_cbdr; @@ -268,12 +366,15 @@ static int enetc_vf_probe(struct pci_dev *pdev, err_alloc_msix: enetc_free_si_resources(priv); err_alloc_si_res: - enetc_teardown_cbdr(&si->cbd_ring); + si->ops->teardown_cbdr(si); err_setup_cbdr: si->ndev = NULL; free_netdev(ndev); err_alloc_netdev: +err_get_driver_data: + msg = si->msg; enetc_pci_remove(pdev); + enetc_msg_dma_free(&pdev->dev, &msg); return err; } @@ -290,7 +391,7 @@ static void enetc_vf_remove(struct pci_dev *pdev) enetc_free_msix(priv); enetc_free_si_resources(priv); - enetc_teardown_cbdr(&si->cbd_ring); + si->ops->teardown_cbdr(si); free_netdev(si->ndev); diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c index 92a0f824dae7..c7eb0234c785 100644 --- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c +++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c @@ -261,40 +261,108 @@ static int imx94_link_config(struct netc_blk_ctrl *priv, } static int imx94_enetc_link_config(struct netc_blk_ctrl *priv, - struct device_node *np) + struct device_node *np, + bool *enetc0_en) { int link_id = imx94_enetc_get_link_id(np); if (link_id < 0) return link_id; + if (link_id == IMX94_ENETC0_LINK && of_device_is_available(np)) + *enetc0_en = true; + return imx94_link_config(priv, np, link_id); } +static int imx94_switch_link_config(struct netc_blk_ctrl *priv, + struct device_node *np, + bool *swp2_en) +{ + struct device_node *ports; + u32 port_id; + int err = 0; + + ports = of_get_child_by_name(np, "ethernet-ports"); + if (!ports) + return -ENODEV; + + /* The switch may be owned by a guest OS, in this case, the switch + * node in the host OS will be disabled, but we still hope that the + * host OS could do some configurations for the switch, as the + * netc_blk_ctrl is owned by host OS. So of_device_is_available() + * is not needed here. + */ + for_each_available_child_of_node_scoped(ports, child) { + if (of_property_read_u32(child, "reg", &port_id) < 0) { + err = -ENODEV; + goto end; + } + + switch (port_id) { + case 0 ... 2: /* External ports */ + err = imx94_link_config(priv, child, port_id); + if (err) + goto end; + + if (port_id == 2) + *swp2_en = true; + + break; + case 3: /* CPU port */ + break; + default: + err = -EINVAL; + goto end; + } + } + +end: + of_node_put(ports); + + return err; +} + static int imx94_netcmix_init(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; + bool enetc0_en = false, swp2_en = false; u32 val; int err; for_each_child_of_node_scoped(np, child) { for_each_child_of_node_scoped(child, gchild) { - if (!of_device_is_compatible(gchild, "pci1131,e101")) - continue; - - err = imx94_enetc_link_config(priv, gchild); - if (err) - return err; + if (of_device_is_compatible(gchild, "pci1131,e101")) { + err = imx94_enetc_link_config(priv, gchild, + &enetc0_en); + if (err) + return err; + } else if (of_device_is_compatible(gchild, + "pci1131,eef2")) { + err = imx94_switch_link_config(priv, gchild, + &swp2_en); + if (err) + return err; + } } } - /* ENETC 0 and switch port 2 share the same parallel interface. - * Currently, the switch is not supported, so this interface is - * used by ENETC 0 by default. + if (enetc0_en && swp2_en) { + dev_err(&pdev->dev, + "Cannot enable swp2 and enetc0 at the same time\n"); + return -EINVAL; + } + + /* ENETC 0 and switch port 2 share the same parallel interface, they + * cannot be enabled at the same time. The interface is set for the + * ENETC 0 by default unless the switch port 2 is enabled in the DTS. */ val = netc_reg_read(priv->netcmix, IMX94_EXT_PIN_CONTROL); - val |= MAC2_MAC3_SEL; + if (!swp2_en) + val |= MAC2_MAC3_SEL; + else + val &= ~MAC2_MAC3_SEL; netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val); return 0; @@ -610,6 +678,78 @@ static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv, return 0; } +static int imx94_ierb_enetc_init(struct netc_blk_ctrl *priv, + struct device_node *np, + u32 phy_mask) +{ + int err; + + err = imx94_enetc_update_tid(priv, np); + if (err) + return err; + + return imx94_enetc_mdio_phyaddr_config(priv, np, phy_mask); +} + +static int imx94_switch_mdio_phyaddr_config(struct netc_blk_ctrl *priv, + struct device_node *np, + u32 port_id, u32 phy_mask) +{ + int addr; + + /* The switch has 3 external ports at most */ + if (port_id > 2) + return 0; + + addr = netc_get_phy_addr(np); + if (addr < 0) { + if (addr == -ENODEV) + return 0; + + return addr; + } + + if (phy_mask & BIT(addr)) { + dev_err(&priv->pdev->dev, + "Found same PHY address in EMDIO and switch node\n"); + return -EINVAL; + } + + netc_reg_write(priv->ierb, IERB_LBCR(port_id), + LBCR_MDIO_PHYAD_PRTAD(addr)); + + return 0; +} + +static int imx94_ierb_switch_init(struct netc_blk_ctrl *priv, + struct device_node *np, + u32 phy_mask) +{ + struct device_node *ports; + u32 port_id; + int err = 0; + + ports = of_get_child_by_name(np, "ethernet-ports"); + if (!ports) + return -ENODEV; + + for_each_available_child_of_node_scoped(ports, child) { + err = of_property_read_u32(child, "reg", &port_id); + if (err) + goto end; + + err = imx94_switch_mdio_phyaddr_config(priv, child, + port_id, phy_mask); + if (err) + goto end; + } + +end: + of_node_put(ports); + + return err; +} + static int imx94_ierb_init(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); @@ -625,17 +765,18 @@ static int imx94_ierb_init(struct platform_device *pdev) for_each_child_of_node_scoped(np, child) { for_each_child_of_node_scoped(child, gchild) { - if (!of_device_is_compatible(gchild, "pci1131,e101")) - continue; - - err = imx94_enetc_update_tid(priv, gchild); - if (err) - return err; - - err = imx94_enetc_mdio_phyaddr_config(priv, gchild, - phy_mask); - if (err) - return err; + if (of_device_is_compatible(gchild, "pci1131,e101")) { + err = imx94_ierb_enetc_init(priv, gchild, + phy_mask); + if (err) + return err; + } else if (of_device_is_compatible(gchild, + "pci1131,eef2")) { + err = imx94_ierb_switch_init(priv, gchild, + phy_mask); + if (err) + return err; + } } } diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c index c94a928622fd..0d83f0c9dc6e 100644 --- a/drivers/net/ethernet/freescale/enetc/ntmp.c +++ b/drivers/net/ethernet/freescale/enetc/ntmp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * NETC NTMP (NETC Table Management Protocol) 2.0 Library - * Copyright 2025 NXP + * Copyright 2025-2026 NXP */ #include @@ -21,16 +21,56 @@ /* Define NTMP Table ID */ #define NTMP_MAFT_ID 1 #define NTMP_RSST_ID 3 +#define NTMP_IPFT_ID 13 +#define NTMP_FDBT_ID 15 +#define NTMP_VFT_ID 18 +#define NTMP_ETT_ID 33 +#define NTMP_ECT_ID 39 +#define NTMP_BPT_ID 41 /* Generic Update Actions for most tables */ #define NTMP_GEN_UA_CFGEU BIT(0) #define NTMP_GEN_UA_STSEU BIT(1) +/* Specific Update Actions for some tables */ +#define FDBT_UA_ACTEU BIT(1) +#define ECT_UA_STSEU BIT(0) +#define BPT_UA_BPSEU BIT(1) + +/* Query Action: 0: Full query. 1: Query entry ID, the fields after entry + * ID are not returned. + */ +#define NTMP_QA_ENTRY_ID 1 + #define NTMP_ENTRY_ID_SIZE 4 #define RSST_ENTRY_NUM 64 #define RSST_STSE_DATA_SIZE(n) ((n) * 8) #define RSST_CFGE_DATA_SIZE(n) (n) +u32 ntmp_lookup_free_eid(unsigned long *bitmap, u32 size) +{ + u32 entry_id; + + entry_id = find_first_zero_bit(bitmap, size); + if (entry_id == size) + return NTMP_NULL_ENTRY_ID; + + /* Set the bit once we found it */ + __set_bit(entry_id, bitmap); + + return entry_id; +} +EXPORT_SYMBOL_GPL(ntmp_lookup_free_eid); + +void ntmp_clear_eid_bitmap(unsigned long *bitmap, u32 entry_id) +{ + if (entry_id == NTMP_NULL_ENTRY_ID) + return; + + __clear_bit(entry_id, bitmap); +} +EXPORT_SYMBOL_GPL(ntmp_clear_eid_bitmap); + int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, const struct netc_cbdr_regs *regs) { @@ -134,11 +174,16 @@ static void ntmp_clean_cbdr(struct netc_cbdr *cbdr) static void ntmp_select_and_lock_cbdr(struct ntmp_user *user, struct netc_cbdr **cbdr) { - /* Currently only ENETC is supported, and it has only one command - * BD ring. - */ - *cbdr = &user->ring[0]; + for (int i = 0; i < user->cbdr_num; i++) { + *cbdr = &user->ring[i]; + if (mutex_trylock(&(*cbdr)->ring_lock)) + return; + } + /* If all command BD rings are locked, we need to select one of + * them and wait for it. + */ + *cbdr = &user->ring[raw_smp_processor_id() % user->cbdr_num]; mutex_lock(&(*cbdr)->ring_lock); } @@ -260,6 +305,18 @@ static const char *ntmp_table_name(int tbl_id) return "MAC Address Filter Table"; case NTMP_RSST_ID: return "RSS Table"; + case NTMP_IPFT_ID: + return "Ingress Port Filter Table"; + case NTMP_FDBT_ID: + return "FDB Table"; + case NTMP_VFT_ID: + return "VLAN Filter Table"; + case NTMP_ETT_ID: + return "Egress Treatment Table"; + case NTMP_ECT_ID: + return "Egress Count Table"; + case NTMP_BPT_ID: + return "Buffer Pool Table"; default: return "Unknown Table"; } @@ -496,5 +553,755 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) } EXPORT_SYMBOL_GPL(ntmp_rsst_query_entry); +/** + * ntmp_ipft_add_entry - add an entry into the ingress port filter table + * @user: target ntmp_user struct + * @entry: the entry data, entry->cfge (configuration element data) and + * entry->keye (key element data) are used as input. Since the entry ID + * is assigned by the hardware, so entry->entry_id is a returned value + * for the driver to use, the driver can update/delete/query the entry + * based on the entry_id. + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_ipft_add_entry(struct ntmp_user *user, + struct ipft_entry_data *entry) +{ + struct ipft_resp_query *resp; + struct ipft_req_ua *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*resp); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Note that NTMP_GEN_UA_STSEU is used to reset the statistics of + * the entry. The STSE_DATA is not present in the request data for + * 'Add' operation. + */ + ntmp_fill_crd(&req->crd, user->tbl.ipft_ver, NTMP_QA_ENTRY_ID, + NTMP_GEN_UA_CFGEU | NTMP_GEN_UA_STSEU); + req->ak.keye = entry->keye; + req->cfge = entry->cfge; + + len = NTMP_LEN(sizeof(*req), swcbd.size); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_IPFT_ID, + NTMP_CMD_AQ, NTMP_AM_TERNARY_KEY); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) { + dev_err(user->dev, "Failed to add %s entry, err: %pe\n", + ntmp_table_name(NTMP_IPFT_ID), ERR_PTR(err)); + + goto unlock_cbdr; + } + + resp = (struct ipft_resp_query *)req; + entry->entry_id = le32_to_cpu(resp->entry_id); + +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_ipft_add_entry); + +/** + * ntmp_ipft_delete_entry - delete a specified ingress port filter table entry + * @user: target ntmp_user struct + * @entry_id: the specified ID of the ingress port filter table entry + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id) +{ + u32 req_len = sizeof(struct ipft_req_qd); + + return ntmp_delete_entry_by_id(user, NTMP_IPFT_ID, + user->tbl.ipft_ver, + entry_id, req_len, + NTMP_STATUS_RESP_LEN); +} +EXPORT_SYMBOL_GPL(ntmp_ipft_delete_entry); + +/** + * ntmp_fdbt_add_entry - add an entry into the FDB table + * @user: target ntmp_user struct + * @entry_id: returned value, the entry ID of the new added entry + * @keye: key element data + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id, + const struct fdbt_keye_data *keye, + const struct fdbt_cfge_data *cfge) +{ + struct fdbt_resp_query *resp; + struct fdbt_req_ua *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, NTMP_QA_ENTRY_ID, + NTMP_GEN_UA_CFGEU); + req->ak.exact.keye = *keye; + req->cfge = *cfge; + + len = NTMP_LEN(swcbd.size, sizeof(*resp)); + /* The entry ID is allotted by hardware, so we need to perform + * a query action after the add action to get the entry ID from + * hardware. + */ + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID, + NTMP_CMD_AQ, NTMP_AM_EXACT_KEY); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) { + dev_err(user->dev, "Failed to add %s entry, err: %pe\n", + ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err)); + goto unlock_cbdr; + } + + if (entry_id) { + resp = (struct fdbt_resp_query *)req; + *entry_id = le32_to_cpu(resp->entry_id); + } + +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_add_entry); + +/** + * ntmp_fdbt_update_entry - update the configuration element data of the + * specified FDB entry + * @user: target ntmp_user struct + * @entry_id: the specified entry ID of the FDB table + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id, + const struct fdbt_cfge_data *cfge) +{ + struct fdbt_req_ua *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, NTMP_GEN_UA_CFGEU); + req->ak.eid.entry_id = cpu_to_le32(entry_id); + req->cfge = *cfge; + + /* Request header */ + len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID, + NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, "Failed to update %s entry, err: %pe\n", + ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_update_entry); + +/** + * ntmp_fdbt_delete_entry - delete the specified FDB entry + * @user: target ntmp_user struct + * @entry_id: the specified ID of the FDB entry + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id) +{ + u32 req_len = sizeof(struct fdbt_req_qd); + + return ntmp_delete_entry_by_id(user, NTMP_FDBT_ID, + user->tbl.fdbt_ver, + entry_id, req_len, + NTMP_STATUS_RESP_LEN); +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_entry); + +/** + * ntmp_fdbt_search_port_entry - Search the FDB entry on the specified + * port based on RESUME_ENTRY_ID + * @user: target ntmp_user struct + * @port: the specified switch port ID + * @resume_entry_id: it is both an input and an output. As an input, it + * represents the FDB entry ID to be searched. If it is a NULL entry ID, + * it indicates that the first FDB entry for that port is being searched. + * As an output, it represents the next FDB entry ID to be searched. + * @entry: returned value, the response data of the searched FDB entry + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port, + u32 *resume_entry_id, + struct fdbt_entry_data *entry) +{ + struct fdbt_resp_query *resp; + struct fdbt_req_qd *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0); + req->ak.search.resume_eid = cpu_to_le32(*resume_entry_id); + req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port)); + /* Match CFGE_DATA[PORT_BITMAP] field */ + req->ak.search.cfge_mc = FDBT_CFGE_MC_PORT_BITMAP; + + /* Request header */ + len = NTMP_LEN(swcbd.size, sizeof(*resp)); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID, + NTMP_CMD_QUERY, NTMP_AM_SEARCH); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) { + dev_err(user->dev, + "Failed to search %s entry on port %d, err: %pe\n", + ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err)); + goto unlock_cbdr; + } + + if (!cbd.resp_hdr.num_matched) { + entry->entry_id = NTMP_NULL_ENTRY_ID; + *resume_entry_id = NTMP_NULL_ENTRY_ID; + goto unlock_cbdr; + } + + resp = (struct fdbt_resp_query *)req; + *resume_entry_id = le32_to_cpu(resp->status); + entry->entry_id = le32_to_cpu(resp->entry_id); + entry->keye = resp->keye; + entry->cfge = resp->cfge; + entry->acte = resp->acte; + +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_search_port_entry); + +/** + * ntmp_fdbt_update_activity_element - update the activity element of all + * the dynamic entries in the FDB table. + * @user: target ntmp_user struct + * + * A single activity update management could be used to process all the + * dynamic entries in the FDB table. When hardware process an activity + * update management command for an entry in the FDB table and the entry + * does not have its activity flag set, the activity counter is incremented. + * However, if the activity flag is set, then both the activity flag and + * activity counter are reset. Software can issue the activity update + * management commands at predefined times and the value of the activity + * counter can then be used to estimate the period of how long an FDB + * entry has been inactive. + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_update_activity_element(struct ntmp_user *user) +{ + struct fdbt_req_ua *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, FDBT_UA_ACTEU); + req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID); + req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC); + req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC; + + /* Request header */ + len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN); + /* For activity update, the access method must be search */ + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID, + NTMP_CMD_UPDATE, NTMP_AM_SEARCH); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, + "Failed to update activity of %s, err: %pe\n", + ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_update_activity_element); + +/** + * ntmp_fdbt_delete_ageing_entries - delete all the ageing dynamic entries + * in the FDB table + * @user: target ntmp_user struct + * @act_cnt: the target value of the activity counter + * + * The matching rule is that the activity flag is not set and the activity + * counter is greater than or equal to act_cnt + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_delete_ageing_entries(struct ntmp_user *user, u8 act_cnt) +{ + struct fdbt_req_qd *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + if (act_cnt > FDBT_ACT_CNT) + return -EINVAL; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0); + req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID); + req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC); + req->ak.search.acte = act_cnt; + /* Exact match with ACTE_DATA[ACT_FLAG] AND + * match >= ACTE_DATA[ACT_CNT] + */ + req->ak.search.acte_mc = FDBT_ACTE_MC; + req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC; + + /* Request header */ + len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID, + NTMP_CMD_DELETE, NTMP_AM_SEARCH); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, + "Failed to delete ageing entries of %s, err: %pe\n", + ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_ageing_entries); + +/** + * ntmp_fdbt_delete_port_dynamic_entries - delete all dynamic FDB entries + * associated with the specified switch port + * @user: target ntmp_user struct + * @port: the specified switch port ID + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port) +{ + struct fdbt_req_qd *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0); + req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID); + req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port)); + req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC); + /* Match CFGE_DATA[DYNAMIC & PORT_BITMAP] field */ + req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP; + + /* Request header */ + len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID, + NTMP_CMD_DELETE, NTMP_AM_SEARCH); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, + "Failed to delete dynamic %s entries on port %d, err: %pe\n", + ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_port_dynamic_entries); + +/** + * ntmp_vft_set_entry - add an entry into the VLAN filter table or update + * the configuration element data of the specified VLAN filter entry + * @user: target ntmp_user struct + * @vid: VLAN ID + * @cmd: command type, NTMP_CMD_ADD or NTMP_CMD_UPDATE + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +static int ntmp_vft_set_entry(struct ntmp_user *user, u16 vid, int cmd, + const struct vft_cfge_data *cfge) +{ + struct netc_swcbd swcbd; + struct vft_req_ua *req; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + if (cmd != NTMP_CMD_ADD && cmd != NTMP_CMD_UPDATE) + return -EINVAL; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, NTMP_GEN_UA_CFGEU); + req->ak.exact.vid = cpu_to_le16(vid); + req->cfge = *cfge; + + /* Request header */ + len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID, + cmd, NTMP_AM_EXACT_KEY); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + ntmp_unlock_cbdr(cbdr); + + return err; +} + +/** + * ntmp_vft_add_entry - add an entry into the VLAN filter table + * @user: target ntmp_user struct + * @vid: VLAN ID + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid, + const struct vft_cfge_data *cfge) +{ + int err; + + err = ntmp_vft_set_entry(user, vid, NTMP_CMD_ADD, cfge); + if (err) + dev_err(user->dev, + "Failed to add %s entry, vid: %u, err: %pe\n", + ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err)); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_vft_add_entry); + +/** + * ntmp_vft_update_entry - update the configuration element data of the + * specified VLAN filter entry + * @user: target ntmp_user struct + * @vid: VLAN ID + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_vft_update_entry(struct ntmp_user *user, u16 vid, + const struct vft_cfge_data *cfge) +{ + int err; + + err = ntmp_vft_set_entry(user, vid, NTMP_CMD_UPDATE, cfge); + if (err) + dev_err(user->dev, + "Failed to update %s entry, vid: %u, err: %pe\n", + ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err)); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_vft_update_entry); + +/** + * ntmp_vft_delete_entry - delete the VLAN filter entry based on the + * specified VLAN ID + * @user: target ntmp_user struct + * @vid: VLAN ID + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid) +{ + struct netc_swcbd swcbd; + struct vft_req_qd *req; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + u32 len; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, 0); + req->ak.exact.vid = cpu_to_le16(vid); + + /* Request header */ + len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN); + ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID, + NTMP_CMD_DELETE, NTMP_AM_EXACT_KEY); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, + "Failed to delete %s entry, vid: %u, err: %pe\n", + ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_vft_delete_entry); + +/** + * ntmp_ett_set_entry - add a new entry to the egress treatment table or + * update the configuration element data of the specified entry + * @user: target ntmp_user struct + * @entry_id: entry ID + * @cmd: command type, NTMP_CMD_ADD or NTMP_CMD_UPDATE + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +static int ntmp_ett_set_entry(struct ntmp_user *user, u32 entry_id, + int cmd, const struct ett_cfge_data *cfge) +{ + struct netc_swcbd swcbd; + struct ett_req_ua *req; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + int err; + + if (cmd != NTMP_CMD_ADD && cmd != NTMP_CMD_UPDATE) + return -EINVAL; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd_eid(&req->rbe, user->tbl.ett_ver, 0, + NTMP_GEN_UA_CFGEU, entry_id); + req->cfge = *cfge; + + /* Request header */ + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), + NTMP_ETT_ID, cmd, NTMP_AM_ENTRY_ID); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + ntmp_unlock_cbdr(cbdr); + + return err; +} + +/** + * ntmp_ett_add_entry - add a new entry to the egress treatment table + * @user: target ntmp_user struct + * @entry_id: entry ID + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_ett_add_entry(struct ntmp_user *user, u32 entry_id, + const struct ett_cfge_data *cfge) +{ + int err; + + err = ntmp_ett_set_entry(user, entry_id, NTMP_CMD_ADD, cfge); + if (err) + dev_err(user->dev, "Failed to add %s entry 0x%x, err: %pe\n", + ntmp_table_name(NTMP_ETT_ID), entry_id, ERR_PTR(err)); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_ett_add_entry); + +/** + * ntmp_ett_update_entry - update the configuration element data of the + * specified entry + * @user: target ntmp_user struct + * @entry_id: entry ID + * @cfge: configuration element data + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_ett_update_entry(struct ntmp_user *user, u32 entry_id, + const struct ett_cfge_data *cfge) +{ + int err; + + err = ntmp_ett_set_entry(user, entry_id, NTMP_CMD_UPDATE, cfge); + if (err) + dev_err(user->dev, + "Failed to update %s entry 0x%x, err: %pe\n", + ntmp_table_name(NTMP_ETT_ID), entry_id, ERR_PTR(err)); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_ett_update_entry); + +/** + * ntmp_ett_delete_entry - delete the specified egress treatment table entry + * @user: target ntmp_user struct + * @entry_id: entry ID + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id) +{ + return ntmp_delete_entry_by_id(user, NTMP_ETT_ID, user->tbl.ett_ver, + entry_id, NTMP_EID_REQ_LEN, 0); +} +EXPORT_SYMBOL_GPL(ntmp_ett_delete_entry); + +/** + * ntmp_ect_update_entry - reset the statistics element data of the + * specified egress counter table entry + * @user: target ntmp_user struct + * @entry_id: entry ID + * + * Return: 0 on success, otherwise a negative error code + */ +int ntmp_ect_update_entry(struct ntmp_user *user, u32 entry_id) +{ + struct ntmp_req_by_eid *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Request data */ + ntmp_fill_crd_eid(req, user->tbl.ect_ver, 0, ECT_UA_STSEU, entry_id); + + /* Request header */ + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), + NTMP_ECT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, + "Failed to update %s entry 0x%x, err: %pe\n", + ntmp_table_name(NTMP_ECT_ID), entry_id, ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_ect_update_entry); + +int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id, + const struct bpt_cfge_data *cfge) +{ + struct bpt_req_update *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; + union netc_cbd cbd; + int err; + + swcbd.size = sizeof(*req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); + if (err) + return err; + + /* Note that BPT_UA_BPSEU is used to update the BPSE_DATA of the entry, + * which is maintained by the hardware. The BPSE_DATA is not present in + * the request data for 'Update' operation. + */ + ntmp_fill_crd_eid(&req->rbe, user->tbl.bpt_ver, 0, + NTMP_GEN_UA_CFGEU | BPT_UA_BPSEU, entry_id); + req->cfge = *cfge; + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), + NTMP_BPT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); + if (err) + dev_err(user->dev, + "Failed to update %s entry 0x%x, err: %pe\n", + ntmp_table_name(NTMP_BPT_ID), entry_id, ERR_PTR(err)); + + ntmp_unlock_cbdr(cbdr); + + return err; +} +EXPORT_SYMBOL_GPL(ntmp_bpt_update_entry); + MODULE_DESCRIPTION("NXP NETC Library"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h index f8dff3ba2c28..531ea7ddd145 100644 --- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h +++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* * NTMP table request and response data buffer formats - * Copyright 2025 NXP + * Copyright 2025-2026 NXP */ #ifndef __NTMP_PRIVATE_H @@ -11,6 +11,7 @@ #include #define NTMP_EID_REQ_LEN 8 +#define NTMP_STATUS_RESP_LEN 4 #define NETC_CBDR_BD_NUM 256 #define NETC_CBDRCIR_INDEX GENMASK(9, 0) #define NETC_CBDRCIR_SBE BIT(31) @@ -30,6 +31,7 @@ union netc_cbd { #define NTMP_CMD_QUERY BIT(2) #define NTMP_CMD_ADD BIT(3) #define NTMP_CMD_QU (NTMP_CMD_QUERY | NTMP_CMD_UPDATE) +#define NTMP_CMD_AQ (NTMP_CMD_ADD | NTMP_CMD_QUERY) u8 access_method; #define NTMP_ACCESS_METHOD GENMASK(7, 4) #define NTMP_AM_ENTRY_ID 0 @@ -97,4 +99,136 @@ struct rsst_req_update { u8 groups[]; }; +/* Ingress Port Filter Table Response Data Buffer Format of Query action */ +struct ipft_resp_query { + __le32 status; + __le32 entry_id; + struct ipft_keye_data keye; + __le64 match_count; /* STSE_DATA */ + struct ipft_cfge_data cfge; +} __packed; + +struct ipft_ak_eid { + __le32 entry_id; + __le32 resv[52]; +}; + +union ipft_access_key { + struct ipft_ak_eid eid; + struct ipft_keye_data keye; +}; + +/* Ingress Port Filter Table Request Data Buffer Format of Update and + * Add actions + */ +struct ipft_req_ua { + struct ntmp_cmn_req_data crd; + union ipft_access_key ak; + struct ipft_cfge_data cfge; +}; + +/* Ingress Port Filter Table Request Data Buffer Format of Query and + * Delete actions + */ +struct ipft_req_qd { + struct ntmp_req_by_eid rbe; + __le32 resv[52]; +}; + +/* Access Key Format of FDB Table */ +struct fdbt_ak_eid { + __le32 entry_id; + __le32 resv[7]; +}; + +struct fdbt_ak_exact { + struct fdbt_keye_data keye; + __le32 resv[5]; +}; + +struct fdbt_ak_search { + __le32 resume_eid; + struct fdbt_keye_data keye; + struct fdbt_cfge_data cfge; + u8 acte; + u8 keye_mc; +#define FDBT_KEYE_MAC GENMASK(1, 0) + u8 cfge_mc; +#define FDBT_CFGE_MC GENMASK(2, 0) +#define FDBT_CFGE_MC_ANY 0 +#define FDBT_CFGE_MC_DYNAMIC 1 +#define FDBT_CFGE_MC_PORT_BITMAP 2 +#define FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP 3 + u8 acte_mc; +#define FDBT_ACTE_MC BIT(0) +}; + +union fdbt_access_key { + struct fdbt_ak_eid eid; + struct fdbt_ak_exact exact; + struct fdbt_ak_search search; +}; + +/* FDB Table Request Data Buffer Format of Update and Add actions */ +struct fdbt_req_ua { + struct ntmp_cmn_req_data crd; + union fdbt_access_key ak; + struct fdbt_cfge_data cfge; +}; + +/* FDB Table Request Data Buffer Format of Query and Delete actions */ +struct fdbt_req_qd { + struct ntmp_cmn_req_data crd; + union fdbt_access_key ak; +}; + +/* FDB Table Response Data Buffer Format of Query action */ +struct fdbt_resp_query { + __le32 status; + __le32 entry_id; + struct fdbt_keye_data keye; + struct fdbt_cfge_data cfge; + u8 acte; + u8 resv[3]; +}; + +/* Access Key Format of VLAN Filter Table */ +struct vft_ak_exact { + __le16 vid; /* bit0~11: VLAN ID, other bits are reserved */ + __le16 resv; +}; + +union vft_access_key { + __le32 entry_id; /* entry_id match */ + struct vft_ak_exact exact; + __le32 resume_entry_id; /* search */ +}; + +/* VLAN Filter Table Request Data Buffer Format of Update and Add actions */ +struct vft_req_ua { + struct ntmp_cmn_req_data crd; + union vft_access_key ak; + struct vft_cfge_data cfge; +}; + +/* VLAN Filter Table Request Data Buffer Format of Query and Delete actions */ +struct vft_req_qd { + struct ntmp_cmn_req_data crd; + union vft_access_key ak; +}; + +/* Egress Treatment Table Request Data Buffer Format of Update and Add + * actions + */ +struct ett_req_ua { + struct ntmp_req_by_eid rbe; + struct ett_cfge_data cfge; +}; + +/* Buffer Pool Table Request Data Buffer Format of Update action */ +struct bpt_req_update { + struct ntmp_req_by_eid rbe; + struct bpt_cfge_data cfge; +}; + #endif diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 6ebde65d7f1b..ced4dbf8cd90 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -218,7 +218,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); */ #if defined(CONFIG_NETtel) #define FEC_FLASHMAC 0xf0006006 -#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) +#elif defined(CONFIG_SCALES) #define FEC_FLASHMAC 0xf0006000 #elif defined(CONFIG_CANCam) #define FEC_FLASHMAC 0xf0020000 diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 3fc29afc9854..5188551add0b 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -692,6 +692,7 @@ static void mpc52xx_fec_start(struct net_device *dev) /** * mpc52xx_fec_stop * @dev: network device + * @may_sleep: whether the caller can sleep (e.g., to wait for tx drain) * * stop all activity on fec and empty dma buffers */ diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c index d48a1c4d0431..e25111bcbf16 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c @@ -150,4 +150,5 @@ struct platform_driver mpc52xx_fec_mdio_driver = { /* let fec driver call it, since this has to be registered before it */ EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver); +MODULE_DESCRIPTION("MPC52xx FEC MDIO bus driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index dc2213b5ce24..7cc22916852f 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -4,7 +4,7 @@ * Copyright (C) 2015-2024 Google LLC */ -#include +#include #include "gve.h" #include "gve_adminq.h" #include "gve_dqo.h" @@ -171,7 +171,7 @@ gve_get_ethtool_stats(struct net_device *netdev, int ring; int i, j; - ASSERT_RTNL(); + netdev_assert_locked(netdev); priv = netdev_priv(netdev); num_tx_queues = gve_num_tx_queues(priv); @@ -983,6 +983,8 @@ const struct ethtool_ops gve_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS, .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | ETHTOOL_RING_USE_RX_BUF_LEN, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM, .get_drvinfo = gve_get_drvinfo, .get_strings = gve_get_strings, .get_sset_count = gve_get_sset_count, diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 424d973c97f2..e4d78ae52daf 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -2894,7 +2894,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto abort_with_wq; if (!gve_is_gqi(priv) && !gve_is_qpl(priv)) - dev->netmem_tx = true; + dev->netmem_tx = NETMEM_TX_DMA; err = register_netdev(dev); if (err) @@ -2967,9 +2967,9 @@ static void gve_shutdown(struct pci_dev *pdev) rtnl_unlock(); } -#ifdef CONFIG_PM -static int gve_suspend(struct pci_dev *pdev, pm_message_t state) +static int gve_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct gve_priv *priv = netdev_priv(netdev); bool was_up = netif_running(priv->dev); @@ -2990,8 +2990,9 @@ static int gve_suspend(struct pci_dev *pdev, pm_message_t state) return 0; } -static int gve_resume(struct pci_dev *pdev) +static int gve_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct gve_priv *priv = netdev_priv(netdev); int err; @@ -3004,7 +3005,8 @@ static int gve_resume(struct pci_dev *pdev) rtnl_unlock(); return err; } -#endif /* CONFIG_PM */ + +static DEFINE_SIMPLE_DEV_PM_OPS(gve_pm_ops, gve_suspend, gve_resume); static const struct pci_device_id gve_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_GOOGLE, PCI_DEV_ID_GVNIC) }, @@ -3017,10 +3019,7 @@ static struct pci_driver gve_driver = { .probe = gve_probe, .remove = gve_remove, .shutdown = gve_shutdown, -#ifdef CONFIG_PM - .suspend = gve_suspend, - .resume = gve_resume, -#endif + .driver.pm = pm_sleep_ptr(&gve_pm_ops), }; module_pci_driver(gve_driver); diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c index f721e9893804..66792e2b0fad 100644 --- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c +++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c @@ -492,7 +492,7 @@ static void hbg_shutdown(struct pci_dev *pdev) } static const struct pci_device_id hbg_pci_tbl[] = { - {PCI_VDEVICE(HUAWEI, 0x3730), 0}, + { PCI_VDEVICE(HUAWEI, 0x3730) }, { } }; MODULE_DEVICE_TABLE(pci, hbg_pci_tbl); diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile index e8af26da1fc1..5785d4c5709e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/Makefile +++ b/drivers/net/ethernet/hisilicon/hns3/Makefile @@ -24,5 +24,6 @@ hclgevf-objs = hns3vf/hclgevf_main.o hns3vf/hclgevf_mbx.o hns3vf/hclgevf_devlin obj-$(CONFIG_HNS3_HCLGE) += hclge.o hclge-common.o hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hclge_regs.o \ hns3pf/hclge_mbx.o hns3pf/hclge_err.o hns3pf/hclge_debugfs.o hns3pf/hclge_ptp.o hns3pf/hclge_devlink.o \ + hns3pf/hclge_fd.o hclge-$(CONFIG_HNS3_DCB) += hns3pf/hclge_dcb.o diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index d7c3df1958f3..a8798eecd9fb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -331,6 +331,7 @@ enum hnae3_dbg_cmd { HNAE3_DBG_CMD_TX_QUEUE_INFO, HNAE3_DBG_CMD_FD_TCAM, HNAE3_DBG_CMD_FD_COUNTER, + HNAE3_DBG_CMD_FD_RULE, HNAE3_DBG_CMD_MAC_TNL_STATUS, HNAE3_DBG_CMD_SERV_INFO, HNAE3_DBG_CMD_UMV_INFO, @@ -778,7 +779,7 @@ struct hnae3_ae_ops { u32 len, u8 *data); bool (*get_cmdq_stat)(struct hnae3_handle *handle); int (*add_cls_flower)(struct hnae3_handle *handle, - struct flow_cls_offload *cls_flower, int tc); + struct flow_cls_offload *cls_flower); int (*del_cls_flower)(struct hnae3_handle *handle, struct flow_cls_offload *cls_flower); bool (*cls_flower_active)(struct hnae3_handle *handle); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 4cce4f4ba6b0..1347edac7699 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -273,6 +273,12 @@ static struct hns3_dbg_cmd_info hns3_dbg_cmd[] = { .dentry = HNS3_DBG_DENTRY_FD, .init = hns3_dbg_common_init_t2, }, + { + .name = "fd_rule", + .cmd = HNAE3_DBG_CMD_FD_RULE, + .dentry = HNS3_DBG_DENTRY_FD, + .init = hns3_dbg_common_init_t2, + }, { .name = "service_task_info", .cmd = HNAE3_DBG_CMD_SERV_INFO, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index a3206c97923e..6ecb32e28e79 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -86,25 +86,39 @@ module_param(page_pool_enabled, bool, 0400); * Class, Class Mask, private data (not used) } */ static const struct pci_device_id hns3_pci_tbl[] = { - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, + { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), + .driver_data = 0, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), + .driver_data = 0, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), + .driver_data = 0, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, /* required last entry */ - {0,} + { } }; MODULE_DEVICE_TABLE(pci, hns3_pci_tbl); @@ -2664,13 +2678,12 @@ static int hns3_setup_tc(struct net_device *netdev, void *type_data) static int hns3_setup_tc_cls_flower(struct hns3_nic_priv *priv, struct flow_cls_offload *flow) { - int tc = tc_classid_to_hwtc(priv->netdev, flow->classid); struct hnae3_handle *h = hns3_get_handle(priv->netdev); switch (flow->command) { case FLOW_CLS_REPLACE: if (h->ae_algo->ops->add_cls_flower) - return h->ae_algo->ops->add_cls_flower(h, flow, tc); + return h->ae_algo->ops->add_cls_flower(h, flow); break; case FLOW_CLS_DESTROY: if (h->ae_algo->ops->del_cls_flower) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 3dab3a271aa6..9a4e29bfa166 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -2121,6 +2121,157 @@ static int hclge_dbg_dump_fd_counter(struct seq_file *s, void *data) return 0; } +__printf(4, 5) +static void hclge_fd_dump_item(struct seq_file *s, const char *name, + const char *suffix, const char *fmt, ...) +{ + va_list args; + + seq_printf(s, "\t\t%s%s: ", name, suffix); + + va_start(args, fmt); + seq_vprintf(s, fmt, args); + va_end(args); + + seq_putc(s, '\n'); +} + +#define hclge_fd_dump_any(s, rule, type, name, fmt, key, mask) \ +do { \ + typeof(s) _s = (s); \ + typeof(name) _name = (name); \ + typeof(fmt) _fmt = (fmt); \ + \ + if (!((rule)->unused_tuple & BIT(type))) { \ + hclge_fd_dump_item(_s, _name, "", _fmt, key); \ + hclge_fd_dump_item(_s, _name, "_mask", _fmt, mask); \ + } \ +} while (0) +#define hclge_fd_dump_u32 hclge_fd_dump_any +#define hclge_fd_dump_ptr hclge_fd_dump_any + +static void hclge_fd_dump_ip(struct seq_file *s, + struct hclge_fd_rule *rule, + u32 type, const char *name, + const u32 *ip, const u32 *mask) +{ + __be32 be_mask[IPV6_ADDR_WORDS]; + __be32 be_ip[IPV6_ADDR_WORDS]; + + if (rule->unused_tuple & BIT(type) || + rule->unused_tuple & BIT(INNER_ETH_TYPE)) + return; + + ipv6_addr_cpu_to_be32(be_ip, ip); + ipv6_addr_cpu_to_be32(be_mask, mask); + + if (rule->tuples.ether_proto == ETH_P_IPV6) + hclge_fd_dump_ptr(s, rule, type, name, "%pI6", + be_ip, be_mask); + else + hclge_fd_dump_ptr(s, rule, type, name, "%pI4", + &be_ip[IPV4_INDEX], &be_mask[IPV4_INDEX]); +} + +static void hclge_dbg_dump_fd_tuples(struct seq_file *s, + struct hclge_fd_rule *rule) +{ + seq_puts(s, "\trule tuples:\n"); + + hclge_fd_dump_ptr(s, rule, INNER_DST_MAC, "dst_mac", "%pM", + rule->tuples.dst_mac, rule->tuples_mask.dst_mac); + hclge_fd_dump_ptr(s, rule, INNER_SRC_MAC, "src_mac", "%pM", + rule->tuples.src_mac, rule->tuples_mask.src_mac); + hclge_fd_dump_u32(s, rule, INNER_VLAN_TAG_FST, "vlan_tag", "0x%04x", + rule->tuples.vlan_tag1, rule->tuples_mask.vlan_tag1); + hclge_fd_dump_u32(s, rule, INNER_ETH_TYPE, "ether_proto", "0x%04x", + rule->tuples.ether_proto, + rule->tuples_mask.ether_proto); + hclge_fd_dump_u32(s, rule, INNER_L2_RSV, "l2_user_def", "0x%04x", + rule->tuples.l2_user_def, + rule->tuples_mask.l2_user_def); + hclge_fd_dump_ip(s, rule, INNER_SRC_IP, "src_ip", + rule->tuples.src_ip, rule->tuples_mask.src_ip); + hclge_fd_dump_ip(s, rule, INNER_DST_IP, "dst_ip", + rule->tuples.dst_ip, rule->tuples_mask.dst_ip); + hclge_fd_dump_u32(s, rule, INNER_IP_TOS, "ip_tos", "0x%02x", + rule->tuples.ip_tos, rule->tuples_mask.ip_tos); + hclge_fd_dump_u32(s, rule, INNER_IP_PROTO, "ip_proto", "0x%02x", + rule->tuples.ip_proto, rule->tuples_mask.ip_proto); + hclge_fd_dump_u32(s, rule, INNER_L3_RSV, "l3_user_def", "0x%04x", + rule->tuples.l3_user_def, + rule->tuples_mask.l3_user_def); + hclge_fd_dump_u32(s, rule, INNER_SRC_PORT, "src_port", "0x%04x", + rule->tuples.src_port, rule->tuples_mask.src_port); + hclge_fd_dump_u32(s, rule, INNER_DST_PORT, "dst_port", "0x%04x", + rule->tuples.dst_port, rule->tuples_mask.dst_port); + hclge_fd_dump_u32(s, rule, INNER_L4_RSV, "l4_user_def", "0x%08x", + rule->tuples.l4_user_def, + rule->tuples_mask.l4_user_def); + hclge_fd_dump_u32(s, rule, OUTER_TUN_VNI, "outer_tun_vni", "0x%06x", + rule->tuples.outer_tun_vni, + rule->tuples_mask.outer_tun_vni); +} + +static void hclge_dbg_dump_fd_action(struct seq_file *s, + struct hclge_fd_rule *rule) +{ + static const char * const action_str[] = { + [HCLGE_FD_ACTION_SELECT_QUEUE] = "select_queue", + [HCLGE_FD_ACTION_DROP_PACKET] = "drop_packet", + [HCLGE_FD_ACTION_SELECT_TC] = "select_tc", + }; + + if (rule->action <= HCLGE_FD_ACTION_SELECT_TC) + seq_printf(s, "\taction: %s\n", action_str[rule->action]); + else + seq_printf(s, "\taction: %s\n", "unknown"); + + if (rule->action == HCLGE_FD_ACTION_SELECT_QUEUE) + seq_printf(s, "\tqueue_id: %u\n", rule->queue_id); + else if (rule->action == HCLGE_FD_ACTION_SELECT_TC) + seq_printf(s, "\ttc: %u\n", rule->cls_flower.tc); +} + +static void hclge_dbg_dump_fd_type(struct hclge_dev *hdev, struct seq_file *s) +{ + static const char * const type_str[] = { + [HCLGE_FD_RULE_NONE] = "none", + [HCLGE_FD_ARFS_ACTIVE] = "arfs", + [HCLGE_FD_EP_ACTIVE] = "ep", + [HCLGE_FD_TC_FLOWER_ACTIVE] = "tc_flow" + }; + + if (hdev->fd_active_type <= HCLGE_FD_TC_FLOWER_ACTIVE) + seq_printf(s, "fd type: %s\n", type_str[hdev->fd_active_type]); + else + seq_printf(s, "fd type: %s\n", "unknown"); +} + +static int hclge_dbg_dump_fd_rule(struct seq_file *s, void *data) +{ + struct hclge_dev *hdev = hclge_seq_file_to_hdev(s); + struct hclge_fd_rule *rule; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + spin_lock_bh(&hdev->fd_rule_lock); + hclge_dbg_dump_fd_type(hdev, s); + hlist_for_each_entry(rule, &hdev->fd_rule_list, rule_node) { + if (rule->state != HCLGE_FD_ACTIVE) + continue; + + seq_printf(s, "location: %u\n", rule->location); + seq_printf(s, "vport_id: %u\n", rule->vf_id); + hclge_dbg_dump_fd_action(s, rule); + hclge_dbg_dump_fd_tuples(s, rule); + } + spin_unlock_bh(&hdev->fd_rule_lock); + + return 0; +} + static const struct hclge_dbg_status_dfx_info hclge_dbg_rst_info[] = { {HCLGE_MISC_VECTOR_REG_BASE, "vector0 interrupt enable status"}, {HCLGE_MISC_RESET_STS_REG, "reset interrupt source"}, @@ -2913,6 +3064,10 @@ static const struct hclge_dbg_func hclge_dbg_cmd_func[] = { .cmd = HNAE3_DBG_CMD_FD_TCAM, .dbg_read_func = hclge_dbg_dump_fd_tcam, }, + { + .cmd = HNAE3_DBG_CMD_FD_RULE, + .dbg_read_func = hclge_dbg_dump_fd_rule, + }, { .cmd = HNAE3_DBG_CMD_MAC_TNL_STATUS, .dbg_read_func = hclge_dbg_dump_mac_tnl_status, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c new file mode 100644 index 000000000000..2fccb0a870b5 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c @@ -0,0 +1,2593 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2026 Hisilicon Limited. + +#include +#include +#include +#include "hclge_fd.h" +#include "hclge_main.h" + +static const struct key_info meta_data_key_info[] = { + { PACKET_TYPE_ID, 6 }, + { IP_FRAGEMENT, 1 }, + { ROCE_TYPE, 1 }, + { NEXT_KEY, 5 }, + { VLAN_NUMBER, 2 }, + { SRC_VPORT, 12 }, + { DST_VPORT, 12 }, + { TUNNEL_PACKET, 1 }, +}; + +static const struct key_info tuple_key_info[] = { + { OUTER_DST_MAC, 48, KEY_OPT_MAC, -1, -1 }, + { OUTER_SRC_MAC, 48, KEY_OPT_MAC, -1, -1 }, + { OUTER_VLAN_TAG_FST, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_ETH_TYPE, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_L2_RSV, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_IP_TOS, 8, KEY_OPT_U8, -1, -1 }, + { OUTER_IP_PROTO, 8, KEY_OPT_U8, -1, -1 }, + { OUTER_SRC_IP, 32, KEY_OPT_IP, -1, -1 }, + { OUTER_DST_IP, 32, KEY_OPT_IP, -1, -1 }, + { OUTER_L3_RSV, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 }, + { OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 }, + { OUTER_TUN_VNI, 24, KEY_OPT_VNI, + offsetof(struct hclge_fd_rule, tuples.outer_tun_vni), + offsetof(struct hclge_fd_rule, tuples_mask.outer_tun_vni) }, + { OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 }, + { INNER_DST_MAC, 48, KEY_OPT_MAC, + offsetof(struct hclge_fd_rule, tuples.dst_mac), + offsetof(struct hclge_fd_rule, tuples_mask.dst_mac) }, + { INNER_SRC_MAC, 48, KEY_OPT_MAC, + offsetof(struct hclge_fd_rule, tuples.src_mac), + offsetof(struct hclge_fd_rule, tuples_mask.src_mac) }, + { INNER_VLAN_TAG_FST, 16, KEY_OPT_LE16, + offsetof(struct hclge_fd_rule, tuples.vlan_tag1), + offsetof(struct hclge_fd_rule, tuples_mask.vlan_tag1) }, + { INNER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 }, + { INNER_ETH_TYPE, 16, KEY_OPT_LE16, + offsetof(struct hclge_fd_rule, tuples.ether_proto), + offsetof(struct hclge_fd_rule, tuples_mask.ether_proto) }, + { INNER_L2_RSV, 16, KEY_OPT_LE16, + offsetof(struct hclge_fd_rule, tuples.l2_user_def), + offsetof(struct hclge_fd_rule, tuples_mask.l2_user_def) }, + { INNER_IP_TOS, 8, KEY_OPT_U8, + offsetof(struct hclge_fd_rule, tuples.ip_tos), + offsetof(struct hclge_fd_rule, tuples_mask.ip_tos) }, + { INNER_IP_PROTO, 8, KEY_OPT_U8, + offsetof(struct hclge_fd_rule, tuples.ip_proto), + offsetof(struct hclge_fd_rule, tuples_mask.ip_proto) }, + { INNER_SRC_IP, 32, KEY_OPT_IP, + offsetof(struct hclge_fd_rule, tuples.src_ip), + offsetof(struct hclge_fd_rule, tuples_mask.src_ip) }, + { INNER_DST_IP, 32, KEY_OPT_IP, + offsetof(struct hclge_fd_rule, tuples.dst_ip), + offsetof(struct hclge_fd_rule, tuples_mask.dst_ip) }, + { INNER_L3_RSV, 16, KEY_OPT_LE16, + offsetof(struct hclge_fd_rule, tuples.l3_user_def), + offsetof(struct hclge_fd_rule, tuples_mask.l3_user_def) }, + { INNER_SRC_PORT, 16, KEY_OPT_LE16, + offsetof(struct hclge_fd_rule, tuples.src_port), + offsetof(struct hclge_fd_rule, tuples_mask.src_port) }, + { INNER_DST_PORT, 16, KEY_OPT_LE16, + offsetof(struct hclge_fd_rule, tuples.dst_port), + offsetof(struct hclge_fd_rule, tuples_mask.dst_port) }, + { INNER_L4_RSV, 32, KEY_OPT_LE32, + offsetof(struct hclge_fd_rule, tuples.l4_user_def), + offsetof(struct hclge_fd_rule, tuples_mask.l4_user_def) }, +}; + +static void hclge_sync_fd_state(struct hclge_dev *hdev) +{ + if (hlist_empty(&hdev->fd_rule_list)) + hdev->fd_active_type = HCLGE_FD_RULE_NONE; +} + +static void hclge_fd_inc_rule_cnt(struct hclge_dev *hdev, u16 location) +{ + if (!test_bit(location, hdev->fd_bmap)) { + set_bit(location, hdev->fd_bmap); + hdev->hclge_fd_rule_num++; + } +} + +static void hclge_fd_dec_rule_cnt(struct hclge_dev *hdev, u16 location) +{ + if (test_bit(location, hdev->fd_bmap)) { + clear_bit(location, hdev->fd_bmap); + hdev->hclge_fd_rule_num--; + } +} + +static void hclge_fd_free_node(struct hclge_dev *hdev, + struct hclge_fd_rule *rule) +{ + hlist_del(&rule->rule_node); + kfree(rule); + hclge_sync_fd_state(hdev); +} + +static void hclge_update_fd_rule_node(struct hclge_dev *hdev, + struct hclge_fd_rule *old_rule, + struct hclge_fd_rule *new_rule, + enum HCLGE_FD_NODE_STATE state) +{ + switch (state) { + case HCLGE_FD_TO_ADD: + case HCLGE_FD_ACTIVE: + /* 1) if the new state is TO_ADD, just replace the old rule + * with the same location, no matter its state, because the + * new rule will be configured to the hardware. + * 2) if the new state is ACTIVE, it means the new rule + * has been configured to the hardware, so just replace + * the old rule node with the same location. + * 3) for it doesn't add a new node to the list, so it's + * unnecessary to update the rule number and fd_bmap. + */ + new_rule->rule_node.next = old_rule->rule_node.next; + new_rule->rule_node.pprev = old_rule->rule_node.pprev; + memcpy(old_rule, new_rule, sizeof(*old_rule)); + kfree(new_rule); + break; + case HCLGE_FD_DELETED: + hclge_fd_dec_rule_cnt(hdev, old_rule->location); + hclge_fd_free_node(hdev, old_rule); + break; + case HCLGE_FD_TO_DEL: + /* if new request is TO_DEL, and old rule is existent + * 1) the state of old rule is TO_DEL, we need do nothing, + * because we delete rule by location, other rule content + * is unnecessary. + * 2) the state of old rule is ACTIVE, we need to change its + * state to TO_DEL, so the rule will be deleted when periodic + * task being scheduled. + * 3) the state of old rule is TO_ADD, it means the rule hasn't + * been added to hardware, so we just delete the rule node from + * fd_rule_list directly. + */ + if (old_rule->state == HCLGE_FD_TO_ADD) { + hclge_fd_dec_rule_cnt(hdev, old_rule->location); + hclge_fd_free_node(hdev, old_rule); + return; + } + old_rule->state = HCLGE_FD_TO_DEL; + break; + } +} + +static struct hclge_fd_rule *hclge_find_fd_rule(struct hlist_head *hlist, + u16 location, + struct hclge_fd_rule **parent) +{ + struct hclge_fd_rule *rule; + struct hlist_node *node; + + hlist_for_each_entry_safe(rule, node, hlist, rule_node) { + if (rule->location == location) + return rule; + else if (rule->location > location) + return NULL; + /* record the parent node, use to keep the nodes in fd_rule_list + * in ascend order. + */ + *parent = rule; + } + + return NULL; +} + +/* insert fd rule node in ascend order according to rule->location */ +static void hclge_fd_insert_rule_node(struct hlist_head *hlist, + struct hclge_fd_rule *rule, + struct hclge_fd_rule *parent) +{ + INIT_HLIST_NODE(&rule->rule_node); + + if (parent) + hlist_add_behind(&rule->rule_node, &parent->rule_node); + else + hlist_add_head(&rule->rule_node, hlist); +} + +static int hclge_fd_set_user_def_cmd(struct hclge_dev *hdev, + struct hclge_fd_user_def_cfg *cfg) +{ + struct hclge_fd_user_def_cfg_cmd *req; + struct hclge_desc desc; + u16 data = 0; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false); + + req = (struct hclge_fd_user_def_cfg_cmd *)desc.data; + + hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[0].ref_cnt > 0); + hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M, + HCLGE_FD_USER_DEF_OFT_S, cfg[0].offset); + req->ol2_cfg = cpu_to_le16(data); + + data = 0; + hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[1].ref_cnt > 0); + hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M, + HCLGE_FD_USER_DEF_OFT_S, cfg[1].offset); + req->ol3_cfg = cpu_to_le16(data); + + data = 0; + hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[2].ref_cnt > 0); + hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M, + HCLGE_FD_USER_DEF_OFT_S, cfg[2].offset); + req->ol4_cfg = cpu_to_le16(data); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "failed to set fd user def data, ret= %d\n", ret); + return ret; +} + +static void hclge_sync_fd_user_def_cfg(struct hclge_dev *hdev, bool locked) +{ + int ret; + + if (!test_and_clear_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state)) + return; + + if (!locked) + spin_lock_bh(&hdev->fd_rule_lock); + + ret = hclge_fd_set_user_def_cmd(hdev, hdev->fd_cfg.user_def_cfg); + if (ret) + set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); + + if (!locked) + spin_unlock_bh(&hdev->fd_rule_lock); +} + +static int hclge_fd_check_user_def_refcnt(struct hclge_dev *hdev, + struct hclge_fd_rule *rule) +{ + struct hlist_head *hlist = &hdev->fd_rule_list; + struct hclge_fd_rule *fd_rule, *parent = NULL; + struct hclge_fd_user_def_info *info, *old_info; + struct hclge_fd_user_def_cfg *cfg; + + if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE || + rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE) + return 0; + + /* for valid layer is start from 1, so need minus 1 to get the cfg */ + cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; + info = &rule->ep.user_def; + + if (!cfg->ref_cnt || cfg->offset == info->offset) + return 0; + + if (cfg->ref_cnt > 1) + goto error; + + fd_rule = hclge_find_fd_rule(hlist, rule->location, &parent); + if (fd_rule) { + old_info = &fd_rule->ep.user_def; + if (info->layer == old_info->layer) + return 0; + } + +error: + dev_err(&hdev->pdev->dev, + "No available offset for layer%d fd rule, each layer only support one user def offset.\n", + info->layer + 1); + return -ENOSPC; +} + +static void hclge_fd_inc_user_def_refcnt(struct hclge_dev *hdev, + struct hclge_fd_rule *rule) +{ + struct hclge_fd_user_def_cfg *cfg; + + if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE || + rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE) + return; + + cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; + if (!cfg->ref_cnt) { + cfg->offset = rule->ep.user_def.offset; + set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); + } + cfg->ref_cnt++; +} + +static void hclge_fd_dec_user_def_refcnt(struct hclge_dev *hdev, + struct hclge_fd_rule *rule) +{ + struct hclge_fd_user_def_cfg *cfg; + + if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE || + rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE) + return; + + cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; + if (!cfg->ref_cnt) + return; + + cfg->ref_cnt--; + if (!cfg->ref_cnt) { + cfg->offset = 0; + set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); + } +} + +static void hclge_update_fd_list(struct hclge_dev *hdev, + enum HCLGE_FD_NODE_STATE state, u16 location, + struct hclge_fd_rule *new_rule) +{ + struct hlist_head *hlist = &hdev->fd_rule_list; + struct hclge_fd_rule *fd_rule, *parent = NULL; + + fd_rule = hclge_find_fd_rule(hlist, location, &parent); + if (fd_rule) { + hclge_fd_dec_user_def_refcnt(hdev, fd_rule); + if (state == HCLGE_FD_ACTIVE) + hclge_fd_inc_user_def_refcnt(hdev, new_rule); + hclge_sync_fd_user_def_cfg(hdev, true); + + hclge_update_fd_rule_node(hdev, fd_rule, new_rule, state); + return; + } + + /* it's unlikely to fail here, because we have checked the rule + * exist before. + */ + if (unlikely(state == HCLGE_FD_TO_DEL || state == HCLGE_FD_DELETED)) { + dev_warn(&hdev->pdev->dev, + "failed to delete fd rule %u, it's inexistent\n", + location); + return; + } + + hclge_fd_inc_user_def_refcnt(hdev, new_rule); + hclge_sync_fd_user_def_cfg(hdev, true); + + hclge_fd_insert_rule_node(hlist, new_rule, parent); + hclge_fd_inc_rule_cnt(hdev, new_rule->location); + + if (state == HCLGE_FD_TO_ADD) { + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); + hclge_task_schedule(hdev, 0); + } +} + +static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode) +{ + struct hclge_get_fd_mode_cmd *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true); + + req = (struct hclge_get_fd_mode_cmd *)desc.data; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "get fd mode fail, ret=%d\n", ret); + return ret; + } + + *fd_mode = req->mode; + + return ret; +} + +static int hclge_get_fd_allocation(struct hclge_dev *hdev, + u32 *stage1_entry_num, + u32 *stage2_entry_num, + u16 *stage1_counter_num, + u16 *stage2_counter_num) +{ + struct hclge_get_fd_allocation_cmd *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_GET_ALLOCATION, true); + + req = (struct hclge_get_fd_allocation_cmd *)desc.data; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=%d\n", + ret); + return ret; + } + + *stage1_entry_num = le32_to_cpu(req->stage1_entry_num); + *stage2_entry_num = le32_to_cpu(req->stage2_entry_num); + *stage1_counter_num = le16_to_cpu(req->stage1_counter_num); + *stage2_counter_num = le16_to_cpu(req->stage2_counter_num); + + return ret; +} + +static int hclge_set_fd_key_config(struct hclge_dev *hdev, + enum HCLGE_FD_STAGE stage_num) +{ + struct hclge_set_fd_key_config_cmd *req; + struct hclge_fd_key_cfg *stage; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_KEY_CONFIG, false); + + req = (struct hclge_set_fd_key_config_cmd *)desc.data; + stage = &hdev->fd_cfg.key_cfg[stage_num]; + req->stage = stage_num; + req->key_select = stage->key_sel; + req->inner_sipv6_word_en = stage->inner_sipv6_word_en; + req->inner_dipv6_word_en = stage->inner_dipv6_word_en; + req->outer_sipv6_word_en = stage->outer_sipv6_word_en; + req->outer_dipv6_word_en = stage->outer_dipv6_word_en; + req->tuple_mask = cpu_to_le32(~stage->tuple_active); + req->meta_data_mask = cpu_to_le32(~stage->meta_data_active); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, "set fd key fail, ret=%d\n", ret); + + return ret; +} + +static void hclge_fd_disable_user_def(struct hclge_dev *hdev) +{ + struct hclge_fd_user_def_cfg *cfg = hdev->fd_cfg.user_def_cfg; + + spin_lock_bh(&hdev->fd_rule_lock); + memset(cfg, 0, sizeof(hdev->fd_cfg.user_def_cfg)); + spin_unlock_bh(&hdev->fd_rule_lock); + + hclge_fd_set_user_def_cmd(hdev, cfg); +} + +int hclge_init_fd_config(struct hclge_dev *hdev) +{ +#define LOW_2_WORDS 0x03 + struct hclge_fd_key_cfg *key_cfg; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return 0; + + ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode); + if (ret) + return ret; + + switch (hdev->fd_cfg.fd_mode) { + case HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1: + hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH; + break; + case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1: + hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH / 2; + break; + default: + dev_err(&hdev->pdev->dev, + "Unsupported flow director mode %u\n", + hdev->fd_cfg.fd_mode); + return -EOPNOTSUPP; + } + + key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1]; + key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE; + key_cfg->inner_sipv6_word_en = LOW_2_WORDS; + key_cfg->inner_dipv6_word_en = LOW_2_WORDS; + key_cfg->outer_sipv6_word_en = 0; + key_cfg->outer_dipv6_word_en = 0; + + key_cfg->tuple_active = BIT(INNER_VLAN_TAG_FST) | BIT(INNER_ETH_TYPE) | + BIT(INNER_IP_PROTO) | BIT(INNER_IP_TOS) | + BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) | + BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); + + /* If use max 400bit key, we can support tuples for ether type */ + if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) { + key_cfg->tuple_active |= BIT(INNER_DST_MAC) | + BIT(INNER_SRC_MAC) | + BIT(OUTER_TUN_VNI); + if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3) + key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES; + } + + /* roce_type is used to filter roce frames + * dst_vport is used to specify the rule + */ + key_cfg->meta_data_active = BIT(ROCE_TYPE) | BIT(DST_VPORT); + + ret = hclge_get_fd_allocation(hdev, + &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1], + &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_2], + &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1], + &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_2]); + if (ret) + return ret; + + return hclge_set_fd_key_config(hdev, HCLGE_FD_STAGE_1); +} + +static int hclge_fd_tcam_config(struct hclge_dev *hdev, u8 stage, bool sel_x, + int loc, u8 *key, bool is_add) +{ + struct hclge_fd_tcam_config_1_cmd *req1; + struct hclge_fd_tcam_config_2_cmd *req2; + struct hclge_fd_tcam_config_3_cmd *req3; + struct hclge_desc desc[3]; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, false); + desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false); + desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false); + + req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data; + req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data; + req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data; + + req1->stage = stage; + req1->xy_sel = sel_x ? 1 : 0; + hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0); + req1->index = cpu_to_le32(loc); + req1->entry_vld = sel_x ? is_add : 0; + + if (key) { + memcpy(req1->tcam_data, &key[0], sizeof(req1->tcam_data)); + memcpy(req2->tcam_data, &key[sizeof(req1->tcam_data)], + sizeof(req2->tcam_data)); + memcpy(req3->tcam_data, &key[sizeof(req1->tcam_data) + + sizeof(req2->tcam_data)], sizeof(req3->tcam_data)); + } + + ret = hclge_cmd_send(&hdev->hw, desc, 3); + if (ret) + dev_err(&hdev->pdev->dev, + "config tcam key fail, ret=%d\n", + ret); + + return ret; +} + +static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc, + struct hclge_fd_ad_data *action) +{ + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); + struct hclge_fd_ad_config_cmd *req; + struct hclge_desc desc; + u64 ad_data = 0; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false); + + req = (struct hclge_fd_ad_config_cmd *)desc.data; + req->index = cpu_to_le32(loc); + req->stage = stage; + + hnae3_set_bit(ad_data, HCLGE_FD_AD_WR_RULE_ID_B, + action->write_rule_id_to_bd); + hnae3_set_field(ad_data, HCLGE_FD_AD_RULE_ID_M, HCLGE_FD_AD_RULE_ID_S, + action->rule_id); + if (test_bit(HNAE3_DEV_SUPPORT_FD_FORWARD_TC_B, ae_dev->caps)) { + hnae3_set_bit(ad_data, HCLGE_FD_AD_TC_OVRD_B, + action->override_tc); + hnae3_set_field(ad_data, HCLGE_FD_AD_TC_SIZE_M, + HCLGE_FD_AD_TC_SIZE_S, (u32)action->tc_size); + } + hnae3_set_bit(ad_data, HCLGE_FD_AD_QID_H_B, + action->queue_id >= HCLGE_TQP_MAX_SIZE_DEV_V2 ? 1 : 0); + hnae3_set_bit(ad_data, HCLGE_FD_AD_COUNTER_NUM_H_B, + action->counter_id >= HCLGE_FD_COUNTER_MAX_SIZE_DEV_V2 ? + 1 : 0); + ad_data <<= 32; + hnae3_set_bit(ad_data, HCLGE_FD_AD_DROP_B, action->drop_packet); + hnae3_set_bit(ad_data, HCLGE_FD_AD_DIRECT_QID_B, + action->forward_to_direct_queue); + hnae3_set_field(ad_data, HCLGE_FD_AD_QID_L_M, HCLGE_FD_AD_QID_L_S, + action->queue_id); + hnae3_set_bit(ad_data, HCLGE_FD_AD_USE_COUNTER_B, action->use_counter); + hnae3_set_field(ad_data, HCLGE_FD_AD_COUNTER_NUM_L_M, + HCLGE_FD_AD_COUNTER_NUM_L_S, action->counter_id); + hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage); + hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S, + action->next_input_key); + + req->ad_data = cpu_to_le64(ad_data); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, "fd ad config fail, ret=%d\n", ret); + + return ret; +} + +static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y, + struct hclge_fd_rule *rule) +{ + int offset, moffset, ip_offset; + enum HCLGE_FD_KEY_OPT key_opt; + u16 tmp_x_s, tmp_y_s; + u32 tmp_x_l, tmp_y_l; + u8 *p = (u8 *)rule; + __le32 le_x, le_y; + int i; + + if (rule->unused_tuple & BIT(tuple_bit)) + return true; + + key_opt = tuple_key_info[tuple_bit].key_opt; + offset = tuple_key_info[tuple_bit].offset; + moffset = tuple_key_info[tuple_bit].moffset; + + switch (key_opt) { + case KEY_OPT_U8: + calc_x(*key_x, p[offset], p[moffset]); + calc_y(*key_y, p[offset], p[moffset]); + + return true; + case KEY_OPT_LE16: + calc_x(tmp_x_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset])); + calc_y(tmp_y_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset])); + *(__le16 *)key_x = cpu_to_le16(tmp_x_s); + *(__le16 *)key_y = cpu_to_le16(tmp_y_s); + + return true; + case KEY_OPT_LE32: + calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset])); + calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset])); + *(__le32 *)key_x = cpu_to_le32(tmp_x_l); + *(__le32 *)key_y = cpu_to_le32(tmp_y_l); + + return true; + case KEY_OPT_MAC: + for (i = 0; i < ETH_ALEN; i++) { + calc_x(key_x[ETH_ALEN - 1 - i], p[offset + i], + p[moffset + i]); + calc_y(key_y[ETH_ALEN - 1 - i], p[offset + i], + p[moffset + i]); + } + + return true; + case KEY_OPT_IP: + ip_offset = IPV4_INDEX * sizeof(u32); + calc_x(tmp_x_l, *(u32 *)(&p[offset + ip_offset]), + *(u32 *)(&p[moffset + ip_offset])); + calc_y(tmp_y_l, *(u32 *)(&p[offset + ip_offset]), + *(u32 *)(&p[moffset + ip_offset])); + *(__le32 *)key_x = cpu_to_le32(tmp_x_l); + *(__le32 *)key_y = cpu_to_le32(tmp_y_l); + + return true; + case KEY_OPT_VNI: + calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset])); + calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset])); + le_x = cpu_to_le32(tmp_x_l); + le_y = cpu_to_le32(tmp_y_l); + memcpy(key_x, &le_x, HCLGE_VNI_LENGTH); + memcpy(key_y, &le_y, HCLGE_VNI_LENGTH); + + return true; + default: + return false; + } +} + +static void hclge_fd_convert_meta_data(struct hclge_fd_key_cfg *key_cfg, + __le32 *key_x, __le32 *key_y, + struct hclge_fd_rule *rule) +{ + u32 tuple_bit, meta_data = 0, tmp_x, tmp_y, port_number; + u8 cur_pos = 0, tuple_size, shift_bits; + unsigned int i; + + for (i = 0; i < MAX_META_DATA; i++) { + tuple_size = meta_data_key_info[i].key_length; + tuple_bit = key_cfg->meta_data_active & BIT(i); + + switch (tuple_bit) { + case BIT(ROCE_TYPE): + hnae3_set_bit(meta_data, cur_pos, NIC_PACKET); + cur_pos += tuple_size; + break; + case BIT(DST_VPORT): + port_number = hclge_get_port_number(HOST_PORT, 0, + rule->vf_id, 0); + hnae3_set_field(meta_data, + GENMASK(cur_pos + tuple_size, cur_pos), + cur_pos, port_number); + cur_pos += tuple_size; + break; + default: + break; + } + } + + calc_x(tmp_x, meta_data, 0xFFFFFFFF); + calc_y(tmp_y, meta_data, 0xFFFFFFFF); + shift_bits = sizeof(meta_data) * 8 - cur_pos; + + *key_x = cpu_to_le32(tmp_x << shift_bits); + *key_y = cpu_to_le32(tmp_y << shift_bits); +} + +/* A complete key is combined with meta data key and tuple key. + * Meta data key is stored at the MSB region, and tuple key is stored at + * the LSB region, unused bits will be filled 0. + */ +static int hclge_config_key(struct hclge_dev *hdev, u8 stage, + struct hclge_fd_rule *rule) +{ + struct hclge_fd_key_cfg *key_cfg = &hdev->fd_cfg.key_cfg[stage]; + u8 key_x[MAX_KEY_BYTES], key_y[MAX_KEY_BYTES]; + u8 *cur_key_x, *cur_key_y; + u8 meta_data_region; + u8 tuple_size; + int ret; + u32 i; + + memset(key_x, 0, sizeof(key_x)); + memset(key_y, 0, sizeof(key_y)); + cur_key_x = key_x; + cur_key_y = key_y; + + for (i = 0; i < MAX_TUPLE; i++) { + bool tuple_valid; + + tuple_size = tuple_key_info[i].key_length / 8; + if (!(key_cfg->tuple_active & BIT(i))) + continue; + + tuple_valid = hclge_fd_convert_tuple(i, cur_key_x, + cur_key_y, rule); + if (tuple_valid) { + cur_key_x += tuple_size; + cur_key_y += tuple_size; + } + } + + meta_data_region = hdev->fd_cfg.max_key_length / 8 - + MAX_META_DATA_LENGTH / 8; + + hclge_fd_convert_meta_data(key_cfg, + (__le32 *)(key_x + meta_data_region), + (__le32 *)(key_y + meta_data_region), + rule); + + ret = hclge_fd_tcam_config(hdev, stage, false, rule->location, key_y, + true); + if (ret) { + dev_err(&hdev->pdev->dev, + "fd key_y config fail, loc=%u, ret=%d\n", + rule->queue_id, ret); + return ret; + } + + ret = hclge_fd_tcam_config(hdev, stage, true, rule->location, key_x, + true); + if (ret) + dev_err(&hdev->pdev->dev, + "fd key_x config fail, loc=%u, ret=%d\n", + rule->queue_id, ret); + return ret; +} + +static int hclge_config_action(struct hclge_dev *hdev, u8 stage, + struct hclge_fd_rule *rule) +{ + struct hclge_vport *vport = hdev->vport; + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_fd_ad_data ad_data; + + memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data)); + ad_data.ad_id = rule->location; + + if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) { + ad_data.drop_packet = true; + } else if (rule->action == HCLGE_FD_ACTION_SELECT_TC) { + ad_data.override_tc = true; + ad_data.queue_id = + kinfo->tc_info.tqp_offset[rule->cls_flower.tc]; + ad_data.tc_size = + ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]); + } else { + ad_data.forward_to_direct_queue = true; + ad_data.queue_id = rule->queue_id; + } + + if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) { + ad_data.use_counter = true; + ad_data.counter_id = rule->vf_id % + hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]; + } else { + ad_data.use_counter = false; + ad_data.counter_id = 0; + } + + ad_data.use_next_stage = false; + ad_data.next_input_key = 0; + + ad_data.write_rule_id_to_bd = true; + ad_data.rule_id = rule->location; + + return hclge_fd_ad_config(hdev, stage, ad_data.ad_id, &ad_data); +} + +static int hclge_fd_check_tcpip4_tuple(struct ethtool_tcpip4_spec *spec, + u32 *unused_tuple) +{ + if (!spec || !unused_tuple) + return -EINVAL; + + *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC); + + if (!spec->ip4src) + *unused_tuple |= BIT(INNER_SRC_IP); + + if (!spec->ip4dst) + *unused_tuple |= BIT(INNER_DST_IP); + + if (!spec->psrc) + *unused_tuple |= BIT(INNER_SRC_PORT); + + if (!spec->pdst) + *unused_tuple |= BIT(INNER_DST_PORT); + + if (!spec->tos) + *unused_tuple |= BIT(INNER_IP_TOS); + + return 0; +} + +static int hclge_fd_check_ip4_tuple(struct ethtool_usrip4_spec *spec, + u32 *unused_tuple) +{ + if (!spec || !unused_tuple) + return -EINVAL; + + *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | + BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); + + if (!spec->ip4src) + *unused_tuple |= BIT(INNER_SRC_IP); + + if (!spec->ip4dst) + *unused_tuple |= BIT(INNER_DST_IP); + + if (!spec->tos) + *unused_tuple |= BIT(INNER_IP_TOS); + + if (!spec->proto) + *unused_tuple |= BIT(INNER_IP_PROTO); + + if (spec->l4_4_bytes) + return -EOPNOTSUPP; + + if (spec->ip_ver != ETH_RX_NFC_IP4) + return -EOPNOTSUPP; + + return 0; +} + +static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec, + u32 *unused_tuple) +{ + if (!spec || !unused_tuple) + return -EINVAL; + + *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC); + + /* check whether src/dst ip address used */ + if (ipv6_addr_any((struct in6_addr *)spec->ip6src)) + *unused_tuple |= BIT(INNER_SRC_IP); + + if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) + *unused_tuple |= BIT(INNER_DST_IP); + + if (!spec->psrc) + *unused_tuple |= BIT(INNER_SRC_PORT); + + if (!spec->pdst) + *unused_tuple |= BIT(INNER_DST_PORT); + + if (!spec->tclass) + *unused_tuple |= BIT(INNER_IP_TOS); + + return 0; +} + +static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec, + u32 *unused_tuple) +{ + if (!spec || !unused_tuple) + return -EINVAL; + + *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | + BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); + + /* check whether src/dst ip address used */ + if (ipv6_addr_any((struct in6_addr *)spec->ip6src)) + *unused_tuple |= BIT(INNER_SRC_IP); + + if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) + *unused_tuple |= BIT(INNER_DST_IP); + + if (!spec->l4_proto) + *unused_tuple |= BIT(INNER_IP_PROTO); + + if (!spec->tclass) + *unused_tuple |= BIT(INNER_IP_TOS); + + if (spec->l4_4_bytes) + return -EOPNOTSUPP; + + return 0; +} + +static int hclge_fd_check_ether_tuple(struct ethhdr *spec, u32 *unused_tuple) +{ + if (!spec || !unused_tuple) + return -EINVAL; + + *unused_tuple |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) | + BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) | + BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO); + + if (is_zero_ether_addr(spec->h_source)) + *unused_tuple |= BIT(INNER_SRC_MAC); + + if (is_zero_ether_addr(spec->h_dest)) + *unused_tuple |= BIT(INNER_DST_MAC); + + if (!spec->h_proto) + *unused_tuple |= BIT(INNER_ETH_TYPE); + + return 0; +} + +static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev, + struct ethtool_rx_flow_spec *fs, + u32 *unused_tuple) +{ + if (fs->flow_type & FLOW_EXT) { + if (fs->h_ext.vlan_etype) { + dev_err(&hdev->pdev->dev, "vlan-etype is not supported!\n"); + return -EOPNOTSUPP; + } + + if (!fs->h_ext.vlan_tci) + *unused_tuple |= BIT(INNER_VLAN_TAG_FST); + + if (fs->m_ext.vlan_tci && + be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) { + dev_err(&hdev->pdev->dev, + "failed to config vlan_tci, invalid vlan_tci: %u, max is %d.\n", + ntohs(fs->h_ext.vlan_tci), VLAN_N_VID - 1); + return -EINVAL; + } + } else { + *unused_tuple |= BIT(INNER_VLAN_TAG_FST); + } + + if (fs->flow_type & FLOW_MAC_EXT) { + if (hdev->fd_cfg.fd_mode != + HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) { + dev_err(&hdev->pdev->dev, + "FLOW_MAC_EXT is not supported in current fd mode!\n"); + return -EOPNOTSUPP; + } + + if (is_zero_ether_addr(fs->h_ext.h_dest)) + *unused_tuple |= BIT(INNER_DST_MAC); + else + *unused_tuple &= ~BIT(INNER_DST_MAC); + } + + return 0; +} + +static int hclge_fd_get_user_def_layer(u32 flow_type, u32 *unused_tuple, + struct hclge_fd_user_def_info *info) +{ + switch (flow_type) { + case ETHER_FLOW: + info->layer = HCLGE_FD_USER_DEF_L2; + *unused_tuple &= ~BIT(INNER_L2_RSV); + break; + case IP_USER_FLOW: + case IPV6_USER_FLOW: + info->layer = HCLGE_FD_USER_DEF_L3; + *unused_tuple &= ~BIT(INNER_L3_RSV); + break; + case TCP_V4_FLOW: + case UDP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V6_FLOW: + info->layer = HCLGE_FD_USER_DEF_L4; + *unused_tuple &= ~BIT(INNER_L4_RSV); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *fs) +{ + return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) == 0; +} + +static int hclge_fd_parse_user_def_field(struct hclge_dev *hdev, + struct ethtool_rx_flow_spec *fs, + u32 *unused_tuple, + struct hclge_fd_user_def_info *info) +{ + u32 tuple_active = hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active; + u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + u16 data, offset, data_mask, offset_mask; + int ret; + + info->layer = HCLGE_FD_USER_DEF_NONE; + *unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES; + + if (!(fs->flow_type & FLOW_EXT) || hclge_fd_is_user_def_all_masked(fs)) + return 0; + + /* user-def data from ethtool is 64 bit value, the bit0~15 is used + * for data, and bit32~47 is used for offset. + */ + data = be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; + data_mask = be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; + offset = be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET; + offset_mask = be32_to_cpu(fs->m_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET; + + if (!(tuple_active & HCLGE_FD_TUPLE_USER_DEF_TUPLES)) { + dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n"); + return -EOPNOTSUPP; + } + + if (offset > HCLGE_FD_MAX_USER_DEF_OFFSET) { + dev_err(&hdev->pdev->dev, + "user-def offset[%u] should be no more than %u\n", + offset, HCLGE_FD_MAX_USER_DEF_OFFSET); + return -EINVAL; + } + + if (offset_mask != HCLGE_FD_USER_DEF_OFFSET_UNMASK) { + dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n"); + return -EINVAL; + } + + ret = hclge_fd_get_user_def_layer(flow_type, unused_tuple, info); + if (ret) { + dev_err(&hdev->pdev->dev, + "unsupported flow type for user-def bytes, ret = %d\n", + ret); + return ret; + } + + info->data = data; + info->data_mask = data_mask; + info->offset = offset; + + return 0; +} + +static int hclge_fd_check_spec(struct hclge_dev *hdev, + struct ethtool_rx_flow_spec *fs, + u32 *unused_tuple, + struct hclge_fd_user_def_info *info) +{ + u32 flow_type; + int ret; + + if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { + dev_err(&hdev->pdev->dev, + "failed to config fd rules, invalid rule location: %u, max is %u\n.", + fs->location, + hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1] - 1); + return -EINVAL; + } + + ret = hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info); + if (ret) + return ret; + + flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + switch (flow_type) { + case SCTP_V4_FLOW: + case TCP_V4_FLOW: + case UDP_V4_FLOW: + ret = hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec, + unused_tuple); + break; + case IP_USER_FLOW: + ret = hclge_fd_check_ip4_tuple(&fs->h_u.usr_ip4_spec, + unused_tuple); + break; + case SCTP_V6_FLOW: + case TCP_V6_FLOW: + case UDP_V6_FLOW: + ret = hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec, + unused_tuple); + break; + case IPV6_USER_FLOW: + ret = hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec, + unused_tuple); + break; + case ETHER_FLOW: + if (hdev->fd_cfg.fd_mode != + HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) { + dev_err(&hdev->pdev->dev, + "ETHER_FLOW is not supported in current fd mode!\n"); + return -EOPNOTSUPP; + } + + ret = hclge_fd_check_ether_tuple(&fs->h_u.ether_spec, + unused_tuple); + break; + default: + dev_err(&hdev->pdev->dev, + "unsupported protocol type, protocol type = %#x\n", + flow_type); + return -EOPNOTSUPP; + } + + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to check flow union tuple, ret = %d\n", + ret); + return ret; + } + + return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple); +} + +static void hclge_fd_get_tcpip4_tuple(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule, u8 ip_proto) +{ + rule->tuples.src_ip[IPV4_INDEX] = + be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src); + rule->tuples_mask.src_ip[IPV4_INDEX] = + be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src); + + rule->tuples.dst_ip[IPV4_INDEX] = + be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst); + rule->tuples_mask.dst_ip[IPV4_INDEX] = + be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst); + + rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc); + rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc); + + rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst); + rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst); + + rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos; + rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos; + + rule->tuples.ether_proto = ETH_P_IP; + rule->tuples_mask.ether_proto = 0xFFFF; + + rule->tuples.ip_proto = ip_proto; + rule->tuples_mask.ip_proto = 0xFF; +} + +static void hclge_fd_get_ip4_tuple(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule) +{ + rule->tuples.src_ip[IPV4_INDEX] = + be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src); + rule->tuples_mask.src_ip[IPV4_INDEX] = + be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src); + + rule->tuples.dst_ip[IPV4_INDEX] = + be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst); + rule->tuples_mask.dst_ip[IPV4_INDEX] = + be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst); + + rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos; + rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos; + + rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto; + rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto; + + rule->tuples.ether_proto = ETH_P_IP; + rule->tuples_mask.ether_proto = 0xFFFF; +} + +static void hclge_fd_get_tcpip6_tuple(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule, u8 ip_proto) +{ + ipv6_addr_be32_to_cpu(rule->tuples.src_ip, + fs->h_u.tcp_ip6_spec.ip6src); + ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip, + fs->m_u.tcp_ip6_spec.ip6src); + + ipv6_addr_be32_to_cpu(rule->tuples.dst_ip, + fs->h_u.tcp_ip6_spec.ip6dst); + ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip, + fs->m_u.tcp_ip6_spec.ip6dst); + + rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc); + rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc); + + rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst); + rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst); + + rule->tuples.ether_proto = ETH_P_IPV6; + rule->tuples_mask.ether_proto = 0xFFFF; + + rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass; + rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass; + + rule->tuples.ip_proto = ip_proto; + rule->tuples_mask.ip_proto = 0xFF; +} + +static void hclge_fd_get_ip6_tuple(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule) +{ + ipv6_addr_be32_to_cpu(rule->tuples.src_ip, + fs->h_u.usr_ip6_spec.ip6src); + ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip, + fs->m_u.usr_ip6_spec.ip6src); + + ipv6_addr_be32_to_cpu(rule->tuples.dst_ip, + fs->h_u.usr_ip6_spec.ip6dst); + ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip, + fs->m_u.usr_ip6_spec.ip6dst); + + rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto; + rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto; + + rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass; + rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass; + + rule->tuples.ether_proto = ETH_P_IPV6; + rule->tuples_mask.ether_proto = 0xFFFF; +} + +static void hclge_fd_get_ether_tuple(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule) +{ + ether_addr_copy(rule->tuples.src_mac, fs->h_u.ether_spec.h_source); + ether_addr_copy(rule->tuples_mask.src_mac, fs->m_u.ether_spec.h_source); + + ether_addr_copy(rule->tuples.dst_mac, fs->h_u.ether_spec.h_dest); + ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_u.ether_spec.h_dest); + + rule->tuples.ether_proto = be16_to_cpu(fs->h_u.ether_spec.h_proto); + rule->tuples_mask.ether_proto = be16_to_cpu(fs->m_u.ether_spec.h_proto); +} + +static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *info, + struct hclge_fd_rule *rule) +{ + switch (info->layer) { + case HCLGE_FD_USER_DEF_L2: + rule->tuples.l2_user_def = info->data; + rule->tuples_mask.l2_user_def = info->data_mask; + break; + case HCLGE_FD_USER_DEF_L3: + rule->tuples.l3_user_def = info->data; + rule->tuples_mask.l3_user_def = info->data_mask; + break; + case HCLGE_FD_USER_DEF_L4: + rule->tuples.l4_user_def = (u32)info->data << 16; + rule->tuples_mask.l4_user_def = (u32)info->data_mask << 16; + break; + default: + break; + } + + rule->ep.user_def = *info; +} + +static int hclge_fd_get_tuple(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule, + struct hclge_fd_user_def_info *info) +{ + u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + + switch (flow_type) { + case SCTP_V4_FLOW: + hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_SCTP); + break; + case TCP_V4_FLOW: + hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_TCP); + break; + case UDP_V4_FLOW: + hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_UDP); + break; + case IP_USER_FLOW: + hclge_fd_get_ip4_tuple(fs, rule); + break; + case SCTP_V6_FLOW: + hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_SCTP); + break; + case TCP_V6_FLOW: + hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_TCP); + break; + case UDP_V6_FLOW: + hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_UDP); + break; + case IPV6_USER_FLOW: + hclge_fd_get_ip6_tuple(fs, rule); + break; + case ETHER_FLOW: + hclge_fd_get_ether_tuple(fs, rule); + break; + default: + return -EOPNOTSUPP; + } + + if (fs->flow_type & FLOW_EXT) { + rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci); + rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci); + hclge_fd_get_user_def_tuple(info, rule); + } + + if (fs->flow_type & FLOW_MAC_EXT) { + ether_addr_copy(rule->tuples.dst_mac, fs->h_ext.h_dest); + ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_ext.h_dest); + } + + return 0; +} + +static int hclge_fd_config_rule(struct hclge_dev *hdev, + struct hclge_fd_rule *rule) +{ + int ret; + + ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule); + if (ret) + return ret; + + return hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule); +} + +static int hclge_add_fd_entry_common(struct hclge_dev *hdev, + struct hclge_fd_rule *rule) +{ + int ret; + + spin_lock_bh(&hdev->fd_rule_lock); + + if (hdev->fd_active_type != rule->rule_type && + (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE || + hdev->fd_active_type == HCLGE_FD_EP_ACTIVE)) { + dev_err(&hdev->pdev->dev, + "mode conflict(new type %d, active type %d), please delete existent rules first\n", + rule->rule_type, hdev->fd_active_type); + spin_unlock_bh(&hdev->fd_rule_lock); + return -EINVAL; + } + + ret = hclge_fd_check_user_def_refcnt(hdev, rule); + if (ret) + goto out; + + ret = hclge_clear_arfs_rules(hdev); + if (ret) + goto out; + + ret = hclge_fd_config_rule(hdev, rule); + if (ret) + goto out; + + rule->state = HCLGE_FD_ACTIVE; + hdev->fd_active_type = rule->rule_type; + hclge_update_fd_list(hdev, rule->state, rule->location, rule); + +out: + spin_unlock_bh(&hdev->fd_rule_lock); + return ret; +} + +bool hclge_is_cls_flower_active(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE; +} + +static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_cookie, + u16 *vport_id, u8 *action, u16 *queue_id) +{ + struct hclge_vport *vport = hdev->vport; + + if (ring_cookie == RX_CLS_FLOW_DISC) { + *action = HCLGE_FD_ACTION_DROP_PACKET; + } else { + u32 ring = ethtool_get_flow_spec_ring(ring_cookie); + u8 vf = ethtool_get_flow_spec_ring_vf(ring_cookie); + u16 tqps; + + /* To keep consistent with user's configuration, minus 1 when + * printing 'vf', because vf id from ethtool is added 1 for vf. + */ + if (vf > hdev->num_req_vfs) { + dev_err(&hdev->pdev->dev, + "Error: vf id (%u) should be less than %u\n", + vf - 1U, hdev->num_req_vfs); + return -EINVAL; + } + + *vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id; + tqps = hdev->vport[vf].nic.kinfo.num_tqps; + + if (ring >= tqps) { + dev_err(&hdev->pdev->dev, + "Error: queue id (%u) > max tqp num (%u)\n", + ring, tqps - 1U); + return -EINVAL; + } + + *action = HCLGE_FD_ACTION_SELECT_QUEUE; + *queue_id = ring; + } + + return 0; +} + +int hclge_add_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_fd_user_def_info info; + u16 dst_vport_id = 0, q_index = 0; + struct ethtool_rx_flow_spec *fs; + struct hclge_fd_rule *rule; + u32 unused = 0; + u8 action; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { + dev_err(&hdev->pdev->dev, + "flow table director is not supported\n"); + return -EOPNOTSUPP; + } + + if (!hdev->fd_en) { + dev_err(&hdev->pdev->dev, + "please enable flow director first\n"); + return -EOPNOTSUPP; + } + + fs = (struct ethtool_rx_flow_spec *)&cmd->fs; + + ret = hclge_fd_check_spec(hdev, fs, &unused, &info); + if (ret) + return ret; + + ret = hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id, + &action, &q_index); + if (ret) + return ret; + + rule = kzalloc_obj(*rule); + if (!rule) + return -ENOMEM; + + ret = hclge_fd_get_tuple(fs, rule, &info); + if (ret) { + kfree(rule); + return ret; + } + + rule->flow_type = fs->flow_type; + rule->location = fs->location; + rule->unused_tuple = unused; + rule->vf_id = dst_vport_id; + rule->queue_id = q_index; + rule->action = action; + rule->rule_type = HCLGE_FD_EP_ACTIVE; + + ret = hclge_add_fd_entry_common(hdev, rule); + if (ret) + kfree(rule); + + return ret; +} + +int hclge_del_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct ethtool_rx_flow_spec *fs; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + fs = (struct ethtool_rx_flow_spec *)&cmd->fs; + + if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) + return -EINVAL; + + spin_lock_bh(&hdev->fd_rule_lock); + if (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE || + !test_bit(fs->location, hdev->fd_bmap)) { + dev_err(&hdev->pdev->dev, + "Delete fail, rule %u is inexistent\n", fs->location); + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOENT; + } + + ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, fs->location, + NULL, false); + if (ret) + goto out; + + hclge_update_fd_list(hdev, HCLGE_FD_DELETED, fs->location, NULL); + +out: + spin_unlock_bh(&hdev->fd_rule_lock); + return ret; +} + +static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev, + bool clear_list) +{ + struct hclge_fd_rule *rule; + struct hlist_node *node; + u16 location; + + spin_lock_bh(&hdev->fd_rule_lock); + + for_each_set_bit(location, hdev->fd_bmap, + hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) + hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location, + NULL, false); + + if (clear_list) { + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, + rule_node) { + hlist_del(&rule->rule_node); + kfree(rule); + } + hdev->fd_active_type = HCLGE_FD_RULE_NONE; + hdev->hclge_fd_rule_num = 0; + bitmap_zero(hdev->fd_bmap, + hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); + } + + spin_unlock_bh(&hdev->fd_rule_lock); +} + +void hclge_del_all_fd_entries(struct hclge_dev *hdev) +{ + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return; + + hclge_clear_fd_rules_in_list(hdev, true); + hclge_fd_disable_user_def(hdev); +} + +int hclge_restore_fd_entries(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_fd_rule *rule; + struct hlist_node *node; + + /* Return ok here, because reset error handling will check this + * return value. If error is returned here, the reset process will + * fail. + */ + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return 0; + + /* if fd is disabled, should not restore it when reset */ + if (!hdev->fd_en) + return 0; + + spin_lock_bh(&hdev->fd_rule_lock); + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { + if (rule->state == HCLGE_FD_ACTIVE) + rule->state = HCLGE_FD_TO_ADD; + } + spin_unlock_bh(&hdev->fd_rule_lock); + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); + + return 0; +} + +int hclge_get_fd_rule_cnt(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || + hclge_is_cls_flower_active(handle)) + return -EOPNOTSUPP; + + cmd->rule_cnt = hdev->hclge_fd_rule_num; + cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]; + + return 0; +} + +static void hclge_fd_get_tcpip4_info(struct hclge_fd_rule *rule, + struct ethtool_tcpip4_spec *spec, + struct ethtool_tcpip4_spec *spec_mask) +{ + spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); + spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); + + spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); + spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); + + spec->psrc = cpu_to_be16(rule->tuples.src_port); + spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.src_port); + + spec->pdst = cpu_to_be16(rule->tuples.dst_port); + spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.dst_port); + + spec->tos = rule->tuples.ip_tos; + spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; +} + +static void hclge_fd_get_ip4_info(struct hclge_fd_rule *rule, + struct ethtool_usrip4_spec *spec, + struct ethtool_usrip4_spec *spec_mask) +{ + spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); + spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); + + spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); + spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); + + spec->tos = rule->tuples.ip_tos; + spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; + + spec->proto = rule->tuples.ip_proto; + spec_mask->proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ? + 0 : rule->tuples_mask.ip_proto; + + spec->ip_ver = ETH_RX_NFC_IP4; +} + +static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule, + struct ethtool_tcpip6_spec *spec, + struct ethtool_tcpip6_spec *spec_mask) +{ + ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip); + ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip); + if (rule->unused_tuple & BIT(INNER_SRC_IP)) + memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src)); + else + ipv6_addr_cpu_to_be32(spec_mask->ip6src, + rule->tuples_mask.src_ip); + + if (rule->unused_tuple & BIT(INNER_DST_IP)) + memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst)); + else + ipv6_addr_cpu_to_be32(spec_mask->ip6dst, + rule->tuples_mask.dst_ip); + + spec->tclass = rule->tuples.ip_tos; + spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; + + spec->psrc = cpu_to_be16(rule->tuples.src_port); + spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.src_port); + + spec->pdst = cpu_to_be16(rule->tuples.dst_port); + spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.dst_port); +} + +static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule, + struct ethtool_usrip6_spec *spec, + struct ethtool_usrip6_spec *spec_mask) +{ + ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip); + ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip); + if (rule->unused_tuple & BIT(INNER_SRC_IP)) + memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src)); + else + ipv6_addr_cpu_to_be32(spec_mask->ip6src, + rule->tuples_mask.src_ip); + + if (rule->unused_tuple & BIT(INNER_DST_IP)) + memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst)); + else + ipv6_addr_cpu_to_be32(spec_mask->ip6dst, + rule->tuples_mask.dst_ip); + + spec->tclass = rule->tuples.ip_tos; + spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; + + spec->l4_proto = rule->tuples.ip_proto; + spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ? + 0 : rule->tuples_mask.ip_proto; +} + +static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule, + struct ethhdr *spec, + struct ethhdr *spec_mask) +{ + ether_addr_copy(spec->h_source, rule->tuples.src_mac); + ether_addr_copy(spec->h_dest, rule->tuples.dst_mac); + + if (rule->unused_tuple & BIT(INNER_SRC_MAC)) + eth_zero_addr(spec_mask->h_source); + else + ether_addr_copy(spec_mask->h_source, rule->tuples_mask.src_mac); + + if (rule->unused_tuple & BIT(INNER_DST_MAC)) + eth_zero_addr(spec_mask->h_dest); + else + ether_addr_copy(spec_mask->h_dest, rule->tuples_mask.dst_mac); + + spec->h_proto = cpu_to_be16(rule->tuples.ether_proto); + spec_mask->h_proto = rule->unused_tuple & BIT(INNER_ETH_TYPE) ? + 0 : cpu_to_be16(rule->tuples_mask.ether_proto); +} + +static void hclge_fd_get_user_def_info(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule) +{ + if ((rule->unused_tuple & HCLGE_FD_TUPLE_USER_DEF_TUPLES) == + HCLGE_FD_TUPLE_USER_DEF_TUPLES) { + fs->h_ext.data[0] = 0; + fs->h_ext.data[1] = 0; + fs->m_ext.data[0] = 0; + fs->m_ext.data[1] = 0; + } else { + fs->h_ext.data[0] = cpu_to_be32(rule->ep.user_def.offset); + fs->h_ext.data[1] = cpu_to_be32(rule->ep.user_def.data); + fs->m_ext.data[0] = + cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK); + fs->m_ext.data[1] = cpu_to_be32(rule->ep.user_def.data_mask); + } +} + +static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule) +{ + if (fs->flow_type & FLOW_EXT) { + fs->h_ext.vlan_tci = cpu_to_be16(rule->tuples.vlan_tag1); + fs->m_ext.vlan_tci = + rule->unused_tuple & BIT(INNER_VLAN_TAG_FST) ? + 0 : cpu_to_be16(rule->tuples_mask.vlan_tag1); + + hclge_fd_get_user_def_info(fs, rule); + } + + if (fs->flow_type & FLOW_MAC_EXT) { + ether_addr_copy(fs->h_ext.h_dest, rule->tuples.dst_mac); + if (rule->unused_tuple & BIT(INNER_DST_MAC)) + eth_zero_addr(fs->m_u.ether_spec.h_dest); + else + ether_addr_copy(fs->m_u.ether_spec.h_dest, + rule->tuples_mask.dst_mac); + } +} + +static struct hclge_fd_rule *hclge_get_fd_rule(struct hclge_dev *hdev, + u16 location) +{ + struct hclge_fd_rule *rule = NULL; + struct hlist_node *node2; + + hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) { + if (rule->location == location) + return rule; + else if (rule->location > location) + return NULL; + } + + return NULL; +} + +static void hclge_fd_get_ring_cookie(struct ethtool_rx_flow_spec *fs, + struct hclge_fd_rule *rule) +{ + if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) { + fs->ring_cookie = RX_CLS_FLOW_DISC; + } else { + u64 vf_id; + + fs->ring_cookie = rule->queue_id; + vf_id = rule->vf_id; + vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; + fs->ring_cookie |= vf_id; + } +} + +int hclge_get_fd_rule_info(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_fd_rule *rule = NULL; + struct hclge_dev *hdev = vport->back; + struct ethtool_rx_flow_spec *fs; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + fs = (struct ethtool_rx_flow_spec *)&cmd->fs; + + spin_lock_bh(&hdev->fd_rule_lock); + + rule = hclge_get_fd_rule(hdev, fs->location); + if (!rule) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOENT; + } + + fs->flow_type = rule->flow_type; + switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { + case SCTP_V4_FLOW: + case TCP_V4_FLOW: + case UDP_V4_FLOW: + hclge_fd_get_tcpip4_info(rule, &fs->h_u.tcp_ip4_spec, + &fs->m_u.tcp_ip4_spec); + break; + case IP_USER_FLOW: + hclge_fd_get_ip4_info(rule, &fs->h_u.usr_ip4_spec, + &fs->m_u.usr_ip4_spec); + break; + case SCTP_V6_FLOW: + case TCP_V6_FLOW: + case UDP_V6_FLOW: + hclge_fd_get_tcpip6_info(rule, &fs->h_u.tcp_ip6_spec, + &fs->m_u.tcp_ip6_spec); + break; + case IPV6_USER_FLOW: + hclge_fd_get_ip6_info(rule, &fs->h_u.usr_ip6_spec, + &fs->m_u.usr_ip6_spec); + break; + /* The flow type of fd rule has been checked before adding in to rule + * list. As other flow types have been handled, it must be ETHER_FLOW + * for the default case + */ + default: + hclge_fd_get_ether_info(rule, &fs->h_u.ether_spec, + &fs->m_u.ether_spec); + break; + } + + hclge_fd_get_ext_info(fs, rule); + + hclge_fd_get_ring_cookie(fs, rule); + + spin_unlock_bh(&hdev->fd_rule_lock); + + return 0; +} + +int hclge_get_all_rules(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd, u32 *rule_locs) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_fd_rule *rule; + struct hlist_node *node2; + u32 cnt = 0; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]; + + spin_lock_bh(&hdev->fd_rule_lock); + hlist_for_each_entry_safe(rule, node2, + &hdev->fd_rule_list, rule_node) { + if (cnt == cmd->rule_cnt) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -EMSGSIZE; + } + + if (rule->state == HCLGE_FD_TO_DEL) + continue; + + rule_locs[cnt] = rule->location; + cnt++; + } + + spin_unlock_bh(&hdev->fd_rule_lock); + + cmd->rule_cnt = cnt; + + return 0; +} + +static void hclge_fd_get_flow_tuples(const struct flow_keys *fkeys, + struct hclge_fd_rule_tuples *tuples) +{ +#define flow_ip6_src fkeys->addrs.v6addrs.src.in6_u.u6_addr32 +#define flow_ip6_dst fkeys->addrs.v6addrs.dst.in6_u.u6_addr32 + + tuples->ether_proto = be16_to_cpu(fkeys->basic.n_proto); + tuples->ip_proto = fkeys->basic.ip_proto; + tuples->dst_port = be16_to_cpu(fkeys->ports.dst); + + if (fkeys->basic.n_proto == htons(ETH_P_IP)) { + tuples->src_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.src); + tuples->dst_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.dst); + } else { + int i; + + for (i = 0; i < IPV6_ADDR_WORDS; i++) { + tuples->src_ip[i] = be32_to_cpu(flow_ip6_src[i]); + tuples->dst_ip[i] = be32_to_cpu(flow_ip6_dst[i]); + } + } +} + +/* traverse all rules, check whether an existed rule has the same tuples */ +static struct hclge_fd_rule * +hclge_fd_search_flow_keys(struct hclge_dev *hdev, + const struct hclge_fd_rule_tuples *tuples) +{ + struct hclge_fd_rule *rule = NULL; + struct hlist_node *node; + + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { + if (!memcmp(tuples, &rule->tuples, sizeof(*tuples))) + return rule; + } + + return NULL; +} + +static void hclge_fd_build_arfs_rule(const struct hclge_fd_rule_tuples *tuples, + struct hclge_fd_rule *rule) +{ + rule->unused_tuple = BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | + BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) | + BIT(INNER_SRC_PORT); + rule->action = 0; + rule->vf_id = 0; + rule->rule_type = HCLGE_FD_ARFS_ACTIVE; + rule->state = HCLGE_FD_TO_ADD; + if (tuples->ether_proto == ETH_P_IP) { + if (tuples->ip_proto == IPPROTO_TCP) + rule->flow_type = TCP_V4_FLOW; + else + rule->flow_type = UDP_V4_FLOW; + } else { + if (tuples->ip_proto == IPPROTO_TCP) + rule->flow_type = TCP_V6_FLOW; + else + rule->flow_type = UDP_V6_FLOW; + } + memcpy(&rule->tuples, tuples, sizeof(rule->tuples)); + memset(&rule->tuples_mask, 0xFF, sizeof(rule->tuples_mask)); +} + +int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id, + u16 flow_id, struct flow_keys *fkeys) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_fd_rule_tuples new_tuples = {}; + struct hclge_dev *hdev = vport->back; + struct hclge_fd_rule *rule; + u16 bit_id; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + /* when there is already fd rule existed add by user, + * arfs should not work + */ + spin_lock_bh(&hdev->fd_rule_lock); + if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE && + hdev->fd_active_type != HCLGE_FD_RULE_NONE) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -EOPNOTSUPP; + } + + hclge_fd_get_flow_tuples(fkeys, &new_tuples); + + /* check is there flow director filter existed for this flow, + * if not, create a new filter for it; + * if filter exist with different queue id, modify the filter; + * if filter exist with same queue id, do nothing + */ + rule = hclge_fd_search_flow_keys(hdev, &new_tuples); + if (!rule) { + bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM); + if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOSPC; + } + + rule = kzalloc_obj(*rule, GFP_ATOMIC); + if (!rule) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOMEM; + } + + rule->location = bit_id; + rule->arfs.flow_id = flow_id; + rule->queue_id = queue_id; + hclge_fd_build_arfs_rule(&new_tuples, rule); + hclge_update_fd_list(hdev, rule->state, rule->location, rule); + hdev->fd_active_type = HCLGE_FD_ARFS_ACTIVE; + } else if (rule->queue_id != queue_id) { + rule->queue_id = queue_id; + rule->state = HCLGE_FD_TO_ADD; + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); + hclge_task_schedule(hdev, 0); + } + spin_unlock_bh(&hdev->fd_rule_lock); + return rule->location; +} + +void hclge_rfs_filter_expire(struct hclge_dev *hdev) +{ +#ifdef CONFIG_RFS_ACCEL + struct hnae3_handle *handle = &hdev->vport[0].nic; + struct hclge_fd_rule *rule; + struct hlist_node *node; + + spin_lock_bh(&hdev->fd_rule_lock); + if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) { + spin_unlock_bh(&hdev->fd_rule_lock); + return; + } + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { + if (rule->state != HCLGE_FD_ACTIVE) + continue; + if (rps_may_expire_flow(handle->netdev, rule->queue_id, + rule->arfs.flow_id, rule->location)) { + rule->state = HCLGE_FD_TO_DEL; + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); + } + } + spin_unlock_bh(&hdev->fd_rule_lock); +#endif +} + +/* make sure being called after lock up with fd_rule_lock */ +int hclge_clear_arfs_rules(struct hclge_dev *hdev) +{ +#ifdef CONFIG_RFS_ACCEL + struct hclge_fd_rule *rule; + struct hlist_node *node; + int ret; + + if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) + return 0; + + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { + switch (rule->state) { + case HCLGE_FD_TO_DEL: + case HCLGE_FD_ACTIVE: + ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, + rule->location, NULL, false); + if (ret) + return ret; + fallthrough; + case HCLGE_FD_TO_ADD: + hclge_fd_dec_rule_cnt(hdev, rule->location); + hlist_del(&rule->rule_node); + kfree(rule); + break; + default: + break; + } + } + hclge_sync_fd_state(hdev); + +#endif + return 0; +} + +static void hclge_get_cls_key_basic(const struct flow_rule *flow, + struct hclge_fd_rule *rule) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic match; + u16 ethtype_key, ethtype_mask; + + flow_rule_match_basic(flow, &match); + ethtype_key = ntohs(match.key->n_proto); + ethtype_mask = ntohs(match.mask->n_proto); + + if (ethtype_key == ETH_P_ALL) { + ethtype_key = 0; + ethtype_mask = 0; + } + rule->tuples.ether_proto = ethtype_key; + rule->tuples_mask.ether_proto = ethtype_mask; + rule->tuples.ip_proto = match.key->ip_proto; + rule->tuples_mask.ip_proto = match.mask->ip_proto; + } else { + rule->unused_tuple |= BIT(INNER_IP_PROTO); + rule->unused_tuple |= BIT(INNER_ETH_TYPE); + } +} + +static void hclge_get_cls_key_mac(const struct flow_rule *flow, + struct hclge_fd_rule *rule) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + + flow_rule_match_eth_addrs(flow, &match); + ether_addr_copy(rule->tuples.dst_mac, match.key->dst); + ether_addr_copy(rule->tuples_mask.dst_mac, match.mask->dst); + ether_addr_copy(rule->tuples.src_mac, match.key->src); + ether_addr_copy(rule->tuples_mask.src_mac, match.mask->src); + if (is_zero_ether_addr(match.mask->dst)) + rule->unused_tuple |= BIT(INNER_DST_MAC); + if (is_zero_ether_addr(match.mask->src)) + rule->unused_tuple |= BIT(INNER_SRC_MAC); + } else { + rule->unused_tuple |= BIT(INNER_DST_MAC); + rule->unused_tuple |= BIT(INNER_SRC_MAC); + } +} + +static void hclge_get_cls_key_vlan(const struct flow_rule *flow, + struct hclge_fd_rule *rule) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_match_vlan match; + + flow_rule_match_vlan(flow, &match); + rule->tuples.vlan_tag1 = match.key->vlan_id | + (match.key->vlan_priority << VLAN_PRIO_SHIFT); + rule->tuples_mask.vlan_tag1 = match.mask->vlan_id | + (match.mask->vlan_priority << VLAN_PRIO_SHIFT); + } else { + rule->unused_tuple |= BIT(INNER_VLAN_TAG_FST); + } +} + +static int hclge_get_cls_key_ip(const struct flow_rule *flow, + struct hclge_fd_rule *rule, + struct netlink_ext_ack *extack) +{ + u16 addr_type = 0; + + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_match_control match; + + flow_rule_match_control(flow, &match); + addr_type = match.key->addr_type; + + if (flow_rule_has_control_flags(match.mask->flags, extack)) + return -EOPNOTSUPP; + } + + if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + struct flow_match_ipv4_addrs match; + + flow_rule_match_ipv4_addrs(flow, &match); + rule->tuples.src_ip[IPV4_INDEX] = be32_to_cpu(match.key->src); + rule->tuples_mask.src_ip[IPV4_INDEX] = + be32_to_cpu(match.mask->src); + rule->tuples.dst_ip[IPV4_INDEX] = be32_to_cpu(match.key->dst); + rule->tuples_mask.dst_ip[IPV4_INDEX] = + be32_to_cpu(match.mask->dst); + if (!match.mask->src) + rule->unused_tuple |= BIT(INNER_SRC_IP); + if (!match.mask->dst) + rule->unused_tuple |= BIT(INNER_DST_IP); + } else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { + struct flow_match_ipv6_addrs match; + + flow_rule_match_ipv6_addrs(flow, &match); + ipv6_addr_be32_to_cpu(rule->tuples.src_ip, + match.key->src.s6_addr32); + ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip, + match.mask->src.s6_addr32); + ipv6_addr_be32_to_cpu(rule->tuples.dst_ip, + match.key->dst.s6_addr32); + ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip, + match.mask->dst.s6_addr32); + if (ipv6_addr_any(&match.mask->src)) + rule->unused_tuple |= BIT(INNER_SRC_IP); + if (ipv6_addr_any(&match.mask->dst)) + rule->unused_tuple |= BIT(INNER_DST_IP); + } else { + rule->unused_tuple |= BIT(INNER_SRC_IP); + rule->unused_tuple |= BIT(INNER_DST_IP); + } + + return 0; +} + +static void hclge_get_cls_key_port(const struct flow_rule *flow, + struct hclge_fd_rule *rule) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports match; + + flow_rule_match_ports(flow, &match); + + rule->tuples.src_port = be16_to_cpu(match.key->src); + rule->tuples_mask.src_port = be16_to_cpu(match.mask->src); + rule->tuples.dst_port = be16_to_cpu(match.key->dst); + rule->tuples_mask.dst_port = be16_to_cpu(match.mask->dst); + } else { + rule->unused_tuple |= BIT(INNER_SRC_PORT); + rule->unused_tuple |= BIT(INNER_DST_PORT); + } +} + +static int hclge_get_cls_enc_keyid(struct hclge_dev *hdev, + const struct flow_rule *flow, + struct hclge_fd_rule *rule, + struct netlink_ext_ack *extack) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ENC_KEYID)) { + struct flow_match_enc_keyid match; + + flow_rule_match_enc_keyid(flow, &match); + + /* vni is only 24 bits and must be greater than 0, + * and it can not be masked. + */ + if (be32_to_cpu(match.mask->keyid) != + HCLGE_FD_VXLAN_VNI_UNMASK || + be32_to_cpu(match.key->keyid) >= VXLAN_N_VID || + !match.key->keyid) { + NL_SET_ERR_MSG_MOD(extack, "invalid enc_keyid"); + return -EINVAL; + } + + rule->tuples.outer_tun_vni = be32_to_cpu(match.key->keyid); + rule->tuples_mask.outer_tun_vni = + be32_to_cpu(match.mask->keyid); + } else { + rule->unused_tuple |= BIT(OUTER_TUN_VNI); + } + + return 0; +} + +static int hclge_get_cls_key_ip_tos(const struct flow_rule *flow, + struct hclge_fd_rule *rule, + struct netlink_ext_ack *extack) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) { + struct flow_match_ip match; + + flow_rule_match_ip(flow, &match); + + if (match.mask->ttl) { + NL_SET_ERR_MSG_MOD(extack, "unsupported TTL"); + return -EOPNOTSUPP; + } + + rule->tuples.ip_tos = match.key->tos; + rule->tuples_mask.ip_tos = match.mask->tos; + if (!rule->tuples_mask.ip_tos) + rule->unused_tuple |= BIT(INNER_IP_TOS); + } else { + rule->unused_tuple |= BIT(INNER_IP_TOS); + } + + return 0; +} + +static int hclge_get_tc_flower_action(struct hclge_dev *hdev, + struct flow_cls_offload *cls_flower, + struct hclge_fd_rule *rule) +{ + struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack = cls_flower->common.extack; + struct hnae3_handle *handle = &hdev->vport[0].nic; + struct flow_action *action = &flow->action; + struct flow_action_entry *act; + int tc; + + if (!flow_action_has_entries(&flow->action)) { + tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid); + if (tc < 0 || tc > hdev->tc_max) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "invalid traffic class: %d", tc); + return -EINVAL; + } + + rule->action = HCLGE_FD_ACTION_SELECT_TC; + rule->cls_flower.tc = tc; + return 0; + } + + act = &action->entries[0]; + switch (act->id) { + case FLOW_ACTION_RX_QUEUE_MAPPING: + if (act->rx_queue >= handle->kinfo.num_tqps) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "queue id (%u) should be less than %u", + act->rx_queue, + handle->kinfo.num_tqps); + return -EINVAL; + } + + rule->queue_id = act->rx_queue; + rule->action = HCLGE_FD_ACTION_SELECT_QUEUE; + return 0; + case FLOW_ACTION_DROP: + rule->action = HCLGE_FD_ACTION_DROP_PACKET; + return 0; + default: + NL_SET_ERR_MSG_FMT_MOD(extack, + "unsupported action(%d)", act->id); + return -EOPNOTSUPP; + } +} + +static int hclge_parse_cls_flower(struct hclge_dev *hdev, + struct flow_cls_offload *cls_flower, + struct hclge_fd_rule *rule) +{ + struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack = cls_flower->common.extack; + int ret; + + /* not support any user def tuples */ + rule->unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES; + + hclge_get_cls_key_basic(flow, rule); + hclge_get_cls_key_mac(flow, rule); + hclge_get_cls_key_vlan(flow, rule); + + ret = hclge_get_cls_key_ip(flow, rule, extack); + if (ret) + return ret; + + hclge_get_cls_key_port(flow, rule); + ret = hclge_get_cls_key_ip_tos(flow, rule, extack); + if (ret) + return ret; + + return hclge_get_cls_enc_keyid(hdev, flow, rule, extack); +} + +static int hclge_check_cls_flower(struct hclge_dev *hdev, + struct flow_cls_offload *cls_flower) +{ + struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack = cls_flower->common.extack; + struct flow_dissector *dissector = flow->match.dissector; + u32 prio = cls_flower->common.prio; + u64 support_keys; + + if (prio == 0 || + prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "prio %u should be in range[1, %u]", + prio, + hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); + return -EINVAL; + } + + if (test_bit(prio - 1, hdev->fd_bmap)) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "prio %u is already used", prio); + return -EINVAL; + } + + support_keys = BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IP); + + if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) + support_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID); + + if (dissector->used_keys & ~support_keys) { + NL_SET_ERR_MSG_FMT_MOD(extack, "unsupported key set: %#llx", + dissector->used_keys); + return -EOPNOTSUPP; + } + + /* driver will parses classid into an action */ + if (cls_flower->classid && flow_action_has_entries(&flow->action)) { + NL_SET_ERR_MSG_MOD(extack, + "cannot specify both classid and action"); + return -EOPNOTSUPP; + } + + if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) { + NL_SET_ERR_MSG_MOD(extack, + "must specify either classid or action"); + return -EINVAL; + } + + if (flow_action_has_entries(&flow->action) && + !flow_offload_has_one_action(&flow->action)) { + NL_SET_ERR_MSG_MOD(extack, "unsupported multiple actions"); + return -EOPNOTSUPP; + } + + return 0; +} + +int hclge_add_cls_flower(struct hnae3_handle *handle, + struct flow_cls_offload *cls_flower) +{ + struct netlink_ext_ack *extack = cls_flower->common.extack; + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_fd_rule *rule; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { + NL_SET_ERR_MSG_MOD(extack, "cls flower is not supported"); + return -EOPNOTSUPP; + } + + ret = hclge_check_cls_flower(hdev, cls_flower); + if (ret) + return ret; + + rule = kzalloc_obj(*rule); + if (!rule) + return -ENOMEM; + + ret = hclge_parse_cls_flower(hdev, cls_flower, rule); + if (ret) { + kfree(rule); + return ret; + } + + ret = hclge_get_tc_flower_action(hdev, cls_flower, rule); + if (ret) { + kfree(rule); + return ret; + } + + rule->location = cls_flower->common.prio - 1; + rule->vf_id = 0; + rule->cls_flower.cookie = cls_flower->cookie; + rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE; + + ret = hclge_add_fd_entry_common(hdev, rule); + if (ret) + kfree(rule); + + return ret; +} + +static struct hclge_fd_rule *hclge_find_cls_flower(struct hclge_dev *hdev, + unsigned long cookie) +{ + struct hclge_fd_rule *rule; + struct hlist_node *node; + + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { + if (rule->cls_flower.cookie == cookie) + return rule; + } + + return NULL; +} + +int hclge_del_cls_flower(struct hnae3_handle *handle, + struct flow_cls_offload *cls_flower) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_fd_rule *rule; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + spin_lock_bh(&hdev->fd_rule_lock); + + rule = hclge_find_cls_flower(hdev, cls_flower->cookie); + if (!rule) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -EINVAL; + } + + ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location, + NULL, false); + if (ret) { + /* if tcam config fail, set rule state to TO_DEL, + * so the rule will be deleted when periodic + * task being scheduled. + */ + hclge_update_fd_list(hdev, HCLGE_FD_TO_DEL, + rule->location, NULL); + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); + spin_unlock_bh(&hdev->fd_rule_lock); + return ret; + } + + hclge_update_fd_list(hdev, HCLGE_FD_DELETED, rule->location, NULL); + spin_unlock_bh(&hdev->fd_rule_lock); + + return 0; +} + +static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist) +{ + struct hclge_fd_rule *rule; + struct hlist_node *node; + int ret = 0; + + if (!test_and_clear_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state)) + return; + + spin_lock_bh(&hdev->fd_rule_lock); + + hlist_for_each_entry_safe(rule, node, hlist, rule_node) { + switch (rule->state) { + case HCLGE_FD_TO_ADD: + ret = hclge_fd_config_rule(hdev, rule); + if (ret) + goto out; + rule->state = HCLGE_FD_ACTIVE; + break; + case HCLGE_FD_TO_DEL: + ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, + rule->location, NULL, false); + if (ret) + goto out; + hclge_fd_dec_rule_cnt(hdev, rule->location); + hclge_fd_free_node(hdev, rule); + break; + default: + break; + } + } + +out: + if (ret) + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); + + spin_unlock_bh(&hdev->fd_rule_lock); +} + +void hclge_sync_fd_table(struct hclge_dev *hdev) +{ + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return; + + if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) { + bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE; + + hclge_clear_fd_rules_in_list(hdev, clear_list); + } + + hclge_sync_fd_user_def_cfg(hdev, false); + + hclge_sync_fd_list(hdev, &hdev->fd_rule_list); +} + +void hclge_enable_fd(struct hnae3_handle *handle, bool enable) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + hdev->fd_en = enable; + + if (!enable) + set_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state); + else + hclge_restore_fd_entries(handle); + + hclge_task_schedule(hdev, 0); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h new file mode 100644 index 000000000000..2f66cc9c3c65 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2026 Hisilicon Limited. */ + +#ifndef __HCLGE_FD_H +#define __HCLGE_FD_H + +struct hnae3_handle; +struct hclge_dev; + +int hclge_init_fd_config(struct hclge_dev *hdev); +int hclge_add_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd); +int hclge_del_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd); +void hclge_del_all_fd_entries(struct hclge_dev *hdev); +int hclge_restore_fd_entries(struct hnae3_handle *handle); +int hclge_get_fd_rule_cnt(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd); +int hclge_get_fd_rule_info(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd); +int hclge_get_all_rules(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd, u32 *rule_locs); +void hclge_enable_fd(struct hnae3_handle *handle, bool enable); +int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id, + u16 flow_id, struct flow_keys *fkeys); +int hclge_add_cls_flower(struct hnae3_handle *handle, + struct flow_cls_offload *cls_flower); +int hclge_del_cls_flower(struct hnae3_handle *handle, + struct flow_cls_offload *cls_flower); +bool hclge_is_cls_flower_active(struct hnae3_handle *handle); +int hclge_clear_arfs_rules(struct hclge_dev *hdev); +void hclge_sync_fd_table(struct hclge_dev *hdev); +void hclge_rfs_filter_expire(struct hclge_dev *hdev); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index dd4045c773d4..2f1984930da2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -16,9 +16,9 @@ #include #include - #include "hclge_cmd.h" #include "hclge_dcb.h" +#include "hclge_fd.h" #include "hclge_main.h" #include "hclge_mbx.h" #include "hclge_mdio.h" @@ -52,8 +52,6 @@ static int hclge_init_vlan_config(struct hclge_dev *hdev); static void hclge_sync_vlan_filter(struct hclge_dev *hdev); static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev); static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle); -static void hclge_rfs_filter_expire(struct hclge_dev *hdev); -static int hclge_clear_arfs_rules(struct hclge_dev *hdev); static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev, unsigned long *addr); static int hclge_set_default_loopback(struct hclge_dev *hdev); @@ -61,7 +59,6 @@ static int hclge_set_default_loopback(struct hclge_dev *hdev); static void hclge_sync_mac_table(struct hclge_dev *hdev); static void hclge_restore_hw_table(struct hclge_dev *hdev); static void hclge_sync_promisc_mode(struct hclge_dev *hdev); -static void hclge_sync_fd_table(struct hclge_dev *hdev); static void hclge_update_fec_stats(struct hclge_dev *hdev); static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret, int wait_cnt); @@ -72,16 +69,16 @@ static struct hnae3_ae_algo ae_algo; static struct workqueue_struct *hclge_wq; static const struct pci_device_id ae_algo_pci_tbl[] = { - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA), 0}, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC) }, + { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl); @@ -311,76 +308,6 @@ static const struct hclge_mac_mgr_tbl_entry_cmd hclge_mgr_table[] = { }, }; -static const struct key_info meta_data_key_info[] = { - { PACKET_TYPE_ID, 6 }, - { IP_FRAGEMENT, 1 }, - { ROCE_TYPE, 1 }, - { NEXT_KEY, 5 }, - { VLAN_NUMBER, 2 }, - { SRC_VPORT, 12 }, - { DST_VPORT, 12 }, - { TUNNEL_PACKET, 1 }, -}; - -static const struct key_info tuple_key_info[] = { - { OUTER_DST_MAC, 48, KEY_OPT_MAC, -1, -1 }, - { OUTER_SRC_MAC, 48, KEY_OPT_MAC, -1, -1 }, - { OUTER_VLAN_TAG_FST, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_ETH_TYPE, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_L2_RSV, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_IP_TOS, 8, KEY_OPT_U8, -1, -1 }, - { OUTER_IP_PROTO, 8, KEY_OPT_U8, -1, -1 }, - { OUTER_SRC_IP, 32, KEY_OPT_IP, -1, -1 }, - { OUTER_DST_IP, 32, KEY_OPT_IP, -1, -1 }, - { OUTER_L3_RSV, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 }, - { OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 }, - { OUTER_TUN_VNI, 24, KEY_OPT_VNI, -1, -1 }, - { OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 }, - { INNER_DST_MAC, 48, KEY_OPT_MAC, - offsetof(struct hclge_fd_rule, tuples.dst_mac), - offsetof(struct hclge_fd_rule, tuples_mask.dst_mac) }, - { INNER_SRC_MAC, 48, KEY_OPT_MAC, - offsetof(struct hclge_fd_rule, tuples.src_mac), - offsetof(struct hclge_fd_rule, tuples_mask.src_mac) }, - { INNER_VLAN_TAG_FST, 16, KEY_OPT_LE16, - offsetof(struct hclge_fd_rule, tuples.vlan_tag1), - offsetof(struct hclge_fd_rule, tuples_mask.vlan_tag1) }, - { INNER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 }, - { INNER_ETH_TYPE, 16, KEY_OPT_LE16, - offsetof(struct hclge_fd_rule, tuples.ether_proto), - offsetof(struct hclge_fd_rule, tuples_mask.ether_proto) }, - { INNER_L2_RSV, 16, KEY_OPT_LE16, - offsetof(struct hclge_fd_rule, tuples.l2_user_def), - offsetof(struct hclge_fd_rule, tuples_mask.l2_user_def) }, - { INNER_IP_TOS, 8, KEY_OPT_U8, - offsetof(struct hclge_fd_rule, tuples.ip_tos), - offsetof(struct hclge_fd_rule, tuples_mask.ip_tos) }, - { INNER_IP_PROTO, 8, KEY_OPT_U8, - offsetof(struct hclge_fd_rule, tuples.ip_proto), - offsetof(struct hclge_fd_rule, tuples_mask.ip_proto) }, - { INNER_SRC_IP, 32, KEY_OPT_IP, - offsetof(struct hclge_fd_rule, tuples.src_ip), - offsetof(struct hclge_fd_rule, tuples_mask.src_ip) }, - { INNER_DST_IP, 32, KEY_OPT_IP, - offsetof(struct hclge_fd_rule, tuples.dst_ip), - offsetof(struct hclge_fd_rule, tuples_mask.dst_ip) }, - { INNER_L3_RSV, 16, KEY_OPT_LE16, - offsetof(struct hclge_fd_rule, tuples.l3_user_def), - offsetof(struct hclge_fd_rule, tuples_mask.l3_user_def) }, - { INNER_SRC_PORT, 16, KEY_OPT_LE16, - offsetof(struct hclge_fd_rule, tuples.src_port), - offsetof(struct hclge_fd_rule, tuples_mask.src_port) }, - { INNER_DST_PORT, 16, KEY_OPT_LE16, - offsetof(struct hclge_fd_rule, tuples.dst_port), - offsetof(struct hclge_fd_rule, tuples_mask.dst_port) }, - { INNER_L4_RSV, 32, KEY_OPT_LE32, - offsetof(struct hclge_fd_rule, tuples.l4_user_def), - offsetof(struct hclge_fd_rule, tuples_mask.l4_user_def) }, -}; - /** * hclge_cmd_send - send command to command queue * @hw: pointer to the hw struct @@ -5181,2366 +5108,6 @@ static void hclge_request_update_promisc_mode(struct hnae3_handle *handle) set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state); } -static void hclge_sync_fd_state(struct hclge_dev *hdev) -{ - if (hlist_empty(&hdev->fd_rule_list)) - hdev->fd_active_type = HCLGE_FD_RULE_NONE; -} - -static void hclge_fd_inc_rule_cnt(struct hclge_dev *hdev, u16 location) -{ - if (!test_bit(location, hdev->fd_bmap)) { - set_bit(location, hdev->fd_bmap); - hdev->hclge_fd_rule_num++; - } -} - -static void hclge_fd_dec_rule_cnt(struct hclge_dev *hdev, u16 location) -{ - if (test_bit(location, hdev->fd_bmap)) { - clear_bit(location, hdev->fd_bmap); - hdev->hclge_fd_rule_num--; - } -} - -static void hclge_fd_free_node(struct hclge_dev *hdev, - struct hclge_fd_rule *rule) -{ - hlist_del(&rule->rule_node); - kfree(rule); - hclge_sync_fd_state(hdev); -} - -static void hclge_update_fd_rule_node(struct hclge_dev *hdev, - struct hclge_fd_rule *old_rule, - struct hclge_fd_rule *new_rule, - enum HCLGE_FD_NODE_STATE state) -{ - switch (state) { - case HCLGE_FD_TO_ADD: - case HCLGE_FD_ACTIVE: - /* 1) if the new state is TO_ADD, just replace the old rule - * with the same location, no matter its state, because the - * new rule will be configured to the hardware. - * 2) if the new state is ACTIVE, it means the new rule - * has been configured to the hardware, so just replace - * the old rule node with the same location. - * 3) for it doesn't add a new node to the list, so it's - * unnecessary to update the rule number and fd_bmap. - */ - new_rule->rule_node.next = old_rule->rule_node.next; - new_rule->rule_node.pprev = old_rule->rule_node.pprev; - memcpy(old_rule, new_rule, sizeof(*old_rule)); - kfree(new_rule); - break; - case HCLGE_FD_DELETED: - hclge_fd_dec_rule_cnt(hdev, old_rule->location); - hclge_fd_free_node(hdev, old_rule); - break; - case HCLGE_FD_TO_DEL: - /* if new request is TO_DEL, and old rule is existent - * 1) the state of old rule is TO_DEL, we need do nothing, - * because we delete rule by location, other rule content - * is unncessary. - * 2) the state of old rule is ACTIVE, we need to change its - * state to TO_DEL, so the rule will be deleted when periodic - * task being scheduled. - * 3) the state of old rule is TO_ADD, it means the rule hasn't - * been added to hardware, so we just delete the rule node from - * fd_rule_list directly. - */ - if (old_rule->state == HCLGE_FD_TO_ADD) { - hclge_fd_dec_rule_cnt(hdev, old_rule->location); - hclge_fd_free_node(hdev, old_rule); - return; - } - old_rule->state = HCLGE_FD_TO_DEL; - break; - } -} - -static struct hclge_fd_rule *hclge_find_fd_rule(struct hlist_head *hlist, - u16 location, - struct hclge_fd_rule **parent) -{ - struct hclge_fd_rule *rule; - struct hlist_node *node; - - hlist_for_each_entry_safe(rule, node, hlist, rule_node) { - if (rule->location == location) - return rule; - else if (rule->location > location) - return NULL; - /* record the parent node, use to keep the nodes in fd_rule_list - * in ascend order. - */ - *parent = rule; - } - - return NULL; -} - -/* insert fd rule node in ascend order according to rule->location */ -static void hclge_fd_insert_rule_node(struct hlist_head *hlist, - struct hclge_fd_rule *rule, - struct hclge_fd_rule *parent) -{ - INIT_HLIST_NODE(&rule->rule_node); - - if (parent) - hlist_add_behind(&rule->rule_node, &parent->rule_node); - else - hlist_add_head(&rule->rule_node, hlist); -} - -static int hclge_fd_set_user_def_cmd(struct hclge_dev *hdev, - struct hclge_fd_user_def_cfg *cfg) -{ - struct hclge_fd_user_def_cfg_cmd *req; - struct hclge_desc desc; - u16 data = 0; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false); - - req = (struct hclge_fd_user_def_cfg_cmd *)desc.data; - - hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[0].ref_cnt > 0); - hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M, - HCLGE_FD_USER_DEF_OFT_S, cfg[0].offset); - req->ol2_cfg = cpu_to_le16(data); - - data = 0; - hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[1].ref_cnt > 0); - hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M, - HCLGE_FD_USER_DEF_OFT_S, cfg[1].offset); - req->ol3_cfg = cpu_to_le16(data); - - data = 0; - hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[2].ref_cnt > 0); - hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M, - HCLGE_FD_USER_DEF_OFT_S, cfg[2].offset); - req->ol4_cfg = cpu_to_le16(data); - - ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, - "failed to set fd user def data, ret= %d\n", ret); - return ret; -} - -static void hclge_sync_fd_user_def_cfg(struct hclge_dev *hdev, bool locked) -{ - int ret; - - if (!test_and_clear_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state)) - return; - - if (!locked) - spin_lock_bh(&hdev->fd_rule_lock); - - ret = hclge_fd_set_user_def_cmd(hdev, hdev->fd_cfg.user_def_cfg); - if (ret) - set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); - - if (!locked) - spin_unlock_bh(&hdev->fd_rule_lock); -} - -static int hclge_fd_check_user_def_refcnt(struct hclge_dev *hdev, - struct hclge_fd_rule *rule) -{ - struct hlist_head *hlist = &hdev->fd_rule_list; - struct hclge_fd_rule *fd_rule, *parent = NULL; - struct hclge_fd_user_def_info *info, *old_info; - struct hclge_fd_user_def_cfg *cfg; - - if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE || - rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE) - return 0; - - /* for valid layer is start from 1, so need minus 1 to get the cfg */ - cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; - info = &rule->ep.user_def; - - if (!cfg->ref_cnt || cfg->offset == info->offset) - return 0; - - if (cfg->ref_cnt > 1) - goto error; - - fd_rule = hclge_find_fd_rule(hlist, rule->location, &parent); - if (fd_rule) { - old_info = &fd_rule->ep.user_def; - if (info->layer == old_info->layer) - return 0; - } - -error: - dev_err(&hdev->pdev->dev, - "No available offset for layer%d fd rule, each layer only support one user def offset.\n", - info->layer + 1); - return -ENOSPC; -} - -static void hclge_fd_inc_user_def_refcnt(struct hclge_dev *hdev, - struct hclge_fd_rule *rule) -{ - struct hclge_fd_user_def_cfg *cfg; - - if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE || - rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE) - return; - - cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; - if (!cfg->ref_cnt) { - cfg->offset = rule->ep.user_def.offset; - set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); - } - cfg->ref_cnt++; -} - -static void hclge_fd_dec_user_def_refcnt(struct hclge_dev *hdev, - struct hclge_fd_rule *rule) -{ - struct hclge_fd_user_def_cfg *cfg; - - if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE || - rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE) - return; - - cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; - if (!cfg->ref_cnt) - return; - - cfg->ref_cnt--; - if (!cfg->ref_cnt) { - cfg->offset = 0; - set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); - } -} - -static void hclge_update_fd_list(struct hclge_dev *hdev, - enum HCLGE_FD_NODE_STATE state, u16 location, - struct hclge_fd_rule *new_rule) -{ - struct hlist_head *hlist = &hdev->fd_rule_list; - struct hclge_fd_rule *fd_rule, *parent = NULL; - - fd_rule = hclge_find_fd_rule(hlist, location, &parent); - if (fd_rule) { - hclge_fd_dec_user_def_refcnt(hdev, fd_rule); - if (state == HCLGE_FD_ACTIVE) - hclge_fd_inc_user_def_refcnt(hdev, new_rule); - hclge_sync_fd_user_def_cfg(hdev, true); - - hclge_update_fd_rule_node(hdev, fd_rule, new_rule, state); - return; - } - - /* it's unlikely to fail here, because we have checked the rule - * exist before. - */ - if (unlikely(state == HCLGE_FD_TO_DEL || state == HCLGE_FD_DELETED)) { - dev_warn(&hdev->pdev->dev, - "failed to delete fd rule %u, it's inexistent\n", - location); - return; - } - - hclge_fd_inc_user_def_refcnt(hdev, new_rule); - hclge_sync_fd_user_def_cfg(hdev, true); - - hclge_fd_insert_rule_node(hlist, new_rule, parent); - hclge_fd_inc_rule_cnt(hdev, new_rule->location); - - if (state == HCLGE_FD_TO_ADD) { - set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); - hclge_task_schedule(hdev, 0); - } -} - -static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode) -{ - struct hclge_get_fd_mode_cmd *req; - struct hclge_desc desc; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true); - - req = (struct hclge_get_fd_mode_cmd *)desc.data; - - ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) { - dev_err(&hdev->pdev->dev, "get fd mode fail, ret=%d\n", ret); - return ret; - } - - *fd_mode = req->mode; - - return ret; -} - -static int hclge_get_fd_allocation(struct hclge_dev *hdev, - u32 *stage1_entry_num, - u32 *stage2_entry_num, - u16 *stage1_counter_num, - u16 *stage2_counter_num) -{ - struct hclge_get_fd_allocation_cmd *req; - struct hclge_desc desc; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_GET_ALLOCATION, true); - - req = (struct hclge_get_fd_allocation_cmd *)desc.data; - - ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) { - dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=%d\n", - ret); - return ret; - } - - *stage1_entry_num = le32_to_cpu(req->stage1_entry_num); - *stage2_entry_num = le32_to_cpu(req->stage2_entry_num); - *stage1_counter_num = le16_to_cpu(req->stage1_counter_num); - *stage2_counter_num = le16_to_cpu(req->stage2_counter_num); - - return ret; -} - -static int hclge_set_fd_key_config(struct hclge_dev *hdev, - enum HCLGE_FD_STAGE stage_num) -{ - struct hclge_set_fd_key_config_cmd *req; - struct hclge_fd_key_cfg *stage; - struct hclge_desc desc; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_KEY_CONFIG, false); - - req = (struct hclge_set_fd_key_config_cmd *)desc.data; - stage = &hdev->fd_cfg.key_cfg[stage_num]; - req->stage = stage_num; - req->key_select = stage->key_sel; - req->inner_sipv6_word_en = stage->inner_sipv6_word_en; - req->inner_dipv6_word_en = stage->inner_dipv6_word_en; - req->outer_sipv6_word_en = stage->outer_sipv6_word_en; - req->outer_dipv6_word_en = stage->outer_dipv6_word_en; - req->tuple_mask = cpu_to_le32(~stage->tuple_active); - req->meta_data_mask = cpu_to_le32(~stage->meta_data_active); - - ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, "set fd key fail, ret=%d\n", ret); - - return ret; -} - -static void hclge_fd_disable_user_def(struct hclge_dev *hdev) -{ - struct hclge_fd_user_def_cfg *cfg = hdev->fd_cfg.user_def_cfg; - - spin_lock_bh(&hdev->fd_rule_lock); - memset(cfg, 0, sizeof(hdev->fd_cfg.user_def_cfg)); - spin_unlock_bh(&hdev->fd_rule_lock); - - hclge_fd_set_user_def_cmd(hdev, cfg); -} - -static int hclge_init_fd_config(struct hclge_dev *hdev) -{ -#define LOW_2_WORDS 0x03 - struct hclge_fd_key_cfg *key_cfg; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return 0; - - ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode); - if (ret) - return ret; - - switch (hdev->fd_cfg.fd_mode) { - case HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1: - hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH; - break; - case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1: - hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH / 2; - break; - default: - dev_err(&hdev->pdev->dev, - "Unsupported flow director mode %u\n", - hdev->fd_cfg.fd_mode); - return -EOPNOTSUPP; - } - - key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1]; - key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE; - key_cfg->inner_sipv6_word_en = LOW_2_WORDS; - key_cfg->inner_dipv6_word_en = LOW_2_WORDS; - key_cfg->outer_sipv6_word_en = 0; - key_cfg->outer_dipv6_word_en = 0; - - key_cfg->tuple_active = BIT(INNER_VLAN_TAG_FST) | BIT(INNER_ETH_TYPE) | - BIT(INNER_IP_PROTO) | BIT(INNER_IP_TOS) | - BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) | - BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); - - /* If use max 400bit key, we can support tuples for ether type */ - if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) { - key_cfg->tuple_active |= - BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC); - if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3) - key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES; - } - - /* roce_type is used to filter roce frames - * dst_vport is used to specify the rule - */ - key_cfg->meta_data_active = BIT(ROCE_TYPE) | BIT(DST_VPORT); - - ret = hclge_get_fd_allocation(hdev, - &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1], - &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_2], - &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1], - &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_2]); - if (ret) - return ret; - - return hclge_set_fd_key_config(hdev, HCLGE_FD_STAGE_1); -} - -static int hclge_fd_tcam_config(struct hclge_dev *hdev, u8 stage, bool sel_x, - int loc, u8 *key, bool is_add) -{ - struct hclge_fd_tcam_config_1_cmd *req1; - struct hclge_fd_tcam_config_2_cmd *req2; - struct hclge_fd_tcam_config_3_cmd *req3; - struct hclge_desc desc[3]; - int ret; - - hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, false); - desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); - hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false); - desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); - hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false); - - req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data; - req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data; - req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data; - - req1->stage = stage; - req1->xy_sel = sel_x ? 1 : 0; - hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0); - req1->index = cpu_to_le32(loc); - req1->entry_vld = sel_x ? is_add : 0; - - if (key) { - memcpy(req1->tcam_data, &key[0], sizeof(req1->tcam_data)); - memcpy(req2->tcam_data, &key[sizeof(req1->tcam_data)], - sizeof(req2->tcam_data)); - memcpy(req3->tcam_data, &key[sizeof(req1->tcam_data) + - sizeof(req2->tcam_data)], sizeof(req3->tcam_data)); - } - - ret = hclge_cmd_send(&hdev->hw, desc, 3); - if (ret) - dev_err(&hdev->pdev->dev, - "config tcam key fail, ret=%d\n", - ret); - - return ret; -} - -static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc, - struct hclge_fd_ad_data *action) -{ - struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); - struct hclge_fd_ad_config_cmd *req; - struct hclge_desc desc; - u64 ad_data = 0; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false); - - req = (struct hclge_fd_ad_config_cmd *)desc.data; - req->index = cpu_to_le32(loc); - req->stage = stage; - - hnae3_set_bit(ad_data, HCLGE_FD_AD_WR_RULE_ID_B, - action->write_rule_id_to_bd); - hnae3_set_field(ad_data, HCLGE_FD_AD_RULE_ID_M, HCLGE_FD_AD_RULE_ID_S, - action->rule_id); - if (test_bit(HNAE3_DEV_SUPPORT_FD_FORWARD_TC_B, ae_dev->caps)) { - hnae3_set_bit(ad_data, HCLGE_FD_AD_TC_OVRD_B, - action->override_tc); - hnae3_set_field(ad_data, HCLGE_FD_AD_TC_SIZE_M, - HCLGE_FD_AD_TC_SIZE_S, (u32)action->tc_size); - } - hnae3_set_bit(ad_data, HCLGE_FD_AD_QID_H_B, - action->queue_id >= HCLGE_TQP_MAX_SIZE_DEV_V2 ? 1 : 0); - hnae3_set_bit(ad_data, HCLGE_FD_AD_COUNTER_NUM_H_B, - action->counter_id >= HCLGE_FD_COUNTER_MAX_SIZE_DEV_V2 ? - 1 : 0); - ad_data <<= 32; - hnae3_set_bit(ad_data, HCLGE_FD_AD_DROP_B, action->drop_packet); - hnae3_set_bit(ad_data, HCLGE_FD_AD_DIRECT_QID_B, - action->forward_to_direct_queue); - hnae3_set_field(ad_data, HCLGE_FD_AD_QID_L_M, HCLGE_FD_AD_QID_L_S, - action->queue_id); - hnae3_set_bit(ad_data, HCLGE_FD_AD_USE_COUNTER_B, action->use_counter); - hnae3_set_field(ad_data, HCLGE_FD_AD_COUNTER_NUM_L_M, - HCLGE_FD_AD_COUNTER_NUM_L_S, action->counter_id); - hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage); - hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S, - action->next_input_key); - - req->ad_data = cpu_to_le64(ad_data); - ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, "fd ad config fail, ret=%d\n", ret); - - return ret; -} - -static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y, - struct hclge_fd_rule *rule) -{ - int offset, moffset, ip_offset; - enum HCLGE_FD_KEY_OPT key_opt; - u16 tmp_x_s, tmp_y_s; - u32 tmp_x_l, tmp_y_l; - u8 *p = (u8 *)rule; - int i; - - if (rule->unused_tuple & BIT(tuple_bit)) - return true; - - key_opt = tuple_key_info[tuple_bit].key_opt; - offset = tuple_key_info[tuple_bit].offset; - moffset = tuple_key_info[tuple_bit].moffset; - - switch (key_opt) { - case KEY_OPT_U8: - calc_x(*key_x, p[offset], p[moffset]); - calc_y(*key_y, p[offset], p[moffset]); - - return true; - case KEY_OPT_LE16: - calc_x(tmp_x_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset])); - calc_y(tmp_y_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset])); - *(__le16 *)key_x = cpu_to_le16(tmp_x_s); - *(__le16 *)key_y = cpu_to_le16(tmp_y_s); - - return true; - case KEY_OPT_LE32: - calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset])); - calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset])); - *(__le32 *)key_x = cpu_to_le32(tmp_x_l); - *(__le32 *)key_y = cpu_to_le32(tmp_y_l); - - return true; - case KEY_OPT_MAC: - for (i = 0; i < ETH_ALEN; i++) { - calc_x(key_x[ETH_ALEN - 1 - i], p[offset + i], - p[moffset + i]); - calc_y(key_y[ETH_ALEN - 1 - i], p[offset + i], - p[moffset + i]); - } - - return true; - case KEY_OPT_IP: - ip_offset = IPV4_INDEX * sizeof(u32); - calc_x(tmp_x_l, *(u32 *)(&p[offset + ip_offset]), - *(u32 *)(&p[moffset + ip_offset])); - calc_y(tmp_y_l, *(u32 *)(&p[offset + ip_offset]), - *(u32 *)(&p[moffset + ip_offset])); - *(__le32 *)key_x = cpu_to_le32(tmp_x_l); - *(__le32 *)key_y = cpu_to_le32(tmp_y_l); - - return true; - default: - return false; - } -} - -static u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id, - u8 vf_id, u8 network_port_id) -{ - u32 port_number = 0; - - if (port_type == HOST_PORT) { - hnae3_set_field(port_number, HCLGE_PF_ID_M, HCLGE_PF_ID_S, - pf_id); - hnae3_set_field(port_number, HCLGE_VF_ID_M, HCLGE_VF_ID_S, - vf_id); - hnae3_set_bit(port_number, HCLGE_PORT_TYPE_B, HOST_PORT); - } else { - hnae3_set_field(port_number, HCLGE_NETWORK_PORT_ID_M, - HCLGE_NETWORK_PORT_ID_S, network_port_id); - hnae3_set_bit(port_number, HCLGE_PORT_TYPE_B, NETWORK_PORT); - } - - return port_number; -} - -static void hclge_fd_convert_meta_data(struct hclge_fd_key_cfg *key_cfg, - __le32 *key_x, __le32 *key_y, - struct hclge_fd_rule *rule) -{ - u32 tuple_bit, meta_data = 0, tmp_x, tmp_y, port_number; - u8 cur_pos = 0, tuple_size, shift_bits; - unsigned int i; - - for (i = 0; i < MAX_META_DATA; i++) { - tuple_size = meta_data_key_info[i].key_length; - tuple_bit = key_cfg->meta_data_active & BIT(i); - - switch (tuple_bit) { - case BIT(ROCE_TYPE): - hnae3_set_bit(meta_data, cur_pos, NIC_PACKET); - cur_pos += tuple_size; - break; - case BIT(DST_VPORT): - port_number = hclge_get_port_number(HOST_PORT, 0, - rule->vf_id, 0); - hnae3_set_field(meta_data, - GENMASK(cur_pos + tuple_size, cur_pos), - cur_pos, port_number); - cur_pos += tuple_size; - break; - default: - break; - } - } - - calc_x(tmp_x, meta_data, 0xFFFFFFFF); - calc_y(tmp_y, meta_data, 0xFFFFFFFF); - shift_bits = sizeof(meta_data) * 8 - cur_pos; - - *key_x = cpu_to_le32(tmp_x << shift_bits); - *key_y = cpu_to_le32(tmp_y << shift_bits); -} - -/* A complete key is combined with meta data key and tuple key. - * Meta data key is stored at the MSB region, and tuple key is stored at - * the LSB region, unused bits will be filled 0. - */ -static int hclge_config_key(struct hclge_dev *hdev, u8 stage, - struct hclge_fd_rule *rule) -{ - struct hclge_fd_key_cfg *key_cfg = &hdev->fd_cfg.key_cfg[stage]; - u8 key_x[MAX_KEY_BYTES], key_y[MAX_KEY_BYTES]; - u8 *cur_key_x, *cur_key_y; - u8 meta_data_region; - u8 tuple_size; - int ret; - u32 i; - - memset(key_x, 0, sizeof(key_x)); - memset(key_y, 0, sizeof(key_y)); - cur_key_x = key_x; - cur_key_y = key_y; - - for (i = 0; i < MAX_TUPLE; i++) { - bool tuple_valid; - - tuple_size = tuple_key_info[i].key_length / 8; - if (!(key_cfg->tuple_active & BIT(i))) - continue; - - tuple_valid = hclge_fd_convert_tuple(i, cur_key_x, - cur_key_y, rule); - if (tuple_valid) { - cur_key_x += tuple_size; - cur_key_y += tuple_size; - } - } - - meta_data_region = hdev->fd_cfg.max_key_length / 8 - - MAX_META_DATA_LENGTH / 8; - - hclge_fd_convert_meta_data(key_cfg, - (__le32 *)(key_x + meta_data_region), - (__le32 *)(key_y + meta_data_region), - rule); - - ret = hclge_fd_tcam_config(hdev, stage, false, rule->location, key_y, - true); - if (ret) { - dev_err(&hdev->pdev->dev, - "fd key_y config fail, loc=%u, ret=%d\n", - rule->queue_id, ret); - return ret; - } - - ret = hclge_fd_tcam_config(hdev, stage, true, rule->location, key_x, - true); - if (ret) - dev_err(&hdev->pdev->dev, - "fd key_x config fail, loc=%u, ret=%d\n", - rule->queue_id, ret); - return ret; -} - -static int hclge_config_action(struct hclge_dev *hdev, u8 stage, - struct hclge_fd_rule *rule) -{ - struct hclge_vport *vport = hdev->vport; - struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; - struct hclge_fd_ad_data ad_data; - - memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data)); - ad_data.ad_id = rule->location; - - if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) { - ad_data.drop_packet = true; - } else if (rule->action == HCLGE_FD_ACTION_SELECT_TC) { - ad_data.override_tc = true; - ad_data.queue_id = - kinfo->tc_info.tqp_offset[rule->cls_flower.tc]; - ad_data.tc_size = - ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]); - } else { - ad_data.forward_to_direct_queue = true; - ad_data.queue_id = rule->queue_id; - } - - if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) { - ad_data.use_counter = true; - ad_data.counter_id = rule->vf_id % - hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]; - } else { - ad_data.use_counter = false; - ad_data.counter_id = 0; - } - - ad_data.use_next_stage = false; - ad_data.next_input_key = 0; - - ad_data.write_rule_id_to_bd = true; - ad_data.rule_id = rule->location; - - return hclge_fd_ad_config(hdev, stage, ad_data.ad_id, &ad_data); -} - -static int hclge_fd_check_tcpip4_tuple(struct ethtool_tcpip4_spec *spec, - u32 *unused_tuple) -{ - if (!spec || !unused_tuple) - return -EINVAL; - - *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC); - - if (!spec->ip4src) - *unused_tuple |= BIT(INNER_SRC_IP); - - if (!spec->ip4dst) - *unused_tuple |= BIT(INNER_DST_IP); - - if (!spec->psrc) - *unused_tuple |= BIT(INNER_SRC_PORT); - - if (!spec->pdst) - *unused_tuple |= BIT(INNER_DST_PORT); - - if (!spec->tos) - *unused_tuple |= BIT(INNER_IP_TOS); - - return 0; -} - -static int hclge_fd_check_ip4_tuple(struct ethtool_usrip4_spec *spec, - u32 *unused_tuple) -{ - if (!spec || !unused_tuple) - return -EINVAL; - - *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | - BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); - - if (!spec->ip4src) - *unused_tuple |= BIT(INNER_SRC_IP); - - if (!spec->ip4dst) - *unused_tuple |= BIT(INNER_DST_IP); - - if (!spec->tos) - *unused_tuple |= BIT(INNER_IP_TOS); - - if (!spec->proto) - *unused_tuple |= BIT(INNER_IP_PROTO); - - if (spec->l4_4_bytes) - return -EOPNOTSUPP; - - if (spec->ip_ver != ETH_RX_NFC_IP4) - return -EOPNOTSUPP; - - return 0; -} - -static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec, - u32 *unused_tuple) -{ - if (!spec || !unused_tuple) - return -EINVAL; - - *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC); - - /* check whether src/dst ip address used */ - if (ipv6_addr_any((struct in6_addr *)spec->ip6src)) - *unused_tuple |= BIT(INNER_SRC_IP); - - if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) - *unused_tuple |= BIT(INNER_DST_IP); - - if (!spec->psrc) - *unused_tuple |= BIT(INNER_SRC_PORT); - - if (!spec->pdst) - *unused_tuple |= BIT(INNER_DST_PORT); - - if (!spec->tclass) - *unused_tuple |= BIT(INNER_IP_TOS); - - return 0; -} - -static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec, - u32 *unused_tuple) -{ - if (!spec || !unused_tuple) - return -EINVAL; - - *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | - BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); - - /* check whether src/dst ip address used */ - if (ipv6_addr_any((struct in6_addr *)spec->ip6src)) - *unused_tuple |= BIT(INNER_SRC_IP); - - if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) - *unused_tuple |= BIT(INNER_DST_IP); - - if (!spec->l4_proto) - *unused_tuple |= BIT(INNER_IP_PROTO); - - if (!spec->tclass) - *unused_tuple |= BIT(INNER_IP_TOS); - - if (spec->l4_4_bytes) - return -EOPNOTSUPP; - - return 0; -} - -static int hclge_fd_check_ether_tuple(struct ethhdr *spec, u32 *unused_tuple) -{ - if (!spec || !unused_tuple) - return -EINVAL; - - *unused_tuple |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) | - BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) | - BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO); - - if (is_zero_ether_addr(spec->h_source)) - *unused_tuple |= BIT(INNER_SRC_MAC); - - if (is_zero_ether_addr(spec->h_dest)) - *unused_tuple |= BIT(INNER_DST_MAC); - - if (!spec->h_proto) - *unused_tuple |= BIT(INNER_ETH_TYPE); - - return 0; -} - -static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev, - struct ethtool_rx_flow_spec *fs, - u32 *unused_tuple) -{ - if (fs->flow_type & FLOW_EXT) { - if (fs->h_ext.vlan_etype) { - dev_err(&hdev->pdev->dev, "vlan-etype is not supported!\n"); - return -EOPNOTSUPP; - } - - if (!fs->h_ext.vlan_tci) - *unused_tuple |= BIT(INNER_VLAN_TAG_FST); - - if (fs->m_ext.vlan_tci && - be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) { - dev_err(&hdev->pdev->dev, - "failed to config vlan_tci, invalid vlan_tci: %u, max is %d.\n", - ntohs(fs->h_ext.vlan_tci), VLAN_N_VID - 1); - return -EINVAL; - } - } else { - *unused_tuple |= BIT(INNER_VLAN_TAG_FST); - } - - if (fs->flow_type & FLOW_MAC_EXT) { - if (hdev->fd_cfg.fd_mode != - HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) { - dev_err(&hdev->pdev->dev, - "FLOW_MAC_EXT is not supported in current fd mode!\n"); - return -EOPNOTSUPP; - } - - if (is_zero_ether_addr(fs->h_ext.h_dest)) - *unused_tuple |= BIT(INNER_DST_MAC); - else - *unused_tuple &= ~BIT(INNER_DST_MAC); - } - - return 0; -} - -static int hclge_fd_get_user_def_layer(u32 flow_type, u32 *unused_tuple, - struct hclge_fd_user_def_info *info) -{ - switch (flow_type) { - case ETHER_FLOW: - info->layer = HCLGE_FD_USER_DEF_L2; - *unused_tuple &= ~BIT(INNER_L2_RSV); - break; - case IP_USER_FLOW: - case IPV6_USER_FLOW: - info->layer = HCLGE_FD_USER_DEF_L3; - *unused_tuple &= ~BIT(INNER_L3_RSV); - break; - case TCP_V4_FLOW: - case UDP_V4_FLOW: - case TCP_V6_FLOW: - case UDP_V6_FLOW: - info->layer = HCLGE_FD_USER_DEF_L4; - *unused_tuple &= ~BIT(INNER_L4_RSV); - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *fs) -{ - return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) == 0; -} - -static int hclge_fd_parse_user_def_field(struct hclge_dev *hdev, - struct ethtool_rx_flow_spec *fs, - u32 *unused_tuple, - struct hclge_fd_user_def_info *info) -{ - u32 tuple_active = hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active; - u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); - u16 data, offset, data_mask, offset_mask; - int ret; - - info->layer = HCLGE_FD_USER_DEF_NONE; - *unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES; - - if (!(fs->flow_type & FLOW_EXT) || hclge_fd_is_user_def_all_masked(fs)) - return 0; - - /* user-def data from ethtool is 64 bit value, the bit0~15 is used - * for data, and bit32~47 is used for offset. - */ - data = be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; - data_mask = be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; - offset = be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET; - offset_mask = be32_to_cpu(fs->m_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET; - - if (!(tuple_active & HCLGE_FD_TUPLE_USER_DEF_TUPLES)) { - dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n"); - return -EOPNOTSUPP; - } - - if (offset > HCLGE_FD_MAX_USER_DEF_OFFSET) { - dev_err(&hdev->pdev->dev, - "user-def offset[%u] should be no more than %u\n", - offset, HCLGE_FD_MAX_USER_DEF_OFFSET); - return -EINVAL; - } - - if (offset_mask != HCLGE_FD_USER_DEF_OFFSET_UNMASK) { - dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n"); - return -EINVAL; - } - - ret = hclge_fd_get_user_def_layer(flow_type, unused_tuple, info); - if (ret) { - dev_err(&hdev->pdev->dev, - "unsupported flow type for user-def bytes, ret = %d\n", - ret); - return ret; - } - - info->data = data; - info->data_mask = data_mask; - info->offset = offset; - - return 0; -} - -static int hclge_fd_check_spec(struct hclge_dev *hdev, - struct ethtool_rx_flow_spec *fs, - u32 *unused_tuple, - struct hclge_fd_user_def_info *info) -{ - u32 flow_type; - int ret; - - if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { - dev_err(&hdev->pdev->dev, - "failed to config fd rules, invalid rule location: %u, max is %u\n.", - fs->location, - hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1] - 1); - return -EINVAL; - } - - ret = hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info); - if (ret) - return ret; - - flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); - switch (flow_type) { - case SCTP_V4_FLOW: - case TCP_V4_FLOW: - case UDP_V4_FLOW: - ret = hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec, - unused_tuple); - break; - case IP_USER_FLOW: - ret = hclge_fd_check_ip4_tuple(&fs->h_u.usr_ip4_spec, - unused_tuple); - break; - case SCTP_V6_FLOW: - case TCP_V6_FLOW: - case UDP_V6_FLOW: - ret = hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec, - unused_tuple); - break; - case IPV6_USER_FLOW: - ret = hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec, - unused_tuple); - break; - case ETHER_FLOW: - if (hdev->fd_cfg.fd_mode != - HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) { - dev_err(&hdev->pdev->dev, - "ETHER_FLOW is not supported in current fd mode!\n"); - return -EOPNOTSUPP; - } - - ret = hclge_fd_check_ether_tuple(&fs->h_u.ether_spec, - unused_tuple); - break; - default: - dev_err(&hdev->pdev->dev, - "unsupported protocol type, protocol type = %#x\n", - flow_type); - return -EOPNOTSUPP; - } - - if (ret) { - dev_err(&hdev->pdev->dev, - "failed to check flow union tuple, ret = %d\n", - ret); - return ret; - } - - return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple); -} - -static void hclge_fd_get_tcpip4_tuple(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule, u8 ip_proto) -{ - rule->tuples.src_ip[IPV4_INDEX] = - be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src); - rule->tuples_mask.src_ip[IPV4_INDEX] = - be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src); - - rule->tuples.dst_ip[IPV4_INDEX] = - be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst); - rule->tuples_mask.dst_ip[IPV4_INDEX] = - be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst); - - rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc); - rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc); - - rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst); - rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst); - - rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos; - rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos; - - rule->tuples.ether_proto = ETH_P_IP; - rule->tuples_mask.ether_proto = 0xFFFF; - - rule->tuples.ip_proto = ip_proto; - rule->tuples_mask.ip_proto = 0xFF; -} - -static void hclge_fd_get_ip4_tuple(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule) -{ - rule->tuples.src_ip[IPV4_INDEX] = - be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src); - rule->tuples_mask.src_ip[IPV4_INDEX] = - be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src); - - rule->tuples.dst_ip[IPV4_INDEX] = - be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst); - rule->tuples_mask.dst_ip[IPV4_INDEX] = - be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst); - - rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos; - rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos; - - rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto; - rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto; - - rule->tuples.ether_proto = ETH_P_IP; - rule->tuples_mask.ether_proto = 0xFFFF; -} - -static void hclge_fd_get_tcpip6_tuple(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule, u8 ip_proto) -{ - ipv6_addr_be32_to_cpu(rule->tuples.src_ip, - fs->h_u.tcp_ip6_spec.ip6src); - ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip, - fs->m_u.tcp_ip6_spec.ip6src); - - ipv6_addr_be32_to_cpu(rule->tuples.dst_ip, - fs->h_u.tcp_ip6_spec.ip6dst); - ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip, - fs->m_u.tcp_ip6_spec.ip6dst); - - rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc); - rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc); - - rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst); - rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst); - - rule->tuples.ether_proto = ETH_P_IPV6; - rule->tuples_mask.ether_proto = 0xFFFF; - - rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass; - rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass; - - rule->tuples.ip_proto = ip_proto; - rule->tuples_mask.ip_proto = 0xFF; -} - -static void hclge_fd_get_ip6_tuple(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule) -{ - ipv6_addr_be32_to_cpu(rule->tuples.src_ip, - fs->h_u.usr_ip6_spec.ip6src); - ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip, - fs->m_u.usr_ip6_spec.ip6src); - - ipv6_addr_be32_to_cpu(rule->tuples.dst_ip, - fs->h_u.usr_ip6_spec.ip6dst); - ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip, - fs->m_u.usr_ip6_spec.ip6dst); - - rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto; - rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto; - - rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass; - rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass; - - rule->tuples.ether_proto = ETH_P_IPV6; - rule->tuples_mask.ether_proto = 0xFFFF; -} - -static void hclge_fd_get_ether_tuple(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule) -{ - ether_addr_copy(rule->tuples.src_mac, fs->h_u.ether_spec.h_source); - ether_addr_copy(rule->tuples_mask.src_mac, fs->m_u.ether_spec.h_source); - - ether_addr_copy(rule->tuples.dst_mac, fs->h_u.ether_spec.h_dest); - ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_u.ether_spec.h_dest); - - rule->tuples.ether_proto = be16_to_cpu(fs->h_u.ether_spec.h_proto); - rule->tuples_mask.ether_proto = be16_to_cpu(fs->m_u.ether_spec.h_proto); -} - -static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *info, - struct hclge_fd_rule *rule) -{ - switch (info->layer) { - case HCLGE_FD_USER_DEF_L2: - rule->tuples.l2_user_def = info->data; - rule->tuples_mask.l2_user_def = info->data_mask; - break; - case HCLGE_FD_USER_DEF_L3: - rule->tuples.l3_user_def = info->data; - rule->tuples_mask.l3_user_def = info->data_mask; - break; - case HCLGE_FD_USER_DEF_L4: - rule->tuples.l4_user_def = (u32)info->data << 16; - rule->tuples_mask.l4_user_def = (u32)info->data_mask << 16; - break; - default: - break; - } - - rule->ep.user_def = *info; -} - -static int hclge_fd_get_tuple(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule, - struct hclge_fd_user_def_info *info) -{ - u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); - - switch (flow_type) { - case SCTP_V4_FLOW: - hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_SCTP); - break; - case TCP_V4_FLOW: - hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_TCP); - break; - case UDP_V4_FLOW: - hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_UDP); - break; - case IP_USER_FLOW: - hclge_fd_get_ip4_tuple(fs, rule); - break; - case SCTP_V6_FLOW: - hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_SCTP); - break; - case TCP_V6_FLOW: - hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_TCP); - break; - case UDP_V6_FLOW: - hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_UDP); - break; - case IPV6_USER_FLOW: - hclge_fd_get_ip6_tuple(fs, rule); - break; - case ETHER_FLOW: - hclge_fd_get_ether_tuple(fs, rule); - break; - default: - return -EOPNOTSUPP; - } - - if (fs->flow_type & FLOW_EXT) { - rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci); - rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci); - hclge_fd_get_user_def_tuple(info, rule); - } - - if (fs->flow_type & FLOW_MAC_EXT) { - ether_addr_copy(rule->tuples.dst_mac, fs->h_ext.h_dest); - ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_ext.h_dest); - } - - return 0; -} - -static int hclge_fd_config_rule(struct hclge_dev *hdev, - struct hclge_fd_rule *rule) -{ - int ret; - - ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule); - if (ret) - return ret; - - return hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule); -} - -static int hclge_add_fd_entry_common(struct hclge_dev *hdev, - struct hclge_fd_rule *rule) -{ - int ret; - - spin_lock_bh(&hdev->fd_rule_lock); - - if (hdev->fd_active_type != rule->rule_type && - (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE || - hdev->fd_active_type == HCLGE_FD_EP_ACTIVE)) { - dev_err(&hdev->pdev->dev, - "mode conflict(new type %d, active type %d), please delete existent rules first\n", - rule->rule_type, hdev->fd_active_type); - spin_unlock_bh(&hdev->fd_rule_lock); - return -EINVAL; - } - - ret = hclge_fd_check_user_def_refcnt(hdev, rule); - if (ret) - goto out; - - ret = hclge_clear_arfs_rules(hdev); - if (ret) - goto out; - - ret = hclge_fd_config_rule(hdev, rule); - if (ret) - goto out; - - rule->state = HCLGE_FD_ACTIVE; - hdev->fd_active_type = rule->rule_type; - hclge_update_fd_list(hdev, rule->state, rule->location, rule); - -out: - spin_unlock_bh(&hdev->fd_rule_lock); - return ret; -} - -static bool hclge_is_cls_flower_active(struct hnae3_handle *handle) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - - return hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE; -} - -static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_cookie, - u16 *vport_id, u8 *action, u16 *queue_id) -{ - struct hclge_vport *vport = hdev->vport; - - if (ring_cookie == RX_CLS_FLOW_DISC) { - *action = HCLGE_FD_ACTION_DROP_PACKET; - } else { - u32 ring = ethtool_get_flow_spec_ring(ring_cookie); - u8 vf = ethtool_get_flow_spec_ring_vf(ring_cookie); - u16 tqps; - - /* To keep consistent with user's configuration, minus 1 when - * printing 'vf', because vf id from ethtool is added 1 for vf. - */ - if (vf > hdev->num_req_vfs) { - dev_err(&hdev->pdev->dev, - "Error: vf id (%u) should be less than %u\n", - vf - 1U, hdev->num_req_vfs); - return -EINVAL; - } - - *vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id; - tqps = hdev->vport[vf].nic.kinfo.num_tqps; - - if (ring >= tqps) { - dev_err(&hdev->pdev->dev, - "Error: queue id (%u) > max tqp num (%u)\n", - ring, tqps - 1U); - return -EINVAL; - } - - *action = HCLGE_FD_ACTION_SELECT_QUEUE; - *queue_id = ring; - } - - return 0; -} - -static int hclge_add_fd_entry(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - struct hclge_fd_user_def_info info; - u16 dst_vport_id = 0, q_index = 0; - struct ethtool_rx_flow_spec *fs; - struct hclge_fd_rule *rule; - u32 unused = 0; - u8 action; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { - dev_err(&hdev->pdev->dev, - "flow table director is not supported\n"); - return -EOPNOTSUPP; - } - - if (!hdev->fd_en) { - dev_err(&hdev->pdev->dev, - "please enable flow director first\n"); - return -EOPNOTSUPP; - } - - fs = (struct ethtool_rx_flow_spec *)&cmd->fs; - - ret = hclge_fd_check_spec(hdev, fs, &unused, &info); - if (ret) - return ret; - - ret = hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id, - &action, &q_index); - if (ret) - return ret; - - rule = kzalloc_obj(*rule); - if (!rule) - return -ENOMEM; - - ret = hclge_fd_get_tuple(fs, rule, &info); - if (ret) { - kfree(rule); - return ret; - } - - rule->flow_type = fs->flow_type; - rule->location = fs->location; - rule->unused_tuple = unused; - rule->vf_id = dst_vport_id; - rule->queue_id = q_index; - rule->action = action; - rule->rule_type = HCLGE_FD_EP_ACTIVE; - - ret = hclge_add_fd_entry_common(hdev, rule); - if (ret) - kfree(rule); - - return ret; -} - -static int hclge_del_fd_entry(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - struct ethtool_rx_flow_spec *fs; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - fs = (struct ethtool_rx_flow_spec *)&cmd->fs; - - if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) - return -EINVAL; - - spin_lock_bh(&hdev->fd_rule_lock); - if (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE || - !test_bit(fs->location, hdev->fd_bmap)) { - dev_err(&hdev->pdev->dev, - "Delete fail, rule %u is inexistent\n", fs->location); - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOENT; - } - - ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, fs->location, - NULL, false); - if (ret) - goto out; - - hclge_update_fd_list(hdev, HCLGE_FD_DELETED, fs->location, NULL); - -out: - spin_unlock_bh(&hdev->fd_rule_lock); - return ret; -} - -static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev, - bool clear_list) -{ - struct hclge_fd_rule *rule; - struct hlist_node *node; - u16 location; - - spin_lock_bh(&hdev->fd_rule_lock); - - for_each_set_bit(location, hdev->fd_bmap, - hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) - hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location, - NULL, false); - - if (clear_list) { - hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, - rule_node) { - hlist_del(&rule->rule_node); - kfree(rule); - } - hdev->fd_active_type = HCLGE_FD_RULE_NONE; - hdev->hclge_fd_rule_num = 0; - bitmap_zero(hdev->fd_bmap, - hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); - } - - spin_unlock_bh(&hdev->fd_rule_lock); -} - -static void hclge_del_all_fd_entries(struct hclge_dev *hdev) -{ - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return; - - hclge_clear_fd_rules_in_list(hdev, true); - hclge_fd_disable_user_def(hdev); -} - -static int hclge_restore_fd_entries(struct hnae3_handle *handle) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - struct hclge_fd_rule *rule; - struct hlist_node *node; - - /* Return ok here, because reset error handling will check this - * return value. If error is returned here, the reset process will - * fail. - */ - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return 0; - - /* if fd is disabled, should not restore it when reset */ - if (!hdev->fd_en) - return 0; - - spin_lock_bh(&hdev->fd_rule_lock); - hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { - if (rule->state == HCLGE_FD_ACTIVE) - rule->state = HCLGE_FD_TO_ADD; - } - spin_unlock_bh(&hdev->fd_rule_lock); - set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); - - return 0; -} - -static int hclge_get_fd_rule_cnt(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || hclge_is_cls_flower_active(handle)) - return -EOPNOTSUPP; - - cmd->rule_cnt = hdev->hclge_fd_rule_num; - cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]; - - return 0; -} - -static void hclge_fd_get_tcpip4_info(struct hclge_fd_rule *rule, - struct ethtool_tcpip4_spec *spec, - struct ethtool_tcpip4_spec *spec_mask) -{ - spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); - spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); - - spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); - spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); - - spec->psrc = cpu_to_be16(rule->tuples.src_port); - spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.src_port); - - spec->pdst = cpu_to_be16(rule->tuples.dst_port); - spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.dst_port); - - spec->tos = rule->tuples.ip_tos; - spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; -} - -static void hclge_fd_get_ip4_info(struct hclge_fd_rule *rule, - struct ethtool_usrip4_spec *spec, - struct ethtool_usrip4_spec *spec_mask) -{ - spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); - spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); - - spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); - spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); - - spec->tos = rule->tuples.ip_tos; - spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; - - spec->proto = rule->tuples.ip_proto; - spec_mask->proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ? - 0 : rule->tuples_mask.ip_proto; - - spec->ip_ver = ETH_RX_NFC_IP4; -} - -static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule, - struct ethtool_tcpip6_spec *spec, - struct ethtool_tcpip6_spec *spec_mask) -{ - ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip); - ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip); - if (rule->unused_tuple & BIT(INNER_SRC_IP)) - memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src)); - else - ipv6_addr_cpu_to_be32(spec_mask->ip6src, - rule->tuples_mask.src_ip); - - if (rule->unused_tuple & BIT(INNER_DST_IP)) - memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst)); - else - ipv6_addr_cpu_to_be32(spec_mask->ip6dst, - rule->tuples_mask.dst_ip); - - spec->tclass = rule->tuples.ip_tos; - spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; - - spec->psrc = cpu_to_be16(rule->tuples.src_port); - spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.src_port); - - spec->pdst = cpu_to_be16(rule->tuples.dst_port); - spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.dst_port); -} - -static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule, - struct ethtool_usrip6_spec *spec, - struct ethtool_usrip6_spec *spec_mask) -{ - ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip); - ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip); - if (rule->unused_tuple & BIT(INNER_SRC_IP)) - memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src)); - else - ipv6_addr_cpu_to_be32(spec_mask->ip6src, - rule->tuples_mask.src_ip); - - if (rule->unused_tuple & BIT(INNER_DST_IP)) - memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst)); - else - ipv6_addr_cpu_to_be32(spec_mask->ip6dst, - rule->tuples_mask.dst_ip); - - spec->tclass = rule->tuples.ip_tos; - spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; - - spec->l4_proto = rule->tuples.ip_proto; - spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ? - 0 : rule->tuples_mask.ip_proto; -} - -static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule, - struct ethhdr *spec, - struct ethhdr *spec_mask) -{ - ether_addr_copy(spec->h_source, rule->tuples.src_mac); - ether_addr_copy(spec->h_dest, rule->tuples.dst_mac); - - if (rule->unused_tuple & BIT(INNER_SRC_MAC)) - eth_zero_addr(spec_mask->h_source); - else - ether_addr_copy(spec_mask->h_source, rule->tuples_mask.src_mac); - - if (rule->unused_tuple & BIT(INNER_DST_MAC)) - eth_zero_addr(spec_mask->h_dest); - else - ether_addr_copy(spec_mask->h_dest, rule->tuples_mask.dst_mac); - - spec->h_proto = cpu_to_be16(rule->tuples.ether_proto); - spec_mask->h_proto = rule->unused_tuple & BIT(INNER_ETH_TYPE) ? - 0 : cpu_to_be16(rule->tuples_mask.ether_proto); -} - -static void hclge_fd_get_user_def_info(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule) -{ - if ((rule->unused_tuple & HCLGE_FD_TUPLE_USER_DEF_TUPLES) == - HCLGE_FD_TUPLE_USER_DEF_TUPLES) { - fs->h_ext.data[0] = 0; - fs->h_ext.data[1] = 0; - fs->m_ext.data[0] = 0; - fs->m_ext.data[1] = 0; - } else { - fs->h_ext.data[0] = cpu_to_be32(rule->ep.user_def.offset); - fs->h_ext.data[1] = cpu_to_be32(rule->ep.user_def.data); - fs->m_ext.data[0] = - cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK); - fs->m_ext.data[1] = cpu_to_be32(rule->ep.user_def.data_mask); - } -} - -static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule) -{ - if (fs->flow_type & FLOW_EXT) { - fs->h_ext.vlan_tci = cpu_to_be16(rule->tuples.vlan_tag1); - fs->m_ext.vlan_tci = - rule->unused_tuple & BIT(INNER_VLAN_TAG_FST) ? - 0 : cpu_to_be16(rule->tuples_mask.vlan_tag1); - - hclge_fd_get_user_def_info(fs, rule); - } - - if (fs->flow_type & FLOW_MAC_EXT) { - ether_addr_copy(fs->h_ext.h_dest, rule->tuples.dst_mac); - if (rule->unused_tuple & BIT(INNER_DST_MAC)) - eth_zero_addr(fs->m_u.ether_spec.h_dest); - else - ether_addr_copy(fs->m_u.ether_spec.h_dest, - rule->tuples_mask.dst_mac); - } -} - -static struct hclge_fd_rule *hclge_get_fd_rule(struct hclge_dev *hdev, - u16 location) -{ - struct hclge_fd_rule *rule = NULL; - struct hlist_node *node2; - - hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) { - if (rule->location == location) - return rule; - else if (rule->location > location) - return NULL; - } - - return NULL; -} - -static void hclge_fd_get_ring_cookie(struct ethtool_rx_flow_spec *fs, - struct hclge_fd_rule *rule) -{ - if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) { - fs->ring_cookie = RX_CLS_FLOW_DISC; - } else { - u64 vf_id; - - fs->ring_cookie = rule->queue_id; - vf_id = rule->vf_id; - vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; - fs->ring_cookie |= vf_id; - } -} - -static int hclge_get_fd_rule_info(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_fd_rule *rule = NULL; - struct hclge_dev *hdev = vport->back; - struct ethtool_rx_flow_spec *fs; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - fs = (struct ethtool_rx_flow_spec *)&cmd->fs; - - spin_lock_bh(&hdev->fd_rule_lock); - - rule = hclge_get_fd_rule(hdev, fs->location); - if (!rule) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOENT; - } - - fs->flow_type = rule->flow_type; - switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { - case SCTP_V4_FLOW: - case TCP_V4_FLOW: - case UDP_V4_FLOW: - hclge_fd_get_tcpip4_info(rule, &fs->h_u.tcp_ip4_spec, - &fs->m_u.tcp_ip4_spec); - break; - case IP_USER_FLOW: - hclge_fd_get_ip4_info(rule, &fs->h_u.usr_ip4_spec, - &fs->m_u.usr_ip4_spec); - break; - case SCTP_V6_FLOW: - case TCP_V6_FLOW: - case UDP_V6_FLOW: - hclge_fd_get_tcpip6_info(rule, &fs->h_u.tcp_ip6_spec, - &fs->m_u.tcp_ip6_spec); - break; - case IPV6_USER_FLOW: - hclge_fd_get_ip6_info(rule, &fs->h_u.usr_ip6_spec, - &fs->m_u.usr_ip6_spec); - break; - /* The flow type of fd rule has been checked before adding in to rule - * list. As other flow types have been handled, it must be ETHER_FLOW - * for the default case - */ - default: - hclge_fd_get_ether_info(rule, &fs->h_u.ether_spec, - &fs->m_u.ether_spec); - break; - } - - hclge_fd_get_ext_info(fs, rule); - - hclge_fd_get_ring_cookie(fs, rule); - - spin_unlock_bh(&hdev->fd_rule_lock); - - return 0; -} - -static int hclge_get_all_rules(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd, u32 *rule_locs) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - struct hclge_fd_rule *rule; - struct hlist_node *node2; - u32 cnt = 0; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]; - - spin_lock_bh(&hdev->fd_rule_lock); - hlist_for_each_entry_safe(rule, node2, - &hdev->fd_rule_list, rule_node) { - if (cnt == cmd->rule_cnt) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -EMSGSIZE; - } - - if (rule->state == HCLGE_FD_TO_DEL) - continue; - - rule_locs[cnt] = rule->location; - cnt++; - } - - spin_unlock_bh(&hdev->fd_rule_lock); - - cmd->rule_cnt = cnt; - - return 0; -} - -static void hclge_fd_get_flow_tuples(const struct flow_keys *fkeys, - struct hclge_fd_rule_tuples *tuples) -{ -#define flow_ip6_src fkeys->addrs.v6addrs.src.in6_u.u6_addr32 -#define flow_ip6_dst fkeys->addrs.v6addrs.dst.in6_u.u6_addr32 - - tuples->ether_proto = be16_to_cpu(fkeys->basic.n_proto); - tuples->ip_proto = fkeys->basic.ip_proto; - tuples->dst_port = be16_to_cpu(fkeys->ports.dst); - - if (fkeys->basic.n_proto == htons(ETH_P_IP)) { - tuples->src_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.src); - tuples->dst_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.dst); - } else { - int i; - - for (i = 0; i < IPV6_ADDR_WORDS; i++) { - tuples->src_ip[i] = be32_to_cpu(flow_ip6_src[i]); - tuples->dst_ip[i] = be32_to_cpu(flow_ip6_dst[i]); - } - } -} - -/* traverse all rules, check whether an existed rule has the same tuples */ -static struct hclge_fd_rule * -hclge_fd_search_flow_keys(struct hclge_dev *hdev, - const struct hclge_fd_rule_tuples *tuples) -{ - struct hclge_fd_rule *rule = NULL; - struct hlist_node *node; - - hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { - if (!memcmp(tuples, &rule->tuples, sizeof(*tuples))) - return rule; - } - - return NULL; -} - -static void hclge_fd_build_arfs_rule(const struct hclge_fd_rule_tuples *tuples, - struct hclge_fd_rule *rule) -{ - rule->unused_tuple = BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | - BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) | - BIT(INNER_SRC_PORT); - rule->action = 0; - rule->vf_id = 0; - rule->rule_type = HCLGE_FD_ARFS_ACTIVE; - rule->state = HCLGE_FD_TO_ADD; - if (tuples->ether_proto == ETH_P_IP) { - if (tuples->ip_proto == IPPROTO_TCP) - rule->flow_type = TCP_V4_FLOW; - else - rule->flow_type = UDP_V4_FLOW; - } else { - if (tuples->ip_proto == IPPROTO_TCP) - rule->flow_type = TCP_V6_FLOW; - else - rule->flow_type = UDP_V6_FLOW; - } - memcpy(&rule->tuples, tuples, sizeof(rule->tuples)); - memset(&rule->tuples_mask, 0xFF, sizeof(rule->tuples_mask)); -} - -static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id, - u16 flow_id, struct flow_keys *fkeys) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_fd_rule_tuples new_tuples = {}; - struct hclge_dev *hdev = vport->back; - struct hclge_fd_rule *rule; - u16 bit_id; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - /* when there is already fd rule existed add by user, - * arfs should not work - */ - spin_lock_bh(&hdev->fd_rule_lock); - if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE && - hdev->fd_active_type != HCLGE_FD_RULE_NONE) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -EOPNOTSUPP; - } - - hclge_fd_get_flow_tuples(fkeys, &new_tuples); - - /* check is there flow director filter existed for this flow, - * if not, create a new filter for it; - * if filter exist with different queue id, modify the filter; - * if filter exist with same queue id, do nothing - */ - rule = hclge_fd_search_flow_keys(hdev, &new_tuples); - if (!rule) { - bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM); - if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOSPC; - } - - rule = kzalloc_obj(*rule, GFP_ATOMIC); - if (!rule) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOMEM; - } - - rule->location = bit_id; - rule->arfs.flow_id = flow_id; - rule->queue_id = queue_id; - hclge_fd_build_arfs_rule(&new_tuples, rule); - hclge_update_fd_list(hdev, rule->state, rule->location, rule); - hdev->fd_active_type = HCLGE_FD_ARFS_ACTIVE; - } else if (rule->queue_id != queue_id) { - rule->queue_id = queue_id; - rule->state = HCLGE_FD_TO_ADD; - set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); - hclge_task_schedule(hdev, 0); - } - spin_unlock_bh(&hdev->fd_rule_lock); - return rule->location; -} - -static void hclge_rfs_filter_expire(struct hclge_dev *hdev) -{ -#ifdef CONFIG_RFS_ACCEL - struct hnae3_handle *handle = &hdev->vport[0].nic; - struct hclge_fd_rule *rule; - struct hlist_node *node; - - spin_lock_bh(&hdev->fd_rule_lock); - if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) { - spin_unlock_bh(&hdev->fd_rule_lock); - return; - } - hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { - if (rule->state != HCLGE_FD_ACTIVE) - continue; - if (rps_may_expire_flow(handle->netdev, rule->queue_id, - rule->arfs.flow_id, rule->location)) { - rule->state = HCLGE_FD_TO_DEL; - set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); - } - } - spin_unlock_bh(&hdev->fd_rule_lock); -#endif -} - -/* make sure being called after lock up with fd_rule_lock */ -static int hclge_clear_arfs_rules(struct hclge_dev *hdev) -{ -#ifdef CONFIG_RFS_ACCEL - struct hclge_fd_rule *rule; - struct hlist_node *node; - int ret; - - if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) - return 0; - - hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { - switch (rule->state) { - case HCLGE_FD_TO_DEL: - case HCLGE_FD_ACTIVE: - ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, - rule->location, NULL, false); - if (ret) - return ret; - fallthrough; - case HCLGE_FD_TO_ADD: - hclge_fd_dec_rule_cnt(hdev, rule->location); - hlist_del(&rule->rule_node); - kfree(rule); - break; - default: - break; - } - } - hclge_sync_fd_state(hdev); - -#endif - return 0; -} - -static void hclge_get_cls_key_basic(const struct flow_rule *flow, - struct hclge_fd_rule *rule) -{ - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_BASIC)) { - struct flow_match_basic match; - u16 ethtype_key, ethtype_mask; - - flow_rule_match_basic(flow, &match); - ethtype_key = ntohs(match.key->n_proto); - ethtype_mask = ntohs(match.mask->n_proto); - - if (ethtype_key == ETH_P_ALL) { - ethtype_key = 0; - ethtype_mask = 0; - } - rule->tuples.ether_proto = ethtype_key; - rule->tuples_mask.ether_proto = ethtype_mask; - rule->tuples.ip_proto = match.key->ip_proto; - rule->tuples_mask.ip_proto = match.mask->ip_proto; - } else { - rule->unused_tuple |= BIT(INNER_IP_PROTO); - rule->unused_tuple |= BIT(INNER_ETH_TYPE); - } -} - -static void hclge_get_cls_key_mac(const struct flow_rule *flow, - struct hclge_fd_rule *rule) -{ - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct flow_match_eth_addrs match; - - flow_rule_match_eth_addrs(flow, &match); - ether_addr_copy(rule->tuples.dst_mac, match.key->dst); - ether_addr_copy(rule->tuples_mask.dst_mac, match.mask->dst); - ether_addr_copy(rule->tuples.src_mac, match.key->src); - ether_addr_copy(rule->tuples_mask.src_mac, match.mask->src); - } else { - rule->unused_tuple |= BIT(INNER_DST_MAC); - rule->unused_tuple |= BIT(INNER_SRC_MAC); - } -} - -static void hclge_get_cls_key_vlan(const struct flow_rule *flow, - struct hclge_fd_rule *rule) -{ - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_VLAN)) { - struct flow_match_vlan match; - - flow_rule_match_vlan(flow, &match); - rule->tuples.vlan_tag1 = match.key->vlan_id | - (match.key->vlan_priority << VLAN_PRIO_SHIFT); - rule->tuples_mask.vlan_tag1 = match.mask->vlan_id | - (match.mask->vlan_priority << VLAN_PRIO_SHIFT); - } else { - rule->unused_tuple |= BIT(INNER_VLAN_TAG_FST); - } -} - -static int hclge_get_cls_key_ip(const struct flow_rule *flow, - struct hclge_fd_rule *rule, - struct netlink_ext_ack *extack) -{ - u16 addr_type = 0; - - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) { - struct flow_match_control match; - - flow_rule_match_control(flow, &match); - addr_type = match.key->addr_type; - - if (flow_rule_has_control_flags(match.mask->flags, extack)) - return -EOPNOTSUPP; - } - - if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { - struct flow_match_ipv4_addrs match; - - flow_rule_match_ipv4_addrs(flow, &match); - rule->tuples.src_ip[IPV4_INDEX] = be32_to_cpu(match.key->src); - rule->tuples_mask.src_ip[IPV4_INDEX] = - be32_to_cpu(match.mask->src); - rule->tuples.dst_ip[IPV4_INDEX] = be32_to_cpu(match.key->dst); - rule->tuples_mask.dst_ip[IPV4_INDEX] = - be32_to_cpu(match.mask->dst); - } else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { - struct flow_match_ipv6_addrs match; - - flow_rule_match_ipv6_addrs(flow, &match); - ipv6_addr_be32_to_cpu(rule->tuples.src_ip, - match.key->src.s6_addr32); - ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip, - match.mask->src.s6_addr32); - ipv6_addr_be32_to_cpu(rule->tuples.dst_ip, - match.key->dst.s6_addr32); - ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip, - match.mask->dst.s6_addr32); - } else { - rule->unused_tuple |= BIT(INNER_SRC_IP); - rule->unused_tuple |= BIT(INNER_DST_IP); - } - - return 0; -} - -static void hclge_get_cls_key_port(const struct flow_rule *flow, - struct hclge_fd_rule *rule) -{ - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_PORTS)) { - struct flow_match_ports match; - - flow_rule_match_ports(flow, &match); - - rule->tuples.src_port = be16_to_cpu(match.key->src); - rule->tuples_mask.src_port = be16_to_cpu(match.mask->src); - rule->tuples.dst_port = be16_to_cpu(match.key->dst); - rule->tuples_mask.dst_port = be16_to_cpu(match.mask->dst); - } else { - rule->unused_tuple |= BIT(INNER_SRC_PORT); - rule->unused_tuple |= BIT(INNER_DST_PORT); - } -} - -static int hclge_parse_cls_flower(struct hclge_dev *hdev, - struct flow_cls_offload *cls_flower, - struct hclge_fd_rule *rule) -{ - struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower); - struct netlink_ext_ack *extack = cls_flower->common.extack; - struct flow_dissector *dissector = flow->match.dissector; - int ret; - - if (dissector->used_keys & - ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | - BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | - BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | - BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_PORTS))) { - dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n", - dissector->used_keys); - return -EOPNOTSUPP; - } - - hclge_get_cls_key_basic(flow, rule); - hclge_get_cls_key_mac(flow, rule); - hclge_get_cls_key_vlan(flow, rule); - - ret = hclge_get_cls_key_ip(flow, rule, extack); - if (ret) - return ret; - - hclge_get_cls_key_port(flow, rule); - - return 0; -} - -static int hclge_check_cls_flower(struct hclge_dev *hdev, - struct flow_cls_offload *cls_flower, int tc) -{ - u32 prio = cls_flower->common.prio; - - if (tc < 0 || tc > hdev->tc_max) { - dev_err(&hdev->pdev->dev, "invalid traffic class\n"); - return -EINVAL; - } - - if (prio == 0 || - prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { - dev_err(&hdev->pdev->dev, - "prio %u should be in range[1, %u]\n", - prio, hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); - return -EINVAL; - } - - if (test_bit(prio - 1, hdev->fd_bmap)) { - dev_err(&hdev->pdev->dev, "prio %u is already used\n", prio); - return -EINVAL; - } - return 0; -} - -static int hclge_add_cls_flower(struct hnae3_handle *handle, - struct flow_cls_offload *cls_flower, - int tc) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - struct hclge_fd_rule *rule; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { - dev_err(&hdev->pdev->dev, - "cls flower is not supported\n"); - return -EOPNOTSUPP; - } - - ret = hclge_check_cls_flower(hdev, cls_flower, tc); - if (ret) { - dev_err(&hdev->pdev->dev, - "failed to check cls flower params, ret = %d\n", ret); - return ret; - } - - rule = kzalloc_obj(*rule); - if (!rule) - return -ENOMEM; - - ret = hclge_parse_cls_flower(hdev, cls_flower, rule); - if (ret) { - kfree(rule); - return ret; - } - - rule->action = HCLGE_FD_ACTION_SELECT_TC; - rule->cls_flower.tc = tc; - rule->location = cls_flower->common.prio - 1; - rule->vf_id = 0; - rule->cls_flower.cookie = cls_flower->cookie; - rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE; - - ret = hclge_add_fd_entry_common(hdev, rule); - if (ret) - kfree(rule); - - return ret; -} - -static struct hclge_fd_rule *hclge_find_cls_flower(struct hclge_dev *hdev, - unsigned long cookie) -{ - struct hclge_fd_rule *rule; - struct hlist_node *node; - - hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { - if (rule->cls_flower.cookie == cookie) - return rule; - } - - return NULL; -} - -static int hclge_del_cls_flower(struct hnae3_handle *handle, - struct flow_cls_offload *cls_flower) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - struct hclge_fd_rule *rule; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - spin_lock_bh(&hdev->fd_rule_lock); - - rule = hclge_find_cls_flower(hdev, cls_flower->cookie); - if (!rule) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -EINVAL; - } - - ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location, - NULL, false); - if (ret) { - /* if tcam config fail, set rule state to TO_DEL, - * so the rule will be deleted when periodic - * task being scheduled. - */ - hclge_update_fd_list(hdev, HCLGE_FD_TO_DEL, rule->location, NULL); - set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); - spin_unlock_bh(&hdev->fd_rule_lock); - return ret; - } - - hclge_update_fd_list(hdev, HCLGE_FD_DELETED, rule->location, NULL); - spin_unlock_bh(&hdev->fd_rule_lock); - - return 0; -} - -static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist) -{ - struct hclge_fd_rule *rule; - struct hlist_node *node; - int ret = 0; - - if (!test_and_clear_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state)) - return; - - spin_lock_bh(&hdev->fd_rule_lock); - - hlist_for_each_entry_safe(rule, node, hlist, rule_node) { - switch (rule->state) { - case HCLGE_FD_TO_ADD: - ret = hclge_fd_config_rule(hdev, rule); - if (ret) - goto out; - rule->state = HCLGE_FD_ACTIVE; - break; - case HCLGE_FD_TO_DEL: - ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, - rule->location, NULL, false); - if (ret) - goto out; - hclge_fd_dec_rule_cnt(hdev, rule->location); - hclge_fd_free_node(hdev, rule); - break; - default: - break; - } - } - -out: - if (ret) - set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); - - spin_unlock_bh(&hdev->fd_rule_lock); -} - -static void hclge_sync_fd_table(struct hclge_dev *hdev) -{ - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return; - - if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) { - bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE; - - hclge_clear_fd_rules_in_list(hdev, clear_list); - } - - hclge_sync_fd_user_def_cfg(hdev, false); - - hclge_sync_fd_list(hdev, &hdev->fd_rule_list); -} - static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle) { struct hclge_vport *vport = hclge_get_vport(handle); @@ -7574,21 +5141,6 @@ static unsigned long hclge_ae_dev_reset_cnt(struct hnae3_handle *handle) return hdev->rst_stats.hw_reset_done_cnt; } -static void hclge_enable_fd(struct hnae3_handle *handle, bool enable) -{ - struct hclge_vport *vport = hclge_get_vport(handle); - struct hclge_dev *hdev = vport->back; - - hdev->fd_en = enable; - - if (!enable) - set_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state); - else - hclge_restore_fd_entries(handle); - - hclge_task_schedule(hdev, 0); -} - static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) { #define HCLGE_LINK_STATUS_WAIT_CNT 3 @@ -7628,6 +5180,26 @@ static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) HCLGE_LINK_STATUS_WAIT_CNT); } +u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id, + u8 vf_id, u8 network_port_id) +{ + u32 port_number = 0; + + if (port_type == HOST_PORT) { + hnae3_set_field(port_number, HCLGE_PF_ID_M, HCLGE_PF_ID_S, + pf_id); + hnae3_set_field(port_number, HCLGE_VF_ID_M, HCLGE_VF_ID_S, + vf_id); + hnae3_set_bit(port_number, HCLGE_PORT_TYPE_B, HOST_PORT); + } else { + hnae3_set_field(port_number, HCLGE_NETWORK_PORT_ID_M, + HCLGE_NETWORK_PORT_ID_S, network_port_id); + hnae3_set_bit(port_number, HCLGE_PORT_TYPE_B, NETWORK_PORT); + } + + return port_number; +} + static int hclge_config_switch_param(struct hclge_dev *hdev, int vfid, u8 switch_param, u8 param_mask) { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 032b472d2368..87adeb64e6ea 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -645,6 +645,9 @@ struct key_info { #define HCLGE_FD_USER_DEF_DATA GENMASK(15, 0) #define HCLGE_FD_USER_DEF_OFFSET GENMASK(15, 0) #define HCLGE_FD_USER_DEF_OFFSET_UNMASK GENMASK(15, 0) +#define HCLGE_FD_VXLAN_VNI_UNMASK GENMASK(31, 0) + +#define HCLGE_VNI_LENGTH 3 /* assigned by firmware, the real filter number for each pf may be less */ #define MAX_FD_FILTER_NUM 4096 @@ -738,6 +741,7 @@ struct hclge_fd_rule_tuples { u32 l4_user_def; u8 ip_tos; u8 ip_proto; + u32 outer_tun_vni; }; struct hclge_fd_rule { @@ -1175,4 +1179,6 @@ int hclge_mac_update_stats(struct hclge_dev *hdev); struct hclge_vport *hclge_get_vf_vport(struct hclge_dev *hdev, int vf); int hclge_inform_vf_reset(struct hclge_vport *vport, u16 reset_type); int hclge_query_scc_version(struct hclge_dev *hdev, u32 *scc_version); +u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id, + u8 vf_id, u8 network_port_id); #endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 0cc911e6c732..063a692c2b41 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -26,11 +26,15 @@ static struct hnae3_ae_algo ae_algovf; static struct workqueue_struct *hclgevf_wq; static const struct pci_device_id ae_algovf_pci_tbl[] = { - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), 0}, - {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF), - HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, + { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), + .driver_data = 0, + }, { + PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF), + .driver_data = HNAE3_DEV_SUPPORT_ROCE_DCB_BITS, + }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, ae_algovf_pci_tbl); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index ae1f523d6841..42f4792d255b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -1467,12 +1467,12 @@ static void hinic_shutdown(struct pci_dev *pdev) } static const struct pci_device_id hinic_pci_table[] = { - { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_QUAD_PORT_25GE), 0}, - { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_100GE), 0}, - { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_100GE_MEZZ), 0}, - { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_QUAD_PORT_25GE_MEZZ), 0}, - { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_VF), 0}, - { 0, 0} + { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_QUAD_PORT_25GE) }, + { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_100GE) }, + { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_100GE_MEZZ) }, + { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_QUAD_PORT_25GE_MEZZ) }, + { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_VF) }, + { } }; MODULE_DEVICE_TABLE(pci, hinic_pci_table); diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c index f83d5bd1041e..36df807cbe09 100644 --- a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c +++ b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c @@ -426,10 +426,9 @@ static void hinic3_remove(struct pci_dev *pdev) } static const struct pci_device_id hinic3_pci_table[] = { - {PCI_VDEVICE(HUAWEI, PCI_DEV_ID_HINIC3_PF), 0}, - {PCI_VDEVICE(HUAWEI, PCI_DEV_ID_HINIC3_VF), 0}, - {0, 0} - + { PCI_VDEVICE(HUAWEI, PCI_DEV_ID_HINIC3_PF) }, + { PCI_VDEVICE(HUAWEI, PCI_DEV_ID_HINIC3_VF) }, + { } }; MODULE_DEVICE_TABLE(pci, hinic3_pci_table); diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 4e503b3d0d2d..c0e1d85fce3f 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -457,7 +458,7 @@ static inline u32 emac_iff2rmr(struct net_device *ndev) if (emac_has_feature(dev, EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE)) { r &= ~EMAC4_RMR_MJS_MASK; - r |= EMAC4_RMR_MJS(ndev->mtu); + r |= EMAC4_RMR_MJS(ndev->mtu + VLAN_HLEN); } return r; @@ -1161,6 +1162,17 @@ static void emac_clean_rx_ring(struct emac_instance *dev) } } +static void emac_clear_mal_desc(struct mal_descriptor *desc, int count) +{ + int i; + + for (i = 0; i < count; i++) { + WRITE_ONCE(desc[i].ctrl, 0); + WRITE_ONCE(desc[i].data_len, 0); + WRITE_ONCE(desc[i].data_ptr, 0); + } +} + static int __emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot) { @@ -1727,7 +1739,6 @@ static inline int emac_rx_sg_append(struct emac_instance *dev, int slot) /* NAPI poll context */ static int emac_poll_rx(void *param, int budget) { - LIST_HEAD(rx_list); struct emac_instance *dev = param; int slot = dev->rx_slot, received = 0; @@ -1784,7 +1795,7 @@ static int emac_poll_rx(void *param, int budget) skb->protocol = eth_type_trans(skb, dev->ndev); emac_rx_csum(dev, skb, ctrl); - list_add_tail(&skb->list, &rx_list); + napi_gro_receive(&dev->mal->napi, skb); next: ++dev->stats.rx_packets; skip: @@ -1828,8 +1839,6 @@ static int emac_poll_rx(void *param, int budget) goto next; } - netif_receive_skb_list(&rx_list); - if (received) { DBG2(dev, "rx %d BDs" NL, received); dev->rx_slot = slot; @@ -3037,6 +3046,11 @@ static int emac_probe(struct platform_device *ofdev) /* Setup error IRQ handler */ dev->emac_irq = platform_get_irq(ofdev, 0); + if (dev->emac_irq < 0) { + err = dev->emac_irq; + goto err_gone; + } + err = devm_request_irq(&ofdev->dev, dev->emac_irq, emac_irq, 0, "EMAC", dev); if (err) { @@ -3086,8 +3100,8 @@ static int emac_probe(struct platform_device *ofdev) DBG(dev, "rx_desc %p" NL, dev->rx_desc); /* Clean rings */ - memset(dev->tx_desc, 0, NUM_TX_BUFF * sizeof(struct mal_descriptor)); - memset(dev->rx_desc, 0, NUM_RX_BUFF * sizeof(struct mal_descriptor)); + emac_clear_mal_desc(dev->tx_desc, NUM_TX_BUFF); + emac_clear_mal_desc(dev->rx_desc, NUM_RX_BUFF); memset(dev->tx_skb, 0, NUM_TX_BUFF * sizeof(struct sk_buff *)); memset(dev->rx_skb, 0, NUM_RX_BUFF * sizeof(struct sk_buff *)); diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 7d70056e9008..74526002d52b 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -635,6 +635,11 @@ static int mal_probe(struct platform_device *ofdev) mal->txeob_irq = platform_get_irq(ofdev, 0); mal->rxeob_irq = platform_get_irq(ofdev, 1); mal->serr_irq = platform_get_irq(ofdev, 2); + if (mal->txeob_irq < 0 || mal->rxeob_irq < 0 || mal->serr_irq < 0) { + err = mal->txeob_irq < 0 ? mal->txeob_irq : + mal->rxeob_irq < 0 ? mal->rxeob_irq : mal->serr_irq; + goto fail2; + } if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) { mal->txde_irq = mal->rxde_irq = mal->serr_irq; @@ -643,6 +648,10 @@ static int mal_probe(struct platform_device *ofdev) } else { mal->txde_irq = platform_get_irq(ofdev, 3); mal->rxde_irq = platform_get_irq(ofdev, 4); + if (mal->txde_irq < 0 || mal->rxde_irq < 0) { + err = mal->txde_irq < 0 ? mal->txde_irq : mal->rxde_irq; + goto fail2; + } irqflags = 0; hdlr_serr = mal_serr; hdlr_txde = mal_txde; @@ -703,13 +712,13 @@ static void mal_remove(struct platform_device *ofdev) MAL_DBG(mal, "remove" NL); /* Synchronize with scheduled polling */ - napi_disable(&mal->napi); - - if (!list_empty(&mal->list)) + if (!list_empty(&mal->list)) { + napi_disable(&mal->napi); /* This is *very* bad */ WARN(1, KERN_EMERG "mal%d: commac list is not empty on remove!\n", mal->index); + } mal_reset(mal); diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index 9074b558de35..29960762e64a 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -176,9 +176,12 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); MODULE_PARM_DESC(eeprom_bad_csum_allow, "Allow bad eeprom checksums"); MODULE_PARM_DESC(use_io, "Force use of i/o access mode"); -#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\ - PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \ - PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich } +#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) { \ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, (device_id)), \ + .class = PCI_CLASS_NETWORK_ETHERNET << 8, \ + .class_mask = 0xFFFF00, \ + .driver_data = (ich) } + static const struct pci_device_id e100_id_table[] = { INTEL_8255X_ETHERNET_DEVICE(0x1029, 0), INTEL_8255X_ETHERNET_DEVICE(0x1030, 0), diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 4dcbeabb3ad2..c15ad95c63c1 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -499,6 +499,9 @@ static int e1000_set_eeprom(struct net_device *netdev, if (ret_val) goto out; + /* Device's eeprom is always little-endian, word addressable */ + le16_to_cpus(&eeprom_buff[0]); + ptr++; } if ((eeprom->offset + eeprom->len) & 1) { @@ -509,11 +512,10 @@ static int e1000_set_eeprom(struct net_device *netdev, &eeprom_buff[last_word - first_word]); if (ret_val) goto out; - } - /* Device's eeprom is always little-endian, word addressable */ - for (i = 0; i < last_word - first_word + 1; i++) - le16_to_cpus(&eeprom_buff[i]); + /* Device's eeprom is always little-endian, word addressable */ + le16_to_cpus(&eeprom_buff[last_word - first_word]); + } memcpy(ptr, bytes, eeprom->len); diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index dbed30943ef4..a8b35ae41141 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -583,20 +583,25 @@ static int e1000_set_eeprom(struct net_device *netdev, /* need read/modify/write of first changed EEPROM word */ /* only the second byte of the word is being modified */ ret_val = e1000_read_nvm(hw, first_word, 1, &eeprom_buff[0]); + if (ret_val) + goto out; + + /* Device's eeprom is always little-endian, word addressable */ + le16_to_cpus(&eeprom_buff[0]); + ptr++; } - if (((eeprom->offset + eeprom->len) & 1) && (!ret_val)) + if ((eeprom->offset + eeprom->len) & 1) { /* need read/modify/write of last changed EEPROM word */ /* only the first byte of the word is being modified */ ret_val = e1000_read_nvm(hw, last_word, 1, &eeprom_buff[last_word - first_word]); + if (ret_val) + goto out; - if (ret_val) - goto out; - - /* Device's eeprom is always little-endian, word addressable */ - for (i = 0; i < last_word - first_word + 1; i++) - le16_to_cpus(&eeprom_buff[i]); + /* Device's eeprom is always little-endian, word addressable */ + le16_to_cpus(&eeprom_buff[last_word - first_word]); + } memcpy(ptr, bytes, eeprom->len); diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 7ce0cc8ab8f4..808e5cddd6a9 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1803,7 +1803,7 @@ static irqreturn_t e1000_intr_msi(int __always_unused irq, void *data) adapter->total_tx_packets = 0; adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __napi_schedule(&adapter->napi); + __napi_schedule_irqoff(&adapter->napi); } return IRQ_HANDLED; @@ -1882,7 +1882,7 @@ static irqreturn_t e1000_intr(int __always_unused irq, void *data) adapter->total_tx_packets = 0; adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __napi_schedule(&adapter->napi); + __napi_schedule_irqoff(&adapter->napi); } return IRQ_HANDLED; @@ -1951,7 +1951,7 @@ static irqreturn_t e1000_intr_msix_rx(int __always_unused irq, void *data) if (napi_schedule_prep(&adapter->napi)) { adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __napi_schedule(&adapter->napi); + __napi_schedule_irqoff(&adapter->napi); } return IRQ_HANDLED; } @@ -3943,7 +3943,7 @@ static void e1000e_systim_reset(struct e1000_adapter *adapter) /* reset the systim ns time counter */ spin_lock_irqsave(&adapter->systim_lock, flags); timecounter_init(&adapter->tc, &adapter->cc, - ktime_to_ns(ktime_get_real())); + ktime_get_real_ns()); spin_unlock_irqrestore(&adapter->systim_lock, flags); /* restore the previous hwtstamp configuration settings */ @@ -7799,137 +7799,370 @@ static const struct pci_error_handlers e1000_err_handler = { }; static const struct pci_device_id e1000_pci_tbl[] = { - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), - board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_FIBER), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES_DUAL), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES_QUAD), board_82571 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571PT_QUAD_COPPER), board_82571 }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_FIBER), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES_DUAL), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_SERDES_QUAD), + .driver_data = board_82571, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82571PT_QUAD_COPPER), + .driver_data = board_82571, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI), board_82572 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_COPPER), board_82572 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_FIBER), board_82572 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_SERDES), board_82572 }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI), + .driver_data = board_82572, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_COPPER), + .driver_data = board_82572, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_FIBER), + .driver_data = board_82572, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_SERDES), + .driver_data = board_82572, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82573E), board_82573 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82573E_IAMT), board_82573 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82573L), board_82573 }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82573E), + .driver_data = board_82573, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82573E_IAMT), + .driver_data = board_82573, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82573L), + .driver_data = board_82573, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82574L), board_82574 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82574LA), board_82574 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82583V), board_82583 }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82574L), + .driver_data = board_82574, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82574LA), + .driver_data = board_82574, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_82583V), + .driver_data = board_82583, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_COPPER_DPT), - board_80003es2lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_COPPER_SPT), - board_80003es2lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_SERDES_DPT), - board_80003es2lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_SERDES_SPT), - board_80003es2lan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_COPPER_DPT), + .driver_data = board_80003es2lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_COPPER_SPT), + .driver_data = board_80003es2lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_SERDES_DPT), + .driver_data = board_80003es2lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_80003ES2LAN_SERDES_SPT), + .driver_data = board_80003es2lan, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IFE), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IFE_G), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IFE_GT), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_AMT), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_C), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M_AMT), board_ich8lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_82567V_3), board_ich8lan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IFE), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IFE_G), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IFE_GT), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_AMT), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_C), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M_AMT), + .driver_data = board_ich8lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_82567V_3), + .driver_data = board_ich8lan, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE_G), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE_GT), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_AMT), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_C), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_BM), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M_AMT), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M_V), board_ich9lan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE_G), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE_GT), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_AMT), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_C), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_BM), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M_AMT), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M_V), + .driver_data = board_ich9lan + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_LM), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_LF), board_ich9lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_V), board_ich9lan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_LM), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_LF), + .driver_data = board_ich9lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_V), + .driver_data = board_ich9lan, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_D_BM_LM), board_ich10lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_D_BM_LF), board_ich10lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_D_BM_V), board_ich10lan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_D_BM_LM), + .driver_data = board_ich10lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_D_BM_LF), + .driver_data = board_ich10lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_D_BM_V), + .driver_data = board_ich10lan, + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_M_HV_LM), board_pchlan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_M_HV_LC), board_pchlan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_D_HV_DM), board_pchlan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_D_HV_DC), board_pchlan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_M_HV_LM), + .driver_data = board_pchlan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_M_HV_LC), + .driver_data = board_pchlan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_D_HV_DM), + .driver_data = board_pchlan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_D_HV_DC), + .driver_data = board_pchlan + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH2_LV_LM), board_pch2lan }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH2_LV_V), board_pch2lan }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH2_LV_LM), + .driver_data = board_pch2lan, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH2_LV_V), + .driver_data = board_pch2lan + }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPT_I217_LM), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPT_I217_V), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPTLP_I218_LM), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPTLP_I218_V), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_LM2), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_V2), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_LM3), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_V3), board_pch_lpt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM2), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V2), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LBG_I219_LM3), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM4), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V4), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM5), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V5), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM6), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM8), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V8), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM9), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V9), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_LM10), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_V10), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_LM11), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_V11), board_pch_cnp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_LM12), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_V12), board_pch_spt }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_LM13), board_pch_tgp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_V13), board_pch_tgp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_LM14), board_pch_tgp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_V14), board_pch_tgp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_LM15), board_pch_tgp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_V15), board_pch_tgp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_LM23), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_V23), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM16), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V16), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM17), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V17), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_LM22), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_V22), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM19), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V19), board_pch_adp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_LM18), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_V18), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM20), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V20), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM21), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V21), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ARL_I219_LM24), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ARL_I219_V24), board_pch_mtp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM25), board_pch_ptp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V25), board_pch_ptp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM27), board_pch_ptp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V27), board_pch_ptp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_NVL_I219_LM29), board_pch_ptp }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_NVL_I219_V29), board_pch_ptp }, + { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPT_I217_LM), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPT_I217_V), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPTLP_I218_LM), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LPTLP_I218_V), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_LM2), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_V2), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_LM3), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_I218_V3), + .driver_data = board_pch_lpt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM2), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V2), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LBG_I219_LM3), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM4), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V4), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM5), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V5), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM6), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM8), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V8), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM9), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V9), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_LM10), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_V10), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_LM11), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_V11), + .driver_data = board_pch_cnp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_LM12), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CMP_I219_V12), + .driver_data = board_pch_spt, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_LM13), + .driver_data = board_pch_tgp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_V13), + .driver_data = board_pch_tgp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_LM14), + .driver_data = board_pch_tgp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_V14), + .driver_data = board_pch_tgp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_LM15), + .driver_data = board_pch_tgp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_TGP_I219_V15), + .driver_data = board_pch_tgp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_LM23), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_V23), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM16), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V16), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM17), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V17), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_LM22), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_RPL_I219_V22), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM19), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_V19), + .driver_data = board_pch_adp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_LM18), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_MTP_I219_V18), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM20), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V20), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_LM21), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LNP_I219_V21), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ARL_I219_LM24), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ARL_I219_V24), + .driver_data = board_pch_mtp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM25), + .driver_data = board_pch_ptp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V25), + .driver_data = board_pch_ptp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_LM27), + .driver_data = board_pch_ptp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_PTP_I219_V27), + .driver_data = board_pch_ptp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_NVL_I219_LM29), + .driver_data = board_pch_ptp, + }, { + PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_NVL_I219_V29), + .driver_data = board_pch_ptp + }, - { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ + { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, e1000_pci_tbl); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index d75b8a50413d..f5b4d062709a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -21,12 +21,12 @@ static const struct fm10k_info *fm10k_info_tbl[] = { * Class, Class Mask, private data (not used) } */ static const struct pci_device_id fm10k_pci_tbl[] = { - { PCI_VDEVICE(INTEL, FM10K_DEV_ID_PF), fm10k_device_pf }, - { PCI_VDEVICE(INTEL, FM10K_DEV_ID_SDI_FM10420_QDA2), fm10k_device_pf }, - { PCI_VDEVICE(INTEL, FM10K_DEV_ID_SDI_FM10420_DA2), fm10k_device_pf }, - { PCI_VDEVICE(INTEL, FM10K_DEV_ID_VF), fm10k_device_vf }, + { PCI_VDEVICE(INTEL, FM10K_DEV_ID_PF), .driver_data = fm10k_device_pf }, + { PCI_VDEVICE(INTEL, FM10K_DEV_ID_SDI_FM10420_QDA2), .driver_data = fm10k_device_pf }, + { PCI_VDEVICE(INTEL, FM10K_DEV_ID_SDI_FM10420_DA2), .driver_data = fm10k_device_pf }, + { PCI_VDEVICE(INTEL, FM10K_DEV_ID_VF), .driver_data = fm10k_device_vf }, /* required last entry */ - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, fm10k_pci_tbl); diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index 1be97a3a86ce..dcf3baec7b73 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -109,7 +109,7 @@ static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) -EFBIG, /* I40E_AQ_RC_EFBIG */ }; - if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) + if (aq_rc >= ARRAY_SIZE(aq_to_posix)) return -ERANGE; return aq_to_posix[aq_rc]; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6d4f9218dc68..a04683004a56 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -63,40 +63,43 @@ static bool i40e_is_total_port_shutdown_enabled(struct i40e_pf *pf); * Class, Class Mask, private data (not used) } */ static const struct pci_device_id i40e_pci_tbl[] = { - {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_BC), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_BC), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_SFP), 0}, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_BC) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_BC) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_SFP) }, /* * This ID conflicts with ipw2200, but the devices can be differentiated * because i40e devices use PCI_CLASS_NETWORK_ETHERNET and ipw2200 * devices use PCI_CLASS_NETWORK_OTHER. */ - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, I40E_DEV_ID_10G_B), - PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_I_X722), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722_A), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_X710_N3000), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_XXV710_N3000), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_B), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_SFP28), 0}, + { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, I40E_DEV_ID_10G_B), + .class = PCI_CLASS_NETWORK_ETHERNET << 8, + .class_mask = 0xffff00, + }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_X722) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_X722) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_I_X722) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722_A) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_X710_N3000) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_XXV710_N3000) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_B) }, + { PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_SFP28) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, i40e_pci_tbl); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 7d07c389bb23..ff62b5f2c815 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -24,9 +24,6 @@ #define I40E_PTP_1GB_INCVAL_MULT 20 #define I40E_ISGN 0x80000000 -#define I40E_PRTTSYN_CTL1_TSYNTYPE_V1 BIT(I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) -#define I40E_PRTTSYN_CTL1_TSYNTYPE_V2 (2 << \ - I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) #define I40E_SUBDEV_ID_25G_PTP_PIN 0xB enum i40e_ptp_pin { @@ -1220,7 +1217,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V1MESSTYPE0_MASK | I40E_PRTTSYN_CTL1_TSYNTYPE_V1 | - I40E_PRTTSYN_CTL1_UDP_ENA_MASK; + I40E_PRTTSYN_CTL1_UDP_ENA_319; config->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: @@ -1237,9 +1234,9 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK | - I40E_PRTTSYN_CTL1_TSYNTYPE_V2; + I40E_PRTTSYN_CTL1_TSYNTYPE_V2_EVENT; if (test_bit(I40E_HW_CAP_PTP_L4, pf->hw.caps)) { - tsyntype |= I40E_PRTTSYN_CTL1_UDP_ENA_MASK; + tsyntype |= I40E_PRTTSYN_CTL1_UDP_ENA_319; config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; } else { config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; diff --git a/drivers/net/ethernet/intel/i40e/i40e_register.h b/drivers/net/ethernet/intel/i40e/i40e_register.h index 432afbb64201..d426d83e0214 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_register.h +++ b/drivers/net/ethernet/intel/i40e/i40e_register.h @@ -788,8 +788,18 @@ #define I40E_PRTTSYN_CTL1_V2MESSTYPE0_SHIFT 16 #define I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK I40E_MASK(0xF, I40E_PRTTSYN_CTL1_V2MESSTYPE0_SHIFT) #define I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT 24 +#define I40E_PRTTSYN_CTL1_TSYNTYPE_MASK I40E_MASK(0x3, I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT) +/* Timestamp UDP v1 packets */ +#define I40E_PRTTSYN_CTL1_TSYNTYPE_V1 \ + FIELD_PREP(I40E_PRTTSYN_CTL1_TSYNTYPE_MASK, 1) +/* Timestamp L2 and UDP v2 packets with message type < 8 */ +#define I40E_PRTTSYN_CTL1_TSYNTYPE_V2_EVENT \ + FIELD_PREP(I40E_PRTTSYN_CTL1_TSYNTYPE_MASK, 3) #define I40E_PRTTSYN_CTL1_UDP_ENA_SHIFT 26 #define I40E_PRTTSYN_CTL1_UDP_ENA_MASK I40E_MASK(0x3, I40E_PRTTSYN_CTL1_UDP_ENA_SHIFT) +/* Timestamp UDP packets on port 319 */ +#define I40E_PRTTSYN_CTL1_UDP_ENA_319 \ + FIELD_PREP(I40E_PRTTSYN_CTL1_UDP_ENA_MASK, 1) #define I40E_PRTTSYN_CTL1_TSYNENA_SHIFT 31 #define I40E_PRTTSYN_CTL1_TSYNENA_MASK I40E_MASK(0x1, I40E_PRTTSYN_CTL1_TSYNENA_SHIFT) #define I40E_PRTTSYN_INC_H 0x001E4060 /* Reset: GLOBR */ diff --git a/drivers/net/ethernet/intel/iavf/iavf_adminq.h b/drivers/net/ethernet/intel/iavf/iavf_adminq.h index bbf5c4b3a2ae..dd2f61172157 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_adminq.h +++ b/drivers/net/ethernet/intel/iavf/iavf_adminq.h @@ -113,7 +113,7 @@ static inline int iavf_aq_rc_to_posix(int aq_ret, int aq_rc) if (aq_ret == IAVF_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) + if (aq_rc >= ARRAY_SIZE(aq_to_posix)) return -ERANGE; return aq_to_posix[aq_rc]; diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index d2914c511e1e..29b8403a066b 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -36,12 +36,12 @@ static const char iavf_copyright[] = * Class, Class Mask, private data (not used) } */ static const struct pci_device_id iavf_pci_tbl[] = { - {PCI_VDEVICE(INTEL, IAVF_DEV_ID_VF), 0}, - {PCI_VDEVICE(INTEL, IAVF_DEV_ID_VF_HV), 0}, - {PCI_VDEVICE(INTEL, IAVF_DEV_ID_X722_VF), 0}, - {PCI_VDEVICE(INTEL, IAVF_DEV_ID_ADAPTIVE_VF), 0}, + { PCI_VDEVICE(INTEL, IAVF_DEV_ID_VF) }, + { PCI_VDEVICE(INTEL, IAVF_DEV_ID_VF_HV) }, + { PCI_VDEVICE(INTEL, IAVF_DEV_ID_X722_VF) }, + { PCI_VDEVICE(INTEL, IAVF_DEV_ID_ADAPTIVE_VF) }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, iavf_pci_tbl); @@ -1134,10 +1134,12 @@ bool iavf_promiscuous_mode_changed(struct iavf_adapter *adapter) * @netdev: network interface device structure * @uc: snapshot of uc address list * @mc: snapshot of mc address list + * + * Return: 0 on success. **/ -static void iavf_set_rx_mode(struct net_device *netdev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int iavf_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct iavf_adapter *adapter = netdev_priv(netdev); @@ -1150,6 +1152,8 @@ static void iavf_set_rx_mode(struct net_device *netdev, if (iavf_promiscuous_mode_changed(adapter)) adapter->aq_required |= IAVF_FLAG_AQ_CONFIGURE_PROMISC_MODE; spin_unlock_bh(&adapter->current_netdev_promisc_flags_lock); + + return 0; } /** diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c index 4f2defd2331b..ec234cc8bd9d 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c @@ -2550,13 +2550,11 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter, case VIRTCHNL_OP_ADD_ETH_ADDR: if (!v_retval) iavf_mac_add_ok(adapter); - if (!ether_addr_equal(netdev->dev_addr, adapter->hw.mac.addr)) - if (!ether_addr_equal(netdev->dev_addr, - adapter->hw.mac.addr)) { - netif_addr_lock_bh(netdev); - eth_hw_addr_set(netdev, adapter->hw.mac.addr); - netif_addr_unlock_bh(netdev); - } + if (!ether_addr_equal(netdev->dev_addr, adapter->hw.mac.addr)) { + netif_addr_lock_bh(netdev); + eth_hw_addr_set(netdev, adapter->hw.mac.addr); + netif_addr_unlock_bh(netdev); + } wake_up(&adapter->vc_waitqueue); break; case VIRTCHNL_OP_GET_STATS: { diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile index 5b2c666496e7..95fd0c49800f 100644 --- a/drivers/net/ethernet/intel/ice/Makefile +++ b/drivers/net/ethernet/intel/ice/Makefile @@ -54,7 +54,7 @@ ice-$(CONFIG_PCI_IOV) += \ 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_dpll.o ice_tspll.o +ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ice_tspll.o ice_cpi.o ice_txclk.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/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c index 641d6e289d5c..22b7d8e6bd9e 100644 --- a/drivers/net/ethernet/intel/ice/devlink/devlink.c +++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c @@ -671,10 +671,10 @@ static int ice_devlink_tx_sched_layers_set(struct devlink *devlink, u32 id, * error. */ static int ice_devlink_tx_sched_layers_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - if (val.vu8 != ICE_SCHED_5_LAYERS && val.vu8 != ICE_SCHED_9_LAYERS) { + if (val->vu8 != ICE_SCHED_5_LAYERS && val->vu8 != ICE_SCHED_9_LAYERS) { NL_SET_ERR_MSG_MOD(extack, "Wrong number of tx scheduler layers provided."); return -EINVAL; @@ -1398,7 +1398,7 @@ static int ice_devlink_enable_roce_set(struct devlink *devlink, u32 id, static int ice_devlink_enable_roce_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); @@ -1465,7 +1465,7 @@ static int ice_devlink_enable_iw_set(struct devlink *devlink, u32 id, static int ice_devlink_enable_iw_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); @@ -1591,10 +1591,10 @@ static int ice_devlink_local_fwd_set(struct devlink *devlink, u32 id, * error. */ static int ice_devlink_local_fwd_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - if (ice_devlink_local_fwd_str_to_mode(val.vstr) < 0) { + if (ice_devlink_local_fwd_str_to_mode(val->vstr) < 0) { NL_SET_ERR_MSG_MOD(extack, "Error: Requested value is not supported."); return -EINVAL; } @@ -1604,12 +1604,12 @@ static int ice_devlink_local_fwd_validate(struct devlink *devlink, u32 id, static int ice_devlink_msix_max_pf_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); - if (val.vu32 > pf->hw.func_caps.common_cap.num_msix_vectors) + if (val->vu32 > pf->hw.func_caps.common_cap.num_msix_vectors) return -EINVAL; return 0; @@ -1617,21 +1617,21 @@ ice_devlink_msix_max_pf_validate(struct devlink *devlink, u32 id, static int ice_devlink_msix_min_pf_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - if (val.vu32 < ICE_MIN_MSIX) + if (val->vu32 < ICE_MIN_MSIX) return -EINVAL; return 0; } static int ice_devlink_enable_rdma_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); - bool new_state = val.vbool; + bool new_state = val->vbool; if (new_state && !test_bit(ICE_FLAG_RDMA_ENA, pf->flags)) return -EOPNOTSUPP; @@ -1791,16 +1791,16 @@ int ice_devlink_register_params(struct ice_pf *pf) value.vu32 = pf->msix.max; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX, - value); + &value); value.vu32 = pf->msix.min; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN, - value); + &value); value.vbool = test_bit(ICE_FLAG_RDMA_ENA, pf->flags); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA, - value); + &value); return 0; diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 725b130dd3a2..f72bb1aa4067 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -1155,4 +1155,16 @@ static inline struct ice_hw *ice_get_primary_hw(struct ice_pf *pf) else return &pf->adapter->ctrl_pf->hw; } + +/** + * ice_get_ctrl_pf - Get pointer to Control PF of the adapter + * @pf: pointer to the current PF structure + * + * Return: A pointer to ice_pf structure which is Control PF, + * NULL if it's not initialized yet. + */ +static inline struct ice_pf *ice_get_ctrl_pf(struct ice_pf *pf) +{ + return !pf->adapter ? NULL : pf->adapter->ctrl_pf; +} #endif /* _ICE_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c b/drivers/net/ethernet/intel/ice/ice_adapter.c index cbb57060bd56..2dc3629d6d0f 100644 --- a/drivers/net/ethernet/intel/ice/ice_adapter.c +++ b/drivers/net/ethernet/intel/ice/ice_adapter.c @@ -62,6 +62,8 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev) adapter->index = ice_adapter_index(pdev); spin_lock_init(&adapter->ptp_gltsyn_time_lock); spin_lock_init(&adapter->txq_ctx_lock); + for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++) + mutex_init(&adapter->cpi_phy_lock[i]); refcount_set(&adapter->refcount, 1); mutex_init(&adapter->ports.lock); @@ -73,6 +75,8 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev) static void ice_adapter_free(struct ice_adapter *adapter) { WARN_ON(!list_empty(&adapter->ports.ports)); + for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++) + mutex_destroy(&adapter->cpi_phy_lock[i]); mutex_destroy(&adapter->ports.lock); kfree(adapter); diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h b/drivers/net/ethernet/intel/ice/ice_adapter.h index e95266c7f20b..4f695f32da3d 100644 --- a/drivers/net/ethernet/intel/ice/ice_adapter.h +++ b/drivers/net/ethernet/intel/ice/ice_adapter.h @@ -5,9 +5,12 @@ #define _ICE_ADAPTER_H_ #include +#include #include #include +#include "ice_type.h" + struct pci_dev; struct ice_pf; @@ -31,6 +34,8 @@ struct ice_port_list { * @ptp_gltsyn_time_lock: Spinlock protecting access to the GLTSYN_TIME * register of the PTP clock. * @txq_ctx_lock: Spinlock protecting access to the GLCOMM_QTX_CNTX_CTL register + * @cpi_phy_lock: Per-PHY mutex serializing CPI REQ/ACK transactions. + * Index 0 = PHY0, index 1 = PHY1. Used on E825C devices. * @ctrl_pf: Control PF of the adapter * @ports: Ports list * @index: 64-bit index cached for collision detection on 32bit systems @@ -41,6 +46,8 @@ struct ice_adapter { spinlock_t ptp_gltsyn_time_lock; /* For access to GLCOMM_QTX_CNTX_CTL register */ spinlock_t txq_ctx_lock; + /* Serialize CPI REQ/ACK transactions per PHY (E825C only) */ + struct mutex cpi_phy_lock[ICE_E825_MAX_PHYS]; struct ice_pf *ctrl_pf; struct ice_port_list ports; diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 3cbb1b0582e3..42878abac9eb 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -1169,6 +1169,8 @@ struct ice_aqc_restart_an { u8 cmd_flags; #define ICE_AQC_RESTART_AN_LINK_RESTART BIT(1) #define ICE_AQC_RESTART_AN_LINK_ENABLE BIT(2) +#define ICE_AQC_RESTART_AN_REFCLK_M GENMASK(4, 3) +#define ICE_AQC_RESTART_AN_REFCLK_NOCHANGE 0 u8 reserved2[13]; }; diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index b617a6bff891..31e0de9e7f60 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -4124,12 +4124,13 @@ int ice_get_link_status(struct ice_port_info *pi, bool *link_up) * @pi: pointer to the port information structure * @ena_link: if true: enable link, if false: disable link * @cd: pointer to command details structure or NULL + * @refclk: the new TX reference clock, 0 if no change * * Sets up the link and restarts the Auto-Negotiation over the link. */ int ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link, - struct ice_sq_cd *cd) + struct ice_sq_cd *cd, u8 refclk) { struct ice_aqc_restart_an *cmd; struct libie_aq_desc desc; @@ -4145,6 +4146,8 @@ ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link, else cmd->cmd_flags &= ~ICE_AQC_RESTART_AN_LINK_ENABLE; + cmd->cmd_flags |= FIELD_PREP(ICE_AQC_RESTART_AN_REFCLK_M, refclk); + return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd); } diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h index e700ac0dc347..9f5344212195 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.h +++ b/drivers/net/ethernet/intel/ice/ice_common.h @@ -215,7 +215,7 @@ ice_cfg_phy_fec(struct ice_port_info *pi, struct ice_aqc_set_phy_cfg_data *cfg, enum ice_fec_mode fec); int ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link, - struct ice_sq_cd *cd); + struct ice_sq_cd *cd, u8 refclk); int ice_aq_set_mac_cfg(struct ice_hw *hw, u16 max_frame_size, struct ice_sq_cd *cd); int diff --git a/drivers/net/ethernet/intel/ice/ice_cpi.c b/drivers/net/ethernet/intel/ice/ice_cpi.c new file mode 100644 index 000000000000..0faa7f1de321 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_cpi.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2026 Intel Corporation */ + +#include "ice_type.h" +#include "ice_common.h" +#include "ice_ptp_hw.h" +#include "ice.h" +#include "ice_cpi.h" + +/** + * ice_cpi_get_dest_dev - get destination PHY for given phy index + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * + * Return: sideband queue destination PHY device. + */ +static enum ice_sbq_dev_id ice_cpi_get_dest_dev(struct ice_hw *hw, u8 phy) +{ + u8 curr_phy = hw->lane_num / hw->ptp.ports_per_phy; + + /* In the driver, lanes 4..7 are in fact 0..3 on a second PHY. + * On a single complex E825C, PHY 0 is always destination device phy_0 + * and PHY 1 is phy_0_peer. + * On dual complex E825C, device phy_0 points to PHY on a current + * complex and phy_0_peer to PHY on a different complex. + */ + if ((!ice_is_dual(hw) && phy) || + (ice_is_dual(hw) && phy != curr_phy)) + return ice_sbq_dev_phy_0_peer; + else + return ice_sbq_dev_phy_0; +} + +/** + * ice_cpi_write_phy - Write a CPI port register + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * @addr: PHY register address + * @val: Value to write + * + * Return: + * * 0 on success + * * other error codes when failed to write to PHY + */ +static int ice_cpi_write_phy(struct ice_hw *hw, u8 phy, u32 addr, u32 val) +{ + struct ice_sbq_msg_input msg = { + .dest_dev = ice_cpi_get_dest_dev(hw, phy), + .opcode = ice_sbq_msg_wr_np, + .msg_addr_low = lower_16_bits(addr), + .msg_addr_high = upper_16_bits(addr), + .data = val + }; + int err; + + err = ice_sbq_rw_reg(hw, &msg, LIBIE_AQ_FLAG_RD); + if (err) + ice_debug(hw, ICE_DBG_PTP, + "Failed to write CPI msg to phy %d, err: %d\n", + phy, err); + + return err; +} + +/** + * ice_cpi_read_phy - Read a CPI port register + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * @addr: PHY register address + * @val: storage for register value + * + * Return: + * * 0 on success + * * other error codes when failed to read from PHY + */ +static int ice_cpi_read_phy(struct ice_hw *hw, u8 phy, u32 addr, u32 *val) +{ + struct ice_sbq_msg_input msg = { + .dest_dev = ice_cpi_get_dest_dev(hw, phy), + .opcode = ice_sbq_msg_rd, + .msg_addr_low = lower_16_bits(addr), + .msg_addr_high = upper_16_bits(addr) + }; + int err; + + err = ice_sbq_rw_reg(hw, &msg, LIBIE_AQ_FLAG_RD); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to read CPI msg from phy %d, err: %d\n", + phy, err); + return err; + } + + *val = msg.data; + + return 0; +} + +/** + * ice_cpi_wait_req0_ack0 - waits for CPI interface to be available + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * + * This function checks if CPI interface is ready to use by CPI client. + * It's done by assuring LM.CMD.REQ and PHY.CMD.ACK bit in CPI + * interface registers to be 0. + * + * Return: 0 on success, negative on error + */ +static int ice_cpi_wait_req0_ack0(struct ice_hw *hw, int phy) +{ + u32 phy_val; + u32 lm_val; + + for (int i = 0; i < CPI_RETRIES_COUNT; i++) { + int err; + + /* check if another CPI Client is also accessing CPI */ + err = ice_cpi_read_phy(hw, phy, CPI0_LM1_CMD_DATA, &lm_val); + if (err) + return err; + if (FIELD_GET(CPI_LM_CMD_REQ_M, lm_val)) + goto retry; + + /* check if PHY.ACK is deasserted */ + err = ice_cpi_read_phy(hw, phy, CPI0_PHY1_CMD_DATA, &phy_val); + if (err) + return err; + if (!FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val)) + /* req0 and ack0 at this point - ready to go */ + return 0; + +retry: + msleep(CPI_RETRIES_CADENCE_MS); + } + + return -ETIMEDOUT; +} + +/** + * ice_cpi_wait_ack - Waits for the PHY.ACK bit to be asserted/deasserted + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * @asserted: desired state of PHY.ACK bit + * @data: pointer to the user data where PHY.data is stored + * + * This function checks if PHY.ACK bit is asserted or deasserted, depending + * on the phase of CPI handshake. If 'asserted' state is required, PHY command + * data is stored in the 'data' storage. + * + * Return: 0 on success, negative on error + */ +static int ice_cpi_wait_ack(struct ice_hw *hw, u8 phy, bool asserted, + u32 *data) +{ + u32 phy_val; + + for (int i = 0; i < CPI_RETRIES_COUNT; i++) { + int err; + + err = ice_cpi_read_phy(hw, phy, CPI0_PHY1_CMD_DATA, &phy_val); + if (err) + return err; + if (asserted && FIELD_GET(CPI_PHY_CMD_ERROR_M, phy_val)) + return -EFAULT; + if (asserted && FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val)) { + if (data) + *data = phy_val; + return 0; + } + if (!asserted && !FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val)) + return 0; + + msleep(CPI_RETRIES_CADENCE_MS); + } + + return -ETIMEDOUT; +} + +#define ice_cpi_wait_ack0(hw, port) \ + ice_cpi_wait_ack(hw, port, false, NULL) + +#define ice_cpi_wait_ack1(hw, port, data) \ + ice_cpi_wait_ack(hw, port, true, data) + +/** + * ice_cpi_req0 - deasserts LM.REQ bit + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * @data: the command data + * + * Return: 0 on success, negative on CPI write error + */ +static int ice_cpi_req0(struct ice_hw *hw, u8 phy, u32 data) +{ + data &= ~CPI_LM_CMD_REQ_M; + + return ice_cpi_write_phy(hw, phy, CPI0_LM1_CMD_DATA, data); +} + +/** + * ice_cpi_exec_cmd - writes command data to CPI interface + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * @data: the command data + * + * Return: 0 on success, otherwise negative on error + */ +static int ice_cpi_exec_cmd(struct ice_hw *hw, int phy, u32 data) +{ + return ice_cpi_write_phy(hw, phy, CPI0_LM1_CMD_DATA, data); +} + +/** + * ice_cpi_phy_lock - get per-PHY lock for CPI transaction serialization + * @hw: pointer to the HW struct + * @phy: PHY index + * + * Return: pointer to PHY mutex, or %NULL when context is unavailable. + */ +static struct mutex *ice_cpi_phy_lock(struct ice_hw *hw, u8 phy) +{ + struct ice_pf *pf = hw->back; + + if (!pf || !pf->adapter || phy >= ICE_E825_MAX_PHYS) + return NULL; + + return &pf->adapter->cpi_phy_lock[phy]; +} + +/** + * ice_cpi_exec - executes CPI command + * @hw: pointer to the HW struct + * @phy: phy index of port the CPI action is taken on + * @cmd: pointer to the command struct to execute + * @resp: pointer to user allocated CPI response struct + * + * This function executes CPI request with respect to CPI handshake + * mechanism. + * + * Return: 0 on success, otherwise negative on error + */ +int ice_cpi_exec(struct ice_hw *hw, u8 phy, + const struct ice_cpi_cmd *cmd, + struct ice_cpi_resp *resp) +{ + struct mutex *cpi_lock; /* serializes CPI transactions per PHY */ + u32 phy_cmd, lm_cmd = 0; + int err, err1 = 0; + + if (!cmd || !resp) + return -EINVAL; + + cpi_lock = ice_cpi_phy_lock(hw, phy); + if (!cpi_lock) + return -EINVAL; + + mutex_lock(cpi_lock); + + lm_cmd = + FIELD_PREP(CPI_LM_CMD_REQ_M, CPI_LM_CMD_REQ) | + FIELD_PREP(CPI_LM_CMD_GET_SET_M, cmd->set) | + FIELD_PREP(CPI_LM_CMD_OPCODE_M, cmd->opcode) | + FIELD_PREP(CPI_LM_CMD_PORTLANE_M, cmd->port) | + FIELD_PREP(CPI_LM_CMD_DATA_M, cmd->data); + + /* 1. Try to acquire the bus, PHY ACK should be low before we begin */ + err = ice_cpi_wait_req0_ack0(hw, phy); + if (err) + goto cpi_exec_exit; + + /* 2. We start the CPI request */ + err = ice_cpi_exec_cmd(hw, phy, lm_cmd); + if (err) + goto cpi_deassert; + + /* + * 3. Wait for CPI confirmation, PHY ACK should be asserted and opcode + * echoed in the response + */ + err = ice_cpi_wait_ack1(hw, phy, &phy_cmd); + if (err) + goto cpi_deassert; + + if (FIELD_GET(CPI_LM_CMD_OPCODE_M, lm_cmd) != + FIELD_GET(CPI_PHY_CMD_OPCODE_M, phy_cmd)) { + err = -EFAULT; + goto cpi_deassert; + } + + resp->opcode = FIELD_GET(CPI_PHY_CMD_OPCODE_M, phy_cmd); + resp->data = FIELD_GET(CPI_PHY_CMD_DATA_M, phy_cmd); + resp->port = FIELD_GET(CPI_PHY_CMD_PORTLANE_M, phy_cmd); + +cpi_deassert: + /* 4. We deassert REQ */ + err1 = ice_cpi_req0(hw, phy, lm_cmd); + if (err1) + goto cpi_exec_exit; + + /* 5. PHY ACK should be deasserted in response */ + err1 = ice_cpi_wait_ack0(hw, phy); + +cpi_exec_exit: + if (!err) + err = err1; + + mutex_unlock(cpi_lock); + + return err; +} + +/** + * ice_cpi_set_cmd - execute CPI SET command + * @hw: pointer to the HW struct + * @opcode: CPI command opcode + * @phy: phy index CPI command is applied for + * @port_lane: ephy index CPI command is applied for + * @data: CPI opcode context specific data + * + * Return: 0 on success, negative error code on failure. + */ +static int ice_cpi_set_cmd(struct ice_hw *hw, u8 opcode, u8 phy, u8 port_lane, + u16 data) +{ + struct ice_cpi_resp cpi_resp = {0}; + struct ice_cpi_cmd cpi_cmd = { + .opcode = opcode, + .set = true, + .port = port_lane, + .data = data, + }; + + return ice_cpi_exec(hw, phy, &cpi_cmd, &cpi_resp); +} + +/** + * ice_cpi_ena_dis_clk_ref - enables/disables Tx reference clock on port + * @hw: pointer to the HW struct + * @phy: phy index of port for which Tx reference clock is enabled/disabled + * @clk: Tx reference clock to enable or disable + * @enable: bool value to enable or disable Tx reference clock + * + * This function executes CPI request to enable or disable specific + * Tx reference clock on given PHY. + * + * Return: 0 on success, negative error code on failure. + */ +int ice_cpi_ena_dis_clk_ref(struct ice_hw *hw, u8 phy, + enum ice_e825c_ref_clk clk, bool enable) +{ + u16 val; + + val = FIELD_PREP(CPI_OPCODE_PHY_CLK_PHY_SEL_M, phy) | + FIELD_PREP(CPI_OPCODE_PHY_CLK_REF_CTRL_M, + enable ? CPI_OPCODE_PHY_CLK_ENABLE : + CPI_OPCODE_PHY_CLK_DISABLE) | + FIELD_PREP(CPI_OPCODE_PHY_CLK_REF_SEL_M, clk); + + return ice_cpi_set_cmd(hw, CPI_OPCODE_PHY_CLK, phy, 0, val); +} + diff --git a/drivers/net/ethernet/intel/ice/ice_cpi.h b/drivers/net/ethernet/intel/ice/ice_cpi.h new file mode 100644 index 000000000000..6a45a920f8c2 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_cpi.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef _ICE_CPI_H_ +#define _ICE_CPI_H_ + +#include "ice_type.h" +#include "ice_ptp_hw.h" + +#define CPI0_PHY1_CMD_DATA 0x7FD028 +#define CPI0_LM1_CMD_DATA 0x7FD024 +#define CPI_RETRIES_COUNT 10 +#define CPI_RETRIES_CADENCE_MS 100 + +/* CPI PHY CMD DATA register (CPI0_PHY1_CMD_DATA) */ +#define CPI_PHY_CMD_DATA_M GENMASK(15, 0) +#define CPI_PHY_CMD_OPCODE_M GENMASK(23, 16) +#define CPI_PHY_CMD_PORTLANE_M GENMASK(26, 24) +#define CPI_PHY_CMD_RSVD_M GENMASK(29, 27) +#define CPI_PHY_CMD_ERROR_M BIT(30) +#define CPI_PHY_CMD_ACK_M BIT(31) + +/* CPI LM CMD DATA register (CPI0_LM1_CMD_DATA) */ +#define CPI_LM_CMD_DATA_M GENMASK(15, 0) +#define CPI_LM_CMD_OPCODE_M GENMASK(23, 16) +#define CPI_LM_CMD_PORTLANE_M GENMASK(26, 24) +#define CPI_LM_CMD_RSVD_M GENMASK(28, 27) +#define CPI_LM_CMD_GET_SET_M BIT(29) +#define CPI_LM_CMD_REQ_M BIT(31) + +#define CPI_OPCODE_PHY_CLK 0xF1 +#define CPI_OPCODE_PHY_CLK_PHY_SEL_M GENMASK(9, 6) +#define CPI_OPCODE_PHY_CLK_REF_CTRL_M GENMASK(5, 4) +#define CPI_OPCODE_PHY_CLK_DISABLE 1 +#define CPI_OPCODE_PHY_CLK_ENABLE 2 +#define CPI_OPCODE_PHY_CLK_REF_SEL_M GENMASK(3, 0) + +#define CPI_LM_CMD_REQ 1 + +struct ice_cpi_cmd { + u8 port; + u8 opcode; + u16 data; + bool set; +}; + +struct ice_cpi_resp { + u8 port; + u8 opcode; + u16 data; +}; + +int ice_cpi_exec(struct ice_hw *hw, u8 phy, + const struct ice_cpi_cmd *cmd, + struct ice_cpi_resp *resp); +int ice_cpi_ena_dis_clk_ref(struct ice_hw *hw, u8 phy, + enum ice_e825c_ref_clk clk, bool enable); +#endif /* _ICE_CPI_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 0704e92ab043..462c69cc11e1 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -4,6 +4,7 @@ #include "ice.h" #include "ice_lib.h" #include "ice_trace.h" +#include "ice_txclk.h" #include #include @@ -19,6 +20,9 @@ #define ICE_DPLL_SW_PIN_INPUT_BASE_QSFP 6 #define ICE_DPLL_SW_PIN_OUTPUT_BASE 0 +#define E825_RCLK_PARENT_0_PIN_IDX 0 +#define E825_RCLK_PARENT_1_PIN_IDX 1 + #define ICE_DPLL_PIN_SW_INPUT_ABS(in_idx) \ (ICE_DPLL_SW_PIN_INPUT_BASE_SFP + (in_idx)) @@ -57,6 +61,7 @@ * @ICE_DPLL_PIN_TYPE_OUTPUT: output pin * @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin * @ICE_DPLL_PIN_TYPE_SOFTWARE: software controlled SMA/U.FL pins + * @ICE_DPLL_PIN_TYPE_TXCLK: transmit clock reference input pin */ enum ice_dpll_pin_type { ICE_DPLL_PIN_INVALID, @@ -64,6 +69,7 @@ enum ice_dpll_pin_type { ICE_DPLL_PIN_TYPE_OUTPUT, ICE_DPLL_PIN_TYPE_RCLK_INPUT, ICE_DPLL_PIN_TYPE_SOFTWARE, + ICE_DPLL_PIN_TYPE_TXCLK, }; static const char * const pin_type_name[] = { @@ -71,10 +77,13 @@ static const char * const pin_type_name[] = { [ICE_DPLL_PIN_TYPE_OUTPUT] = "output", [ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input", [ICE_DPLL_PIN_TYPE_SOFTWARE] = "software", + [ICE_DPLL_PIN_TYPE_TXCLK] = "txclk-input", }; static const char * const ice_dpll_sw_pin_sma[] = { "SMA1", "SMA2" }; static const char * const ice_dpll_sw_pin_ufl[] = { "U.FL1", "U.FL2" }; +static const char * const ice_dpll_ext_eref_pin = "EXT_EREF0"; +static const char * const ice_dpll_fwnode_ext_synce = "clk_ref_synce"; static const struct dpll_pin_frequency ice_esync_range[] = { DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ), @@ -2605,12 +2614,206 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv, return ret; } +/** + * ice_dpll_txclk_work - apply a pending TX reference clock change + * @work: work_struct embedded in struct ice_dplls + * + * This worker executes an outstanding TX reference clock switch request + * that was previously queued via the DPLL TXCLK pin set callback. + * + * The worker performs only the operational part of the switch, issuing + * the necessary firmware commands to request a new TX reference clock + * selection (e.g. triggering an AN restart). It does not verify whether + * the requested clock was ultimately accepted by the hardware. + * + * Hardware verification, software state reconciliation, pin state + * notification, and TXC DPLL lock-status updates are performed later, + * after link-up, by ice_txclk_update_and_notify(). + * + * Context: + * - Runs in process context on pf->dplls.wq and may sleep. + * - Serializes access to shared TXCLK state using pf->dplls.lock. + */ +static void ice_dpll_txclk_work(struct work_struct *work) +{ + struct ice_dplls *dplls = + container_of(work, struct ice_dplls, txclk_work); + struct ice_pf *pf = container_of(dplls, struct ice_pf, dplls); + struct dpll_pin *old_pin = NULL; + struct dpll_pin *new_pin = NULL; + enum ice_e825c_ref_clk clk; + bool do_switch; + int err; + + mutex_lock(&pf->dplls.lock); + do_switch = pf->dplls.txclk_switch_requested; + clk = pf->ptp.port.tx_clk_req; + mutex_unlock(&pf->dplls.lock); + + if (!do_switch) + return; + + err = ice_txclk_set_clk(pf, clk); + + mutex_lock(&pf->dplls.lock); + /* Only clear the request flag if no newer request arrived while + * the lock was dropped. Otherwise leave it set so the re-queued + * worker run picks up the updated tx_clk_req value. + */ + if (pf->ptp.port.tx_clk_req == clk) + pf->dplls.txclk_switch_requested = false; + if (err) { + /* Roll back the requested clock to match the current hardware + * state so that ice_txclk_update_and_notify() does not + * misinterpret a future link-up as a failed switch. Only roll + * back if no newer request arrived in the meantime; otherwise + * the re-queued worker run will apply the updated value. + */ + dev_err(ice_pf_to_dev(pf), + "TX clock switch to %u failed, err=%d; reverting\n", + clk, err); + if (pf->ptp.port.tx_clk_req == clk) { + /* Capture pins for post-unlock notification so that + * userspace observes the requested pin flipping back + * to DISCONNECTED and the effective pin to CONNECTED. + */ + new_pin = ice_txclk_get_pin(pf, clk); + old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk); + pf->ptp.port.tx_clk_req = pf->ptp.port.tx_clk; + } + } + mutex_unlock(&pf->dplls.lock); + + if (old_pin) + dpll_pin_change_ntf(old_pin); + if (new_pin) + dpll_pin_change_ntf(new_pin); +} + +/** + * ice_dpll_txclk_state_on_dpll_set - set a state on TX clk pin + * @pin: pointer to a pin + * @pin_priv: private data pointer passed on pin registration + * @dpll: registered dpll pointer + * @dpll_priv: private data pointer passed on dpll registration + * @state: state to be set on pin + * @extack: error reporting + * + * Dpll subsystem callback, set a state of a Tx reference clock pin + * + * Context: Acquires and releases pf->dplls.lock. + * Return: + * * 0 - success + * * negative - failure + */ +static int +ice_dpll_txclk_state_on_dpll_set(const struct dpll_pin *pin, void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, enum dpll_pin_state state, + struct netlink_ext_ack *extack) +{ + struct ice_dpll_pin *p = pin_priv; + struct ice_pf *pf = p->pf; + enum ice_e825c_ref_clk new_clk; + int ret = 0; + + if (ice_dpll_is_reset(pf, extack)) + return -EBUSY; + + if (state != DPLL_PIN_STATE_CONNECTED && + state != DPLL_PIN_STATE_DISCONNECTED) { + NL_SET_ERR_MSG(extack, + "unsupported pin state for TX reference clock"); + return -EINVAL; + } + + /* Check ICE_FLAG_DPLL and queue_work() under pf->dplls.lock. + * ice_dpll_deinit() clears the flag under the same lock before + * cancel_work_sync() and wq destruction, so a callback arriving + * after teardown observes the cleared flag and bails out. + */ + mutex_lock(&pf->dplls.lock); + if (!test_bit(ICE_FLAG_DPLL, pf->flags)) { + ret = -ENODEV; + goto unlock; + } + if (state == DPLL_PIN_STATE_DISCONNECTED && + p->tx_ref_src != pf->ptp.port.tx_clk_req) + goto unlock; + + new_clk = (state == DPLL_PIN_STATE_DISCONNECTED) ? ICE_REF_CLK_ENET : + p->tx_ref_src; + if (new_clk == pf->ptp.port.tx_clk_req) + goto unlock; + + pf->ptp.port.tx_clk_req = new_clk; + pf->dplls.txclk_switch_requested = true; + queue_work(pf->dplls.wq, &pf->dplls.txclk_work); +unlock: + mutex_unlock(&pf->dplls.lock); + return ret; +} + +/** + * ice_dpll_txclk_state_on_dpll_get - get a state of Tx clk reference pin + * @pin: pointer to a pin + * @pin_priv: private data pointer passed on pin registration + * @dpll: registered dpll pointer + * @dpll_priv: private data pointer passed on dpll registration + * @state: on success holds pin state on parent pin + * @extack: error reporting + * + * TXCLK DPLL pin state is derived and not stored explicitly. + * + * Only external TX reference clocks (SYNCE, EREF0) are modeled + * as DPLL pins. The internal ENET (TXCO) clock has no pin and, + * when selected, all TXCLK pins are reported DISCONNECTED. + * + * During a pending TXCLK switch, the requested pin may be + * reported as CONNECTED before hardware verification. + * Hardware acceptance and synchronization are reported + * exclusively via TXC DPLL lock-status. + * + * Context: Acquires and releases pf->dplls.lock + * Return: + * * 0 - success + * * negative - failure + */ +static int +ice_dpll_txclk_state_on_dpll_get(const struct dpll_pin *pin, void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state *state, + struct netlink_ext_ack *extack) +{ + struct ice_dpll_pin *p = pin_priv; + struct ice_pf *pf = p->pf; + + if (ice_dpll_is_reset(pf, extack)) + return -EBUSY; + + mutex_lock(&pf->dplls.lock); + if (pf->ptp.port.tx_clk_req == p->tx_ref_src) + *state = DPLL_PIN_STATE_CONNECTED; + else + *state = DPLL_PIN_STATE_DISCONNECTED; + mutex_unlock(&pf->dplls.lock); + + return 0; +} + static const struct dpll_pin_ops ice_dpll_rclk_ops = { .state_on_pin_set = ice_dpll_rclk_state_on_pin_set, .state_on_pin_get = ice_dpll_rclk_state_on_pin_get, .direction_get = ice_dpll_input_direction, }; +static const struct dpll_pin_ops ice_dpll_txclk_ops = { + .state_on_dpll_set = ice_dpll_txclk_state_on_dpll_set, + .state_on_dpll_get = ice_dpll_txclk_state_on_dpll_get, + .direction_get = ice_dpll_input_direction, +}; + static const struct dpll_pin_ops ice_dpll_pin_sma_ops = { .state_on_dpll_set = ice_dpll_sma_pin_state_set, .state_on_dpll_get = ice_dpll_sw_pin_state_get, @@ -3135,9 +3338,13 @@ ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins, { int i; - for (i = 0; i < count; i++) - if (!pins[i].hidden) - dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]); + for (i = 0; i < count; i++) { + if (pins[i].hidden) + continue; + if (IS_ERR_OR_NULL(pins[i].pin)) + continue; + dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]); + } } /** @@ -3311,19 +3518,42 @@ static bool ice_dpll_is_fwnode_pin(struct ice_dpll_pin *pin) return !IS_ERR_OR_NULL(pin->fwnode); } +static bool ice_dpll_fwnode_eq(const struct fwnode_handle *a, + const struct fwnode_handle *b) +{ + return a && a == b; +} + static void ice_dpll_pin_notify_work(struct work_struct *work) { struct ice_dpll_pin_work *w = container_of(work, struct ice_dpll_pin_work, work); struct ice_dpll_pin *pin, *parent = w->pin; + bool is_tx_synce_parent = false; struct ice_pf *pf = parent->pf; + bool is_rclk_parent = false; int ret; wait_for_completion(&pf->dplls.dpll_init); if (!test_bit(ICE_FLAG_DPLL, pf->flags)) goto out; /* DPLL initialization failed */ + /* Decide which parent we are handling, defensively checking FWNs */ + for (int i = 0; i < pf->dplls.rclk.num_parents; i++) { + if (ice_dpll_fwnode_eq(parent->fwnode, + pf->dplls.inputs[i].fwnode)) { + is_rclk_parent = true; + break; + } + } + + is_tx_synce_parent = + ice_dpll_fwnode_eq(parent->fwnode, + pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX].fwnode); + if (!is_rclk_parent && !is_tx_synce_parent) + goto out; + switch (w->action) { case DPLL_PIN_CREATED: if (!IS_ERR_OR_NULL(parent->pin)) { @@ -3340,16 +3570,28 @@ static void ice_dpll_pin_notify_work(struct work_struct *work) goto out; } - /* Register rclk pin */ - pin = &pf->dplls.rclk; - ret = dpll_pin_on_pin_register(parent->pin, pin->pin, - &ice_dpll_rclk_ops, pin); - if (ret) { - dev_err(ice_pf_to_dev(pf), - "Failed to register pin: %pe\n", ERR_PTR(ret)); - dpll_pin_put(parent->pin, &parent->tracker); - parent->pin = NULL; - goto out; + if (is_rclk_parent) { + /* Register rclk pin via on-pin relationship */ + pin = &pf->dplls.rclk; + ret = dpll_pin_on_pin_register(parent->pin, pin->pin, + &ice_dpll_rclk_ops, pin); + if (ret) { + dev_err(ice_pf_to_dev(pf), + "RCLK pin register failed: %pe\n", + ERR_PTR(ret)); + goto drop_parent_ref; + } + } else if (is_tx_synce_parent) { + /* Register TX-CLK SYNCE pin directly to TXC DPLL */ + pin = &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX]; + ret = dpll_pin_register(pf->dplls.txc.dpll, pin->pin, + &ice_dpll_txclk_ops, pin); + if (ret) { + dev_err(ice_pf_to_dev(pf), + "TX SYNCE pin register failed: %pe\n", + ERR_PTR(ret)); + goto drop_parent_ref; + } } break; case DPLL_PIN_DELETED: @@ -3358,11 +3600,18 @@ static void ice_dpll_pin_notify_work(struct work_struct *work) goto out; } - /* Unregister rclk pin */ - pin = &pf->dplls.rclk; - dpll_pin_on_pin_unregister(parent->pin, pin->pin, - &ice_dpll_rclk_ops, pin); - + if (is_rclk_parent) { + /* Unregister rclk pin */ + pin = &pf->dplls.rclk; + dpll_pin_on_pin_unregister(parent->pin, pin->pin, + &ice_dpll_rclk_ops, pin); + } else if (is_tx_synce_parent) { + /* Unregister TX-CLK SYNCE pin from TXC DPLL */ + pin = &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX]; + dpll_pin_unregister(pf->dplls.txc.dpll, pin->pin, + &ice_dpll_txclk_ops, pin); + } +drop_parent_ref: /* Drop fwnode pin reference */ dpll_pin_put(parent->pin, &parent->tracker); parent->pin = NULL; @@ -3388,6 +3637,12 @@ static int ice_dpll_pin_notify(struct notifier_block *nb, unsigned long action, if (pin->fwnode != info->fwnode) return NOTIFY_DONE; /* Not this pin */ + /* Ignore notification which are the outcome of internal pin + * registration/unregistration calls - synce pin case. + */ + if (info->src_clock_id == pin->pf->dplls.clock_id) + return NOTIFY_DONE; + work = kzalloc_obj(*work); if (!work) return NOTIFY_DONE; @@ -3490,10 +3745,16 @@ ice_dpll_init_rclk_pin(struct ice_pf *pf, int start_idx, } static void -ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin) +ice_dpll_stop_fwnode_pin_activity(struct ice_dpll_pin *pin, bool flush) { unregister_dpll_notifier(&pin->nb); - flush_workqueue(pin->pf->dplls.wq); + if (flush) + flush_workqueue(pin->pf->dplls.wq); +} + +static void +ice_dpll_release_fwnode_pin(struct ice_dpll_pin *pin) +{ if (!IS_ERR_OR_NULL(pin->pin)) { dpll_pin_put(pin->pin, &pin->tracker); pin->pin = NULL; @@ -3502,6 +3763,13 @@ ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin) pin->fwnode = NULL; } +static void +ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin) +{ + ice_dpll_stop_fwnode_pin_activity(pin, true); + ice_dpll_release_fwnode_pin(pin); +} + static void ice_dpll_deinit_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins, int start_idx) @@ -3513,6 +3781,26 @@ ice_dpll_deinit_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins, destroy_workqueue(pf->dplls.wq); } +static int ice_dpll_deinit_txclk_pins(struct ice_pf *pf) +{ + struct ice_dpll_pin *synce_pin = &pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX]; + struct ice_dpll *dt = &pf->dplls.txc; + + ice_dpll_stop_fwnode_pin_activity(synce_pin, true); + ice_dpll_unregister_pins(dt->dpll, pf->dplls.txclks, + &ice_dpll_txclk_ops, + ARRAY_SIZE(pf->dplls.txclks)); + ice_dpll_release_pins(&pf->dplls.txclks[E825_EXT_EREF_PIN_IDX], 1); + /* ice_dpll_release_pins() puts the pin but does not clear the slot, + * unlike ice_dpll_release_fwnode_pin() used for SYNCE below. NULL it + * so a late ice_txclk_get_pin() returns NULL rather than a dangling + * pointer. + */ + pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin = NULL; + ice_dpll_release_fwnode_pin(synce_pin); + return 0; +} + /** * ice_dpll_deinit_pins - deinitialize direct pins * @pf: board private structure @@ -3532,8 +3820,10 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu) struct ice_dpll *dp = &d->pps; ice_dpll_deinit_rclk_pin(pf); - if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) + if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) { + ice_dpll_deinit_txclk_pins(pf); ice_dpll_deinit_fwnode_pins(pf, pf->dplls.inputs, 0); + } if (cgu) { ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops, num_inputs); @@ -3639,7 +3929,7 @@ static int ice_dpll_init_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins, int start_idx) { - char pin_name[8]; + char pin_name[16]; int i, ret; pf->dplls.wq = create_singlethread_workqueue("ice_dpll_wq"); @@ -3656,6 +3946,11 @@ ice_dpll_init_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins, return 0; error: + /* + * A notifier worker may already be queued and blocked on dpll_init; + * release it so the per-pin flush below does not deadlock. + */ + complete_all(&pf->dplls.dpll_init); while (i--) ice_dpll_deinit_fwnode_pin(&pins[start_idx + i]); @@ -3664,6 +3959,64 @@ ice_dpll_init_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins, return ret; } +static int ice_dpll_init_txclk_pins(struct ice_pf *pf, int start_idx) +{ + struct ice_dpll_pin *ref_pin = pf->dplls.txclks; + struct ice_dpll *txc = &pf->dplls.txc; + int ret; + + /* Configure EXT_EREF0 pin */ + ret = ice_dpll_get_pins(pf, ref_pin, start_idx, 1, pf->dplls.clock_id); + if (ret) + return ret; + ret = dpll_pin_register(txc->dpll, ref_pin->pin, &ice_dpll_txclk_ops, + ref_pin); + if (ret) + goto err_release_ext_eref; + + /* + * Configure EXT_SYNCE pin (fwnode-backed). + * The pin may not yet be available; in that case registration + * will be deferred via the notifier path. + */ + ref_pin++; + ret = ice_dpll_init_fwnode_pin(ref_pin, ice_dpll_fwnode_ext_synce); + if (ret) + goto err_unregister_ext_eref; + + if (IS_ERR_OR_NULL(ref_pin->pin)) { + dev_dbg(ice_pf_to_dev(pf), + "Tx-clk SYNCE pin not registered yet\n"); + return 0; + } + + ret = dpll_pin_register(txc->dpll, ref_pin->pin, &ice_dpll_txclk_ops, + ref_pin); + if (ret) + goto err_deinit_synce; + + return 0; + +err_deinit_synce: + /* + * Avoid deadlock against notifier workers blocked on dpll_init. + * The outer init error path will complete dpll_init and flush the + * shared workqueue before destroying it. + */ + ice_dpll_stop_fwnode_pin_activity(ref_pin, false); + ice_dpll_release_fwnode_pin(ref_pin); +err_unregister_ext_eref: + dpll_pin_unregister(txc->dpll, + pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin, + &ice_dpll_txclk_ops, + &pf->dplls.txclks[E825_EXT_EREF_PIN_IDX]); + +err_release_ext_eref: + ice_dpll_release_pins(&pf->dplls.txclks[E825_EXT_EREF_PIN_IDX], 1); + + return ret; +} + /** * ice_dpll_init_pins_e825 - init pins and register pins with a dplls * @pf: board private structure @@ -3686,6 +4039,15 @@ static int ice_dpll_init_pins_e825(struct ice_pf *pf) ret = ice_dpll_init_rclk_pin(pf, DPLL_PIN_IDX_UNSPEC, &ice_dpll_rclk_ops); + + if (ret) + goto unregister_pins; + + ret = ice_dpll_init_txclk_pins(pf, 0); + if (ret) + ice_dpll_deinit_rclk_pin(pf); + +unregister_pins: if (ret) { /* Inform DPLL notifier works that DPLL init was finished * unsuccessfully (ICE_DPLL_FLAG not set). @@ -3804,7 +4166,7 @@ static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu) static void ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu) { - if (cgu) + if (cgu || pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) dpll_device_unregister(d->dpll, d->ops, d); dpll_device_put(d->dpll, &d->tracker); } @@ -3839,12 +4201,13 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu, return ret; } d->pf = pf; - if (cgu) { + if (cgu || pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) { const struct dpll_device_ops *ops = &ice_dpll_ops; if (type == DPLL_TYPE_PPS && ice_dpll_is_pps_phase_monitor(pf)) ops = &ice_dpll_pom_ops; - ice_dpll_update_state(pf, d, true); + if (cgu) + ice_dpll_update_state(pf, d, true); ret = dpll_device_register(d->dpll, type, ops, d); if (ret) { dpll_device_put(d->dpll, &d->tracker); @@ -4210,6 +4573,36 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf) return 0; } +/** + * ice_dpll_init_info_txclk_pins_e825c - initializes tx-clk pins information + * @pf: board private structure + * + * Init information for tx-clks pin, cache them in pf->dplls.txclks + * + * Return: + * * 0 - success + */ +static int ice_dpll_init_info_txclk_pins_e825c(struct ice_pf *pf) +{ + struct ice_dpll_pin *tx_pin; + + for (int i = 0; i < ICE_DPLL_TXCLK_NUM_MAX; i++) { + tx_pin = &pf->dplls.txclks[i]; + tx_pin->prop.type = DPLL_PIN_TYPE_EXT; + tx_pin->prop.capabilities |= + DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + tx_pin->pf = pf; + if (i == E825_EXT_EREF_PIN_IDX) { + tx_pin->prop.board_label = ice_dpll_ext_eref_pin; + tx_pin->tx_ref_src = ICE_REF_CLK_EREF0; + } else if (i == E825_EXT_SYNCE_PIN_IDX) { + tx_pin->tx_ref_src = ICE_REF_CLK_SYNCE; + } + } + + return 0; +} + /** * ice_dpll_init_pins_info - init pins info wrapper * @pf: board private structure @@ -4235,6 +4628,9 @@ ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type) return ice_dpll_init_info_rclk_pin(pf); case ICE_DPLL_PIN_TYPE_SOFTWARE: return ice_dpll_init_info_sw_pins(pf); + + case ICE_DPLL_PIN_TYPE_TXCLK: + return ice_dpll_init_info_txclk_pins_e825c(pf); default: return -EINVAL; } @@ -4268,11 +4664,15 @@ static void ice_dpll_deinit_info(struct ice_pf *pf) static int ice_dpll_init_info_e825c(struct ice_pf *pf) { struct ice_dplls *d = &pf->dplls; + struct ice_dpll *dt = &d->txc; int ret = 0; int i; d->clock_id = ice_generate_clock_id(pf); d->num_inputs = ICE_SYNCE_CLK_NUM; + dt->dpll_state = ice_txclk_lock_status(pf->ptp.port.tx_clk); + dt->mode = DPLL_MODE_MANUAL; + dt->dpll_idx = pf->ptp.port.port_num; d->inputs = kzalloc_objs(*d->inputs, d->num_inputs); if (!d->inputs) @@ -4289,6 +4689,11 @@ static int ice_dpll_init_info_e825c(struct ice_pf *pf) ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT); if (ret) goto deinit_info; + + ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_TXCLK); + if (ret) + goto deinit_info; + dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs: %u, outputs: %u, rclk-parents: %u\n", __func__, d->num_inputs, d->num_outputs, d->rclk.num_parents); @@ -4412,15 +4817,38 @@ void ice_dpll_deinit(struct ice_pf *pf) { bool cgu = ice_is_feature_supported(pf, ICE_F_CGU); + /* Clear ICE_FLAG_DPLL under the lock so that any new caller of + * ice_txclk_update_and_notify() observes the cleared flag and + * returns early. In-flight callers that already passed the flag + * check hold txclk_notify_rwsem for read across the out-of-lock + * dpll_*_change_ntf() calls; the down_write/up_write barrier + * below waits for them to finish before pins and the TXC DPLL + * device may be freed. + */ + mutex_lock(&pf->dplls.lock); clear_bit(ICE_FLAG_DPLL, pf->flags); + mutex_unlock(&pf->dplls.lock); + + /* Wait for in-flight ice_txclk_update_and_notify() readers */ + if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) { + down_write(&pf->dplls.txclk_notify_rwsem); + up_write(&pf->dplls.txclk_notify_rwsem); + } + if (cgu) ice_dpll_deinit_worker(pf); + if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) + cancel_work_sync(&pf->dplls.txclk_work); + ice_dpll_deinit_pins(pf, cgu); if (!IS_ERR_OR_NULL(pf->dplls.pps.dpll)) ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu); if (!IS_ERR_OR_NULL(pf->dplls.eec.dpll)) ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu); + if (!IS_ERR_OR_NULL(pf->dplls.txc.dpll)) + ice_dpll_deinit_dpll(pf, &pf->dplls.txc, false); + ice_dpll_deinit_info(pf); mutex_destroy(&pf->dplls.lock); } @@ -4440,20 +4868,42 @@ static void ice_dpll_init_e825(struct ice_pf *pf) struct ice_dplls *d = &pf->dplls; int err; + /* E825 sets ICE_F_PHY_RCLK unconditionally, so DPLL may run without + * PTP. Populate the HW-topology fields the TX-clk path divides by, + * otherwise userspace can trigger div-by-zero in ice_txclk_set_clk(). + * When PTP is supported, ice_ptp_init() handles this. + */ + if (!test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) { + ice_ptp_init_hw(&pf->hw); + if (pf->hw.lane_num >= 0) + pf->ptp.port.port_num = pf->hw.lane_num; + } + mutex_init(&d->lock); + /* Initialize the txclk worker and its notification rwsem before any + * code path can fail: ice_dpll_deinit() runs unconditionally on + * failure and calls cancel_work_sync() / down_write() on these. + */ + INIT_WORK(&d->txclk_work, ice_dpll_txclk_work); + init_rwsem(&d->txclk_notify_rwsem); init_completion(&d->dpll_init); err = ice_dpll_init_info_e825c(pf); if (err) goto err_exit; - err = ice_dpll_init_pins_e825(pf); + err = ice_dpll_init_dpll(pf, &pf->dplls.txc, false, DPLL_TYPE_GENERIC); if (err) goto deinit_info; + err = ice_dpll_init_pins_e825(pf); + if (err) + goto deinit_txclk; set_bit(ICE_FLAG_DPLL, pf->flags); complete_all(&d->dpll_init); return; +deinit_txclk: + ice_dpll_deinit_dpll(pf, &pf->dplls.txc, false); deinit_info: ice_dpll_deinit_info(pf); err_exit: diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h index 8678575359b9..103ba3e49068 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.h +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h @@ -7,6 +7,9 @@ #include "ice.h" #define ICE_DPLL_RCLK_NUM_MAX 4 +#define ICE_DPLL_TXCLK_NUM_MAX 2 +#define E825_EXT_EREF_PIN_IDX 0 +#define E825_EXT_SYNCE_PIN_IDX 1 #define ICE_CGU_R10 0x28 #define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) @@ -79,6 +82,7 @@ struct ice_dpll_pin { u8 ref_sync; bool active; bool hidden; + enum ice_e825c_ref_clk tx_ref_src; }; /** ice_dpll - store info required for DPLL control @@ -124,12 +128,15 @@ struct ice_dpll { /** ice_dplls - store info required for CCU (clock controlling unit) * @kworker: periodic worker * @work: periodic work - * @lock: locks access to configuration of a dpll + * @wq: workqueue used to schedule DPLL-related deferred work + * @lock: protects DPLL configuration (see Locking below) * @eec: pointer to EEC dpll dev * @pps: pointer to PPS dpll dev + * @txc: pointer to TXC dpll dev * @inputs: input pins pointer * @outputs: output pins pointer * @rclk: recovered pins pointer + * @txclks: TX clock reference pins pointer * @num_inputs: number of input pins available on dpll * @num_outputs: number of output pins available on dpll * @cgu_state_acq_err_num: number of errors returned during periodic work @@ -138,6 +145,28 @@ struct ice_dpll { * @input_phase_adj_max: max phase adjust value for an input pins * @output_phase_adj_max: max phase adjust value for an output pins * @periodic_counter: counter of periodic work executions + * @generic: true when generic DPLL ops are used + * @txclk_work: deferred TX reference clock switch worker + * @txclk_switch_requested: a TX ref clock switch is queued in @txclk_work + * @txclk_notify_rwsem: drains in-flight TXCLK notifications on teardown + * + * Locking: + * Acquisition order (top to bottom): + * + * txclk_notify_rwsem (read) + * -> pf->dplls.lock + * -> ctrl_pf->dplls.lock + * + * - @lock serializes all DPLL state mutations on this PF. When the + * controlling PF's lock must also be taken (e.g. updating the shared + * tx_refclks usage map), acquire pf->dplls.lock first, then + * ctrl_pf->dplls.lock. Skip the second acquire when pf == ctrl_pf + * to avoid recursive locking. + * - @txclk_notify_rwsem is held for read across + * ice_txclk_update_and_notify(), including the out-of-lock + * dpll_*_change_ntf() calls. ice_dpll_deinit() takes the write side + * standalone (not nested under any other lock) to drain in-flight + * readers before pins and the TXC DPLL device are freed. */ struct ice_dplls { struct kthread_worker *kworker; @@ -147,11 +176,13 @@ struct ice_dplls { struct completion dpll_init; struct ice_dpll eec; struct ice_dpll pps; + struct ice_dpll txc; struct ice_dpll_pin *inputs; struct ice_dpll_pin *outputs; struct ice_dpll_pin sma[ICE_DPLL_PIN_SW_NUM]; struct ice_dpll_pin ufl[ICE_DPLL_PIN_SW_NUM]; struct ice_dpll_pin rclk; + struct ice_dpll_pin txclks[ICE_DPLL_TXCLK_NUM_MAX]; u8 num_inputs; u8 num_outputs; u8 sma_data; @@ -162,6 +193,9 @@ struct ice_dplls { s32 output_phase_adj_max; u32 periodic_counter; bool generic; + struct work_struct txclk_work; + bool txclk_switch_requested; + struct rw_semaphore txclk_notify_rwsem; }; #if IS_ENABLED(CONFIG_PTP_1588_CLOCK) diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index f28416a707d7..236d293aba98 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -1251,7 +1251,7 @@ static int ice_lbtest_receive_frames(struct ice_rx_ring *rx_ring) rx_buf = &rx_ring->rx_fqes[i]; page = __netmem_to_page(rx_buf->netmem); received_buf = page_address(page) + rx_buf->offset + - page->pp->p.offset; + pp_page_to_nmdesc(page)->pp->p.offset; if (ice_lbtest_check_frame(received_buf)) valid_frames++; diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c b/drivers/net/ethernet/intel/ice/ice_fw_update.c index c143e546dd8c..36314610927b 100644 --- a/drivers/net/ethernet/intel/ice/ice_fw_update.c +++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c @@ -726,7 +726,7 @@ static int ice_finalize_update(struct pldmfw *context) switch (priv->reset_level) { case ICE_AQC_NVM_EMPR_FLAG: devlink_flash_update_status_notify(devlink, - "Activate new firmware by devlink reload", + "Activate new firmware by devlink reload action fw_activate", NULL, 0, 0); break; case ICE_AQC_NVM_PERST_FLAG: diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 837b71b7b2b7..8cdc4fda89e9 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -3769,7 +3769,8 @@ int ice_set_link(struct ice_vsi *vsi, bool ena) if (vsi->type != ICE_VSI_PF) return -EINVAL; - status = ice_aq_set_link_restart_an(pi, ena, NULL); + status = ice_aq_set_link_restart_an(pi, ena, NULL, + ICE_AQC_RESTART_AN_REFCLK_NOCHANGE); /* if link is owned by manageability, FW will return LIBIE_AQ_RC_EMODE. * this is not a fatal error, so print a warning message and return diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index f9e4ec6f7ebb..ec3d89d8d4d3 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -4,6 +4,7 @@ #include "ice.h" #include "ice_lib.h" #include "ice_trace.h" +#include "ice_txclk.h" static const char ice_pin_names[][64] = { "SDP0", @@ -54,11 +55,6 @@ static const struct ice_ptp_pin_desc ice_pin_desc_dpll[] = { { SDP3, { 3, -1 }, { 0, 0 }}, }; -static struct ice_pf *ice_get_ctrl_pf(struct ice_pf *pf) -{ - return !pf->adapter ? NULL : pf->adapter->ctrl_pf; -} - static struct ice_ptp *ice_get_ctrl_ptp(struct ice_pf *pf) { struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf); @@ -1328,6 +1324,9 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup) } } mutex_unlock(&pf->dplls.lock); + + if (linkup) + ice_txclk_update_and_notify(pf); } switch (hw->mac_type) { @@ -3062,14 +3061,9 @@ void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) dev_err(ice_pf_to_dev(pf), "PTP reset failed %d\n", err); } -static int ice_ptp_setup_adapter(struct ice_pf *pf) +static void ice_ptp_setup_adapter(struct ice_pf *pf) { - if (!ice_pf_src_tmr_owned(pf) || !ice_is_primary(&pf->hw)) - return -EPERM; - pf->adapter->ctrl_pf = pf; - - return 0; } static int ice_ptp_setup_pf(struct ice_pf *pf) @@ -3093,6 +3087,21 @@ static int ice_ptp_setup_pf(struct ice_pf *pf) &pf->adapter->ports.ports); mutex_unlock(&pf->adapter->ports.lock); + /* Seed the per-PHY Tx reference clock usage map for this port. + * Only meaningful on E825 (other MAC types don't expose tx-clk + * selection). No locking is needed because this runs during + * ice_ptp_init() before pf->dplls.lock exists and before any + * link event or DPLL callback can observe the map. + */ + if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) { + u8 port_num, phy; + + port_num = ptp->port.port_num; + phy = port_num / pf->hw.ptp.ports_per_phy; + set_bit(port_num, + &ctrl_ptp->tx_refclks[phy][pf->ptp.port.tx_clk]); + } + return 0; } @@ -3312,15 +3321,27 @@ void ice_ptp_init(struct ice_pf *pf) /* If this function owns the clock hardware, it must allocate and * configure the PTP clock device to represent it. */ - if (ice_pf_src_tmr_owned(pf) && ice_is_primary(hw)) { - err = ice_ptp_setup_adapter(pf); - if (err) - goto err_exit; + if (ice_pf_src_tmr_owned(pf)) { + ice_ptp_setup_adapter(pf); + err = ice_ptp_init_owner(pf); if (err) goto err_exit; } + ptp->port.tx_clk = ICE_REF_CLK_ENET; + ptp->port.tx_clk_req = ICE_REF_CLK_ENET; + if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) { + enum ice_e825c_ref_clk tx_ref_clk; + + err = ice_get_serdes_ref_sel_e825c(hw, ptp->port.port_num, + &tx_ref_clk); + if (!err) { + ptp->port.tx_clk = tx_ref_clk; + ptp->port.tx_clk_req = tx_ref_clk; + } + } + err = ice_ptp_setup_pf(pf); if (err) goto err_exit; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 8c44bd758a4f..c4b0da7ce20e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -144,6 +144,8 @@ struct ice_ptp_tx { * @link_up: indicates whether the link is up * @tx_fifo_busy_cnt: number of times the Tx FIFO was busy * @port_num: the port number this structure represents + * @tx_clk: currently active Tx reference clock source + * @tx_clk_req: requested Tx reference clock source (new target) */ struct ice_ptp_port { struct list_head list_node; @@ -153,6 +155,8 @@ struct ice_ptp_port { bool link_up; u8 tx_fifo_busy_cnt; u8 port_num; + enum ice_e825c_ref_clk tx_clk; + enum ice_e825c_ref_clk tx_clk_req; }; enum ice_ptp_tx_interrupt { @@ -236,6 +240,7 @@ struct ice_ptp_pin_desc { * @info: structure defining PTP hardware capabilities * @clock: pointer to registered PTP clock device * @tstamp_config: hardware timestamping configuration + * @tx_refclks: bitmaps table to store the information about TX reference clocks * @reset_time: kernel time after clock stop on reset * @tx_hwtstamp_good: number of completed Tx timestamp requests * @tx_hwtstamp_skipped: number of Tx time stamp requests skipped @@ -261,6 +266,7 @@ struct ice_ptp { struct ptp_clock_info info; struct ptp_clock *clock; struct kernel_hwtstamp_config tstamp_config; + unsigned long tx_refclks[ICE_E825_MAX_PHYS][ICE_REF_CLK_MAX]; u64 reset_time; u64 tx_hwtstamp_good; u32 tx_hwtstamp_skipped; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 2c18e16fe053..8e5f97835954 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -486,6 +486,43 @@ static int ice_read_phy_eth56g(struct ice_hw *hw, u8 port, u32 addr, u32 *val) return err; } +/** + * ice_get_serdes_ref_sel_e825c - Read current Tx ref clock source + * @hw: pointer to the HW struct + * @port: port number for which Tx reference clock is read + * @clk: Tx reference clock value (output) + * + * Return: 0 on success, other error codes when failed to read from PHY + */ +int ice_get_serdes_ref_sel_e825c(struct ice_hw *hw, u8 port, + enum ice_e825c_ref_clk *clk) +{ + u8 lane = port % hw->ptp.ports_per_phy; + u32 serdes_rx_nt, serdes_tx_nt; + u32 val; + int ret; + + ret = ice_read_phy_eth56g(hw, port, + SERDES_IP_IF_LN_FLXM_GENERAL(lane, 0), + &val); + if (ret) + return ret; + + serdes_rx_nt = FIELD_GET(CFG_ICTL_PCS_REF_SEL_RX_NT, val); + serdes_tx_nt = FIELD_GET(CFG_ICTL_PCS_REF_SEL_TX_NT, val); + + if (serdes_tx_nt == REF_SEL_NT_SYNCE && + serdes_rx_nt == REF_SEL_NT_SYNCE) + *clk = ICE_REF_CLK_SYNCE; + else if (serdes_tx_nt == REF_SEL_NT_EREF0 && + serdes_rx_nt == REF_SEL_NT_EREF0) + *clk = ICE_REF_CLK_EREF0; + else + *clk = ICE_REF_CLK_ENET; + + return 0; +} + /** * ice_phy_res_address_eth56g - Calculate a PHY port register address * @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 1c9e77dbc770..16b1988e993d 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -265,6 +265,13 @@ struct ice_cgu_pin_desc { struct dpll_pin_frequency *freq_supp; }; +enum ice_e825c_ref_clk { + ICE_REF_CLK_ENET, + ICE_REF_CLK_SYNCE, + ICE_REF_CLK_EREF0, + ICE_REF_CLK_MAX, +}; + #define E810C_QSFP_C827_0_HANDLE 2 #define E810C_QSFP_C827_1_HANDLE 3 @@ -376,6 +383,8 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port); int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold); int ice_phy_cfg_ptp_1step_eth56g(struct ice_hw *hw, u8 port); int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port); +int ice_get_serdes_ref_sel_e825c(struct ice_hw *hw, u8 port, + enum ice_e825c_ref_clk *clk); #define ICE_ETH56G_NOMINAL_INCVAL 0x140000000ULL #define ICE_ETH56G_NOMINAL_PCS_REF_TUS 0x100000000ULL @@ -788,4 +797,12 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw) #define PHY_PTP_1STEP_PD_DELAY_M GENMASK(30, 1) #define PHY_PTP_1STEP_PD_DLY_V_M BIT(31) +#define SERDES_IP_IF_LN_FLXM_GENERAL(n, m) \ + (0x32B800 + (m) * 0x100000 + (n) * 0x8000) +#define CFG_ICTL_PCS_REF_SEL_RX_NT GENMASK(9, 6) +#define CFG_ICTL_PCS_REF_SEL_TX_NT GENMASK(28, 25) +#define REF_SEL_NT_ENET 0 +#define REF_SEL_NT_EREF0 1 +#define REF_SEL_NT_SYNCE 2 + #endif /* _ICE_PTP_HW_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h b/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h index 21bb861febbf..226243d32968 100644 --- a/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h @@ -54,8 +54,9 @@ enum ice_sbq_dev_id { }; enum ice_sbq_msg_opcode { - ice_sbq_msg_rd = 0x00, - ice_sbq_msg_wr = 0x01 + ice_sbq_msg_rd = 0x00, + ice_sbq_msg_wr = 0x01, + ice_sbq_msg_wr_np = 0x02 }; #define ICE_SBQ_MSG_FLAGS 0x40 diff --git a/drivers/net/ethernet/intel/ice/ice_txclk.c b/drivers/net/ethernet/intel/ice/ice_txclk.c new file mode 100644 index 000000000000..48459f971cbf --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_txclk.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2026 Intel Corporation */ + +#include "ice.h" +#include "ice_cpi.h" +#include "ice_txclk.h" + +#define ICE_PHY0 0 +#define ICE_PHY1 1 + +/** + * ice_txclk_get_pin - map TX reference clock to its DPLL pin + * @pf: pointer to the PF structure + * @ref_clk: TX reference clock selection + * + * Return the DPLL pin corresponding to a given external TX reference + * clock. Only external TX reference clocks (SYNCE and EREF0) are + * represented as DPLL pins. The internal ENET (TXCO) clock has no + * associated DPLL pin and therefore yields %NULL. + * + * This helper is used when emitting DPLL pin change notifications + * after TX reference clock transitions have been verified. + * + * Return: Pointer to the corresponding struct dpll_pin, or %NULL if + * the TX reference clock has no DPLL pin representation. + */ +struct dpll_pin * +ice_txclk_get_pin(struct ice_pf *pf, enum ice_e825c_ref_clk ref_clk) +{ + switch (ref_clk) { + case ICE_REF_CLK_SYNCE: + return pf->dplls.txclks[E825_EXT_SYNCE_PIN_IDX].pin; + case ICE_REF_CLK_EREF0: + return pf->dplls.txclks[E825_EXT_EREF_PIN_IDX].pin; + case ICE_REF_CLK_ENET: + default: + return NULL; + } +} + +/** + * ice_txclk_enable_peer - Enable required TX reference clock on peer PHY + * @pf: pointer to the PF structure + * @clk: TX reference clock that must be enabled + * + * Some TX reference clocks on E825-class devices (SyncE and EREF0) must + * be enabled on both PHY complexes to allow proper routing: + * + * - SyncE must be enabled on both PHYs when used by PHY0 + * - EREF0 must be enabled on both PHYs when used by PHY1 + * + * If the requested clock is not yet enabled on the peer PHY, enable it. + * ENET does not require duplication and is ignored. + * + * Return: 0 on success or negative error code on failure. + */ +static int ice_txclk_enable_peer(struct ice_pf *pf, enum ice_e825c_ref_clk clk) +{ + struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf); + bool peer_clk_in_use; + u8 port_num, phy; + int err; + + if (clk == ICE_REF_CLK_ENET) + return 0; + + if (IS_ERR_OR_NULL(ctrl_pf)) { + dev_err(ice_pf_to_dev(pf), + "Can't enable tx-clk on peer: no controlling PF\n"); + return -EINVAL; + } + + port_num = pf->ptp.port.port_num; + phy = port_num / pf->hw.ptp.ports_per_phy; + peer_clk_in_use = true; + + /* Hold ctrl_pf->dplls.lock across both the peer-usage check and + * the enable AQ command so that two PFs racing to enable the same + * peer-PHY clock cannot both observe peer_clk_in_use == false and + * issue duplicate enables. + */ + mutex_lock(&ctrl_pf->dplls.lock); + if (clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0) + peer_clk_in_use = ice_txclk_any_port_uses(ctrl_pf, + ICE_PHY1, + clk); + else if (clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1) + peer_clk_in_use = ice_txclk_any_port_uses(ctrl_pf, + ICE_PHY0, + clk); + + if ((clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0 && !peer_clk_in_use) || + (clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1 && !peer_clk_in_use)) { + u8 peer_phy = phy ? ICE_PHY0 : ICE_PHY1; + + err = ice_cpi_ena_dis_clk_ref(&pf->hw, peer_phy, clk, true); + if (err) { + mutex_unlock(&ctrl_pf->dplls.lock); + dev_err(ice_pf_to_dev(pf), + "Failed to enable the %u TX clock for the %u PHY\n", + clk, peer_phy); + return err; + } + } + mutex_unlock(&ctrl_pf->dplls.lock); + + return 0; +} + +#define ICE_REFCLK_USER_TO_AQ_IDX(x) ((x) + 1) + +/** + * ice_txclk_set_clk - Set Tx reference clock + * @pf: pointer to pf structure + * @clk: new Tx clock + * + * Return: 0 on success, negative value otherwise. + */ +int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk) +{ + struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf); + struct ice_port_info *port_info; + bool clk_in_use; + u8 port_num, phy; + int err; + + if (pf->ptp.port.tx_clk == clk) + return 0; + + if (IS_ERR_OR_NULL(ctrl_pf)) { + dev_err(ice_pf_to_dev(pf), + "Can't set tx-clk: no controlling PF\n"); + return -EINVAL; + } + + if (!test_bit(ICE_FLAG_DPLL, ctrl_pf->flags)) { + dev_err(ice_pf_to_dev(pf), + "Can't set tx-clk: ctrl PF DPLL not available\n"); + return -EOPNOTSUPP; + } + + port_num = pf->ptp.port.port_num; + phy = port_num / pf->hw.ptp.ports_per_phy; + port_info = pf->hw.port_info; + + /* Hold ctrl_pf->dplls.lock across both the usage check and the + * enable AQ command so that two PFs racing to switch to the same + * (phy, clk) cannot both observe clk_in_use == false and issue + * duplicate enables. The tx_refclks bitmap is updated only later + * by ice_txclk_update_and_notify() after link-up, so without this + * the check-then-act window is wide open. + */ + mutex_lock(&ctrl_pf->dplls.lock); + clk_in_use = ice_txclk_any_port_uses(ctrl_pf, phy, clk); + if (!clk_in_use) { + err = ice_cpi_ena_dis_clk_ref(&pf->hw, phy, clk, true); + if (err) { + mutex_unlock(&ctrl_pf->dplls.lock); + dev_err(ice_pf_to_dev(pf), "Failed to enable the %u TX clock for the %u PHY\n", + clk, phy); + return err; + } + } + mutex_unlock(&ctrl_pf->dplls.lock); + + if (!clk_in_use) { + err = ice_txclk_enable_peer(pf, clk); + if (err) + return err; + } + + /* We are ready to switch to the new TX clk. */ + err = ice_aq_set_link_restart_an(port_info, true, NULL, + ICE_REFCLK_USER_TO_AQ_IDX(clk)); + if (err) { + dev_err(ice_pf_to_dev(pf), + "AN restart AQ command failed with err %d\n", + err); + return err; + } + + /* Clear txclk_switch_requested only after the AN restart AQ has been + * accepted by FW. Clearing earlier would race with any asynchronous + * link-up event: ice_txclk_update_and_notify() would observe the + * cleared flag, read the stale SERDES selector, and misinterpret the + * not-yet-applied switch as a HW rejection. Only clear if no newer + * request has overwritten tx_clk_req while we were dropping locks. + */ + mutex_lock(&pf->dplls.lock); + if (pf->ptp.port.tx_clk_req == clk) + pf->dplls.txclk_switch_requested = false; + mutex_unlock(&pf->dplls.lock); + + return 0; +} + +/** + * ice_txclk_update_and_notify - Validate TX reference clock switching + * @pf: pointer to PF structure + * + * After a link-up event, verify whether the previously requested TX reference + * clock transition actually succeeded. The SERDES reference selector reflects + * the effective hardware choice, which may differ from the requested clock + * when Auto-Negotiation or firmware applies additional policy. + * + * If the hardware-selected clock differs from the requested one, update the + * software state accordingly and stop further processing. + * + * When the switch is successful, update the per‑PHY usage bitmaps so that the + * driver knows which reference clock is currently in use by this port. + * + * This function does not initiate a clock switch; it only validates the result + * of a previously triggered transition and performs cleanup of unused clocks. + */ +void ice_txclk_update_and_notify(struct ice_pf *pf) +{ + struct ice_ptp_port *ptp_port = &pf->ptp.port; + struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf); + struct dpll_pin *old_pin = NULL; + struct dpll_pin *new_pin = NULL; + struct ice_hw *hw = &pf->hw; + enum ice_e825c_ref_clk clk; + bool notify_dpll = false; + int err; + u8 phy; + + phy = ptp_port->port_num / hw->ptp.ports_per_phy; + + /* Hold txclk_notify_rwsem for read across the entire critical + * region, including the out-of-lock dpll_*_change_ntf() calls + * below. ice_dpll_deinit() takes the write side to wait for all + * in-flight notifications to complete before freeing pins and the + * TXC DPLL device, preventing a use-after-free on rmmod. + */ + down_read(&pf->dplls.txclk_notify_rwsem); + mutex_lock(&pf->dplls.lock); + /* Bail out if DPLL subsystem is being torn down. ice_dpll_deinit() + * clears ICE_FLAG_DPLL before freeing pins and the dpll device, so a + * cleared flag under the lock means those objects can no longer be + * safely dereferenced. + */ + if (!test_bit(ICE_FLAG_DPLL, pf->flags)) { + mutex_unlock(&pf->dplls.lock); + goto out; + } + /* If a switch is still pending, the link-up event preceded the + * worker's AN restart. Hardware hasn't applied the new clock yet, + * so reading the SERDES selector now would produce a false failure. + * Let the worker run first; the link-up that follows the AN restart + * will trigger the verification. + */ + if (pf->dplls.txclk_switch_requested) { + mutex_unlock(&pf->dplls.lock); + goto out; + } + /* no TX clock change requested */ + if (pf->ptp.port.tx_clk == pf->ptp.port.tx_clk_req) { + mutex_unlock(&pf->dplls.lock); + goto out; + } + /* verify current Tx reference settings */ + err = ice_get_serdes_ref_sel_e825c(hw, + ptp_port->port_num, + &clk); + if (err) { + mutex_unlock(&pf->dplls.lock); + goto out; + } + + if (clk != pf->ptp.port.tx_clk_req) { + dev_warn(ice_pf_to_dev(pf), + "Failed to switch tx-clk for phy %d and clk %u (current: %u)\n", + phy, pf->ptp.port.tx_clk_req, clk); + old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk_req); + new_pin = ice_txclk_get_pin(pf, clk); + pf->ptp.port.tx_clk = clk; + pf->ptp.port.tx_clk_req = clk; + /* Update the reference clock bitmap to match the hardware + * clock that was actually accepted, so that + * ice_txclk_any_port_uses() reflects reality even on failure. + * The map is owned by ctrl_pf; take its lock per documented + * order (pf->dplls.lock first, then ctrl_pf->dplls.lock) so + * readers on other PFs observe a consistent snapshot. + */ + if (!IS_ERR_OR_NULL(ctrl_pf)) { + if (ctrl_pf != pf) + mutex_lock(&ctrl_pf->dplls.lock); + for (int i = 0; i < ICE_REF_CLK_MAX; i++) { + if (clk == i) + set_bit(ptp_port->port_num, + &ctrl_pf->ptp.tx_refclks[phy][i]); + else + clear_bit(ptp_port->port_num, + &ctrl_pf->ptp.tx_refclks[phy][i]); + } + if (ctrl_pf != pf) + mutex_unlock(&ctrl_pf->dplls.lock); + } + goto err_notify; + } + + old_pin = ice_txclk_get_pin(pf, pf->ptp.port.tx_clk); + pf->ptp.port.tx_clk = clk; + pf->ptp.port.tx_clk_req = clk; + + if (IS_ERR_OR_NULL(ctrl_pf)) { + dev_err(ice_pf_to_dev(pf), + "Can't set tx-clk: no controlling PF\n"); + goto err_notify; + } + + /* update Tx reference clock usage map; map is owned by ctrl_pf, + * take its lock per documented order so readers on other PFs see + * a consistent view. + */ + if (ctrl_pf != pf) + mutex_lock(&ctrl_pf->dplls.lock); + for (int i = 0; i < ICE_REF_CLK_MAX; i++) + if (clk == i) + set_bit(ptp_port->port_num, + &ctrl_pf->ptp.tx_refclks[phy][i]); + else + clear_bit(ptp_port->port_num, + &ctrl_pf->ptp.tx_refclks[phy][i]); + if (ctrl_pf != pf) + mutex_unlock(&ctrl_pf->dplls.lock); + +err_notify: + /* Update TXC DPLL lock status based on effective TX clk, while still + * holding the lock to prevent concurrent link-up events from racing + * on dpll_state. + */ + if (!IS_ERR_OR_NULL(pf->dplls.txc.dpll)) { + enum dpll_lock_status new_lock = ice_txclk_lock_status(clk); + + if (pf->dplls.txc.dpll_state != new_lock) { + pf->dplls.txc.dpll_state = new_lock; + notify_dpll = true; + } + } + mutex_unlock(&pf->dplls.lock); + + /* Notify TX clk pins state transition */ + if (old_pin) + dpll_pin_change_ntf(old_pin); + if (new_pin) + dpll_pin_change_ntf(new_pin); + + if (notify_dpll && !IS_ERR_OR_NULL(pf->dplls.txc.dpll)) + dpll_device_change_ntf(pf->dplls.txc.dpll); + +out: + up_read(&pf->dplls.txclk_notify_rwsem); +} diff --git a/drivers/net/ethernet/intel/ice/ice_txclk.h b/drivers/net/ethernet/intel/ice/ice_txclk.h new file mode 100644 index 000000000000..21d97afb2afc --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_txclk.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2026 Intel Corporation */ + +#ifndef _ICE_TXCLK_H_ +#define _ICE_TXCLK_H_ + +/** + * ice_txclk_any_port_uses - check if any port on a PHY uses this TX refclk + * @ctrl_pf: control PF (owner of the shared tx_refclks map) + * @phy: PHY index + * @clk: TX reference clock + * + * Return: true if any bit (port) is set for this clock on this PHY + */ +static inline bool +ice_txclk_any_port_uses(const struct ice_pf *ctrl_pf, u8 phy, + enum ice_e825c_ref_clk clk) +{ + return find_first_bit(&ctrl_pf->ptp.tx_refclks[phy][clk], + BITS_PER_LONG) < BITS_PER_LONG; +} + +static inline enum dpll_lock_status +ice_txclk_lock_status(enum ice_e825c_ref_clk clk) +{ + switch (clk) { + case ICE_REF_CLK_SYNCE: + case ICE_REF_CLK_EREF0: + return DPLL_LOCK_STATUS_LOCKED; + case ICE_REF_CLK_ENET: + default: + return DPLL_LOCK_STATUS_UNLOCKED; + } +} + +int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk); +void ice_txclk_update_and_notify(struct ice_pf *pf); +struct dpll_pin *ice_txclk_get_pin(struct ice_pf *pf, + enum ice_e825c_ref_clk ref_clk); +#endif /* _ICE_TXCLK_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index 1e82f4c40b32..d9a5c1aae7c2 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -893,6 +893,8 @@ struct ice_ptp_hw { u8 ports_per_phy; }; +#define ICE_E825_MAX_PHYS 2 + /* Port hardware description */ struct ice_hw { u8 __iomem *hw_addr; diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c index f6b3b15364ff..7f9056404f64 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c @@ -2022,7 +2022,7 @@ static void idpf_tx_read_tstamp(struct idpf_tx_queue *txq, struct sk_buff *skb) /* Fetch timestamp from completion descriptor through * virtchnl msg to report to stack. */ - queue_work(system_unbound_wq, txq->tstamp_task); + queue_work(system_dfl_wq, txq->tstamp_task); break; } diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h index 02ae447cc24a..39fea65c075c 100644 --- a/drivers/net/ethernet/intel/idpf/virtchnl2.h +++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h @@ -1572,13 +1572,15 @@ VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_vport_tx_tstamp_latches); * struct virtchnl2_ptp_get_dev_clk_time - Associated with message * VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME. * @dev_time_ns: Device clock time value in nanoseconds + * @pad: Padding for future extensions * * PF/VF sends this message to receive the time from the main timer. */ struct virtchnl2_ptp_get_dev_clk_time { __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time); +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time); /** * struct virtchnl2_ptp_get_cross_time: Associated with message @@ -1586,26 +1588,30 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time); * @sys_time_ns: System counter value expressed in nanoseconds, read * synchronously with device time * @dev_time_ns: Device clock time value expressed in nanoseconds + * @pad: Padding for future extensions * * PF/VF sends this message to receive the cross time. */ struct virtchnl2_ptp_get_cross_time { __le64 sys_time_ns; __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_cross_time); +VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time); /** * struct virtchnl2_ptp_set_dev_clk_time: Associated with message * VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME. * @dev_time_ns: Device time value expressed in nanoseconds to set + * @pad: Padding for future extensions * * PF/VF sends this message to set the time of the main timer. */ struct virtchnl2_ptp_set_dev_clk_time { __le64 dev_time_ns; + u8 pad[8]; }; -VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_set_dev_clk_time); +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time); /** * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index fa028928482f..7e6f9aa2d57b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -442,7 +442,7 @@ /* Interrupt Cause Set */ #define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ #define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_ICS_DRSTA E1000_ICR_DRSTA /* Device Reset Aserted */ +#define E1000_ICS_DRSTA E1000_ICR_DRSTA /* Device Reset Asserted */ /* Extended Interrupt Cause Set */ /* E1000_EITR_CNT_IGNR is only for 82576 and newer */ diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index fa3dfafd2bb1..2bcce6eef0c7 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -1581,7 +1581,7 @@ s32 igb_disable_pcie_master(struct e1000_hw *hw) * igb_validate_mdi_setting - Verify MDI/MDIx settings * @hw: pointer to the HW structure * - * Verify that when not using auto-negotitation that MDI/MDIx is correctly + * Verify that when not using auto-negotiation that MDI/MDIx is correctly * set, which is forced to MDI mode only. **/ s32 igb_validate_mdi_setting(struct e1000_hw *hw) diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index 178e60ec71d4..9e44527f5eea 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -30,7 +30,7 @@ /* Indicates that VF is still clear to send requests */ #define E1000_VT_MSGTYPE_CTS 0x20000000 #define E1000_VT_MSGINFO_SHIFT 16 -/* bits 23:16 are used for exra info for certain messages */ +/* bits 23:16 are used for extra info for certain messages */ #define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT) #define E1000_VF_RESET 0x01 /* VF requests reset */ diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c index c8638502c2be..cf4e5d0e9417 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -405,7 +405,7 @@ s32 igb_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) * Writes data to EEPROM at offset using SPI interface. * * If e1000_update_nvm_checksum is not called after this function , the - * EEPROM will most likley contain an invalid checksum. + * EEPROM will most likely contain an invalid checksum. **/ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ce91dda00ec0..a1e89a375744 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -63,40 +63,40 @@ static const struct pci_device_id igb_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SGMII), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_FLASHLESS), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES_FLASHLESS), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_FIBER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SGMII), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_QUAD_FIBER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SGMII), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_BACKPLANE), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SFP), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER_ET2), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SGMII), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_FLASHLESS), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES_FLASHLESS), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_COPPER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_FIBER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SGMII), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_QUAD_FIBER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SGMII), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_BACKPLANE), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SFP), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER_ET2), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), .driver_data = board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER), .driver_data = board_82575 }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, igb_pci_tbl); @@ -7157,7 +7157,7 @@ static irqreturn_t igb_msix_ring(int irq, void *data) /* Write the ITR value calculated from the previous interrupt. */ igb_write_itr(q_vector); - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } @@ -8198,7 +8198,7 @@ static irqreturn_t igb_intr_msi(int irq, void *data) if (icr & E1000_ICR_TS) igb_tsync_interrupt(adapter); - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } @@ -8244,7 +8244,7 @@ static irqreturn_t igb_intr(int irq, void *data) if (icr & E1000_ICR_TS) igb_tsync_interrupt(adapter); - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index bd85d02ecadd..638d8242b66b 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -1500,12 +1500,13 @@ void igb_ptp_reset(struct igb_adapter *adapter) /* Re-initialize the timer. */ if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) { - struct timespec64 ts = ktime_to_timespec64(ktime_get_real()); + struct timespec64 ts; + ktime_get_real_ts64(&ts); igb_ptp_write_i210(adapter, &ts); } else { timecounter_init(&adapter->tc, &adapter->cc, - ktime_to_ns(ktime_get_real())); + ktime_get_real_ns()); } out: spin_unlock_irqrestore(&adapter->tmreg_lock, flags); diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 5d04541072b3..0a3d0a1cba43 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2937,8 +2937,8 @@ static const struct pci_error_handlers igbvf_err_handler = { }; static const struct pci_device_id igbvf_pci_tbl[] = { - { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_VF), board_vf }, - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_VF), board_i350_vf }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_VF), .driver_data = board_vf }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_VF), .driver_data = board_i350_vf }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, igbvf_pci_tbl); diff --git a/drivers/net/ethernet/intel/igc/igc_diag.c b/drivers/net/ethernet/intel/igc/igc_diag.c index a43d7244ee70..031561fdce49 100644 --- a/drivers/net/ethernet/intel/igc/igc_diag.c +++ b/drivers/net/ethernet/intel/igc/igc_diag.c @@ -172,7 +172,7 @@ bool igc_link_test(struct igc_adapter *adapter, u64 *data) *data = 0; - /* add delay to give enough time for autonegotioation to finish */ + /* add delay to give enough time for autonegotiation to finish */ ssleep(5); link_up = igc_has_link(adapter); diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 8ac16808023c..2c9e2dfd8499 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -47,24 +47,24 @@ static const struct igc_info *igc_info_tbl[] = { }; static const struct pci_device_id igc_pci_tbl[] = { - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LM), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_V), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_I), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I220_V), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K2), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_K), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LMVP), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LMVP), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_IT), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LM), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_V), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_IT), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I221_V), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_BLANK_NVM), board_base }, - { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_BLANK_NVM), board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LM), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_V), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_I), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I220_V), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K2), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_K), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LMVP), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LMVP), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_IT), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LM), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_V), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_IT), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I221_V), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_BLANK_NVM), .driver_data = board_base }, + { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_BLANK_NVM), .driver_data = board_base }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, igc_pci_tbl); @@ -1797,7 +1797,7 @@ static const enum pkt_hash_types igc_rss_type_table[IGC_RSS_TYPE_MAX_TABLE] = { [IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = PKT_HASH_TYPE_L4, [10] = PKT_HASH_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */ [11] = PKT_HASH_TYPE_NONE, /* keep array sized for SW bit-mask */ - [12] = PKT_HASH_TYPE_NONE, /* to handle future HW revisons */ + [12] = PKT_HASH_TYPE_NONE, /* to handle future HW revisions */ [13] = PKT_HASH_TYPE_NONE, [14] = PKT_HASH_TYPE_NONE, [15] = PKT_HASH_TYPE_NONE, @@ -2649,7 +2649,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) } if (igc_fpe_is_pmac_enabled(adapter) && - igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf)) { + igc_fpe_handle_mpacket(adapter, rx_desc, size, pktbuf + pkt_offset)) { /* Advance the ring next-to-clean */ igc_is_non_eop(rx_ring, rx_desc); cleaned_count++; @@ -5688,7 +5688,7 @@ static irqreturn_t igc_msix_ring(int irq, void *data) /* Write the ITR value calculated from the previous interrupt. */ igc_write_itr(q_vector); - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } @@ -6059,7 +6059,7 @@ static irqreturn_t igc_intr_msi(int irq, void *data) if (icr & IGC_ICR_TS) igc_tsync_interrupt(adapter); - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } @@ -6105,7 +6105,7 @@ static irqreturn_t igc_intr(int irq, void *data) if (icr & IGC_ICR_TS) igc_tsync_interrupt(adapter); - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } @@ -7039,7 +7039,7 @@ static enum xdp_rss_hash_type igc_xdp_rss_type[IGC_RSS_TYPE_MAX_TABLE] = { [IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = XDP_RSS_TYPE_L4_IPV6_UDP_EX, [10] = XDP_RSS_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */ [11] = XDP_RSS_TYPE_NONE, /* keep array sized for SW bit-mask */ - [12] = XDP_RSS_TYPE_NONE, /* to handle future HW revisons */ + [12] = XDP_RSS_TYPE_NONE, /* to handle future HW revisions */ [13] = XDP_RSS_TYPE_NONE, [14] = XDP_RSS_TYPE_NONE, [15] = XDP_RSS_TYPE_NONE, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c index 52f73142ff75..4d8ae5b56145 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.c @@ -142,20 +142,14 @@ static int ixgbe_aci_send_cmd_execute(struct ixgbe_hw *hw, IXGBE_PF_HICR); /* Read sync Admin Command response */ - if ((hicr & IXGBE_PF_HICR_SV)) { - for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++) { + if ((hicr & IXGBE_PF_HICR_SV)) + for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++) raw_desc[i] = IXGBE_READ_REG(hw, IXGBE_PF_HIDA(i)); - raw_desc[i] = raw_desc[i]; - } - } /* Read async Admin Command response */ - if ((hicr & IXGBE_PF_HICR_EV) && !(hicr & IXGBE_PF_HICR_C)) { - for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++) { + if ((hicr & IXGBE_PF_HICR_EV) && !(hicr & IXGBE_PF_HICR_C)) + for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++) raw_desc[i] = IXGBE_READ_REG(hw, IXGBE_PF_HIDA_2(i)); - raw_desc[i] = raw_desc[i]; - } - } /* Handle timeout and invalid state of HICR register */ if (hicr & IXGBE_PF_HICR_C) @@ -628,6 +622,9 @@ static bool ixgbe_parse_e610_caps(struct ixgbe_hw *hw, (phys_id & IXGBE_EXT_TOPO_DEV_IMG_PROG_EN) != 0; break; } + case LIBIE_AQC_CAPS_EEE: + caps->eee_support = (u8)number; + break; default: /* Not one of the recognized common capabilities */ return false; @@ -1073,6 +1070,7 @@ void ixgbe_copy_phy_caps_to_cfg(struct ixgbe_aci_cmd_get_phy_caps_data *caps, cfg->link_fec_opt = caps->link_fec_options; cfg->module_compliance_enforcement = caps->module_compliance_enforcement; + cfg->eee_entry_delay = caps->eee_entry_delay; } /** @@ -1093,11 +1091,16 @@ int ixgbe_aci_set_phy_cfg(struct ixgbe_hw *hw, { struct ixgbe_aci_cmd_set_phy_cfg *cmd; struct libie_aq_desc desc; + bool use_buff_eee_field; + u16 buf_size; int err; if (!cfg) return -EINVAL; + /* If FW supports EEE, we have to use buffer with EEE field. */ + use_buff_eee_field = hw->dev_caps.common_cap.eee_support; + cmd = libie_aq_raw(&desc); /* Ensure that only valid bits of cfg->caps can be turned on. */ cfg->caps &= IXGBE_ACI_PHY_ENA_VALID_MASK; @@ -1106,7 +1109,17 @@ int ixgbe_aci_set_phy_cfg(struct ixgbe_hw *hw, cmd->lport_num = hw->bus.func; desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD); - err = ixgbe_aci_send_cmd(hw, &desc, cfg, sizeof(*cfg)); + if (use_buff_eee_field) + buf_size = sizeof(*cfg); + else + /* Buffer w/o eee_entry_delay field is 2B smaller. */ + buf_size = sizeof(*cfg) - sizeof(u16); + + err = ixgbe_aci_send_cmd(hw, &desc, cfg, buf_size); + + /* 1.40 config format is compatible with pre-1.40, just extends + * it at the end. + */ if (!err) hw->phy.curr_user_phy_cfg = *cfg; @@ -1386,6 +1399,7 @@ int ixgbe_aci_get_link_info(struct ixgbe_hw *hw, bool ena_lse, li->topo_media_conflict = link_data.topo_media_conflict; li->pacing = link_data.cfg & (IXGBE_ACI_CFG_PACING_M | IXGBE_ACI_CFG_PACING_TYPE_M); + li->eee_status = link_data.eee_status; /* Update fc info. */ tx_pause = !!(link_data.an_info & IXGBE_ACI_LINK_PAUSE_TX); @@ -1980,14 +1994,58 @@ int ixgbe_identify_phy_e610(struct ixgbe_hw *hw) /* Set PHY ID */ memcpy(&hw->phy.id, pcaps.phy_id_oui, sizeof(u32)); - hw->phy.eee_speeds_supported = IXGBE_LINK_SPEED_10_FULL | - IXGBE_LINK_SPEED_100_FULL | - IXGBE_LINK_SPEED_1GB_FULL; + /* E610 supports EEE only for speeds above 1G */ + if (hw->device_id == IXGBE_DEV_ID_E610_2_5G_T) + hw->phy.eee_speeds_supported = IXGBE_LINK_SPEED_2_5GB_FULL; + else + hw->phy.eee_speeds_supported = IXGBE_LINK_SPEED_2_5GB_FULL | + IXGBE_LINK_SPEED_5GB_FULL | + IXGBE_LINK_SPEED_10GB_FULL; + hw->phy.eee_speeds_advertised = hw->phy.eee_speeds_supported; return 0; } +/** + * ixgbe_setup_eee_e610 - Enable/disable EEE support + * @hw: pointer to the HW structure + * @enable_eee: boolean flag to enable EEE + * + * Enable/disable EEE based on @enable_eee. + * + * Return: the exit code of the operation. + */ +int ixgbe_setup_eee_e610(struct ixgbe_hw *hw, bool enable_eee) +{ + struct ixgbe_aci_cmd_get_phy_caps_data phy_caps = {}; + struct ixgbe_aci_cmd_set_phy_cfg_data phy_cfg = {}; + u16 eee_cap = 0; + int err; + + err = ixgbe_aci_get_phy_caps(hw, false, + IXGBE_ACI_REPORT_ACTIVE_CFG, &phy_caps); + if (err) + return err; + + ixgbe_copy_phy_caps_to_cfg(&phy_caps, &phy_cfg); + phy_cfg.caps |= (IXGBE_ACI_PHY_ENA_LINK | + IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT); + + if (enable_eee) { + if (hw->phy.eee_speeds_advertised & IXGBE_LINK_SPEED_2_5GB_FULL) + eee_cap |= IXGBE_ACI_PHY_EEE_EN_2_5GBASE_T; + if (hw->phy.eee_speeds_advertised & IXGBE_LINK_SPEED_5GB_FULL) + eee_cap |= IXGBE_ACI_PHY_EEE_EN_5GBASE_T; + if (hw->phy.eee_speeds_advertised & IXGBE_LINK_SPEED_10GB_FULL) + eee_cap |= IXGBE_ACI_PHY_EEE_EN_10GBASE_T; + } + + phy_cfg.eee_cap = cpu_to_le16(eee_cap); + + return ixgbe_aci_set_phy_cfg(hw, &phy_cfg); +} + /** * ixgbe_identify_module_e610 - Identify SFP module type * @hw: pointer to hardware structure @@ -4001,6 +4059,7 @@ static const struct ixgbe_mac_operations mac_ops_e610 = { .fw_rollback_mode = ixgbe_fw_rollback_mode_e610, .get_nvm_ver = ixgbe_get_active_nvm_ver, .get_link_capabilities = ixgbe_get_link_capabilities_e610, + .setup_eee = ixgbe_setup_eee_e610, .get_bus_info = ixgbe_get_bus_info_generic, .acquire_swfw_sync = ixgbe_acquire_swfw_sync_X540, .release_swfw_sync = ixgbe_release_swfw_sync_X540, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h index 11916b979d28..2cb76a3d30ae 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_e610.h @@ -55,6 +55,7 @@ int ixgbe_init_phy_ops_e610(struct ixgbe_hw *hw); int ixgbe_identify_phy_e610(struct ixgbe_hw *hw); int ixgbe_identify_module_e610(struct ixgbe_hw *hw); int ixgbe_setup_phy_link_e610(struct ixgbe_hw *hw); +int ixgbe_setup_eee_e610(struct ixgbe_hw *hw, bool enable_eee); int ixgbe_set_phy_power_e610(struct ixgbe_hw *hw, bool on); int ixgbe_enter_lplu_e610(struct ixgbe_hw *hw); int ixgbe_init_eeprom_params_e610(struct ixgbe_hw *hw); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index ba049b3a9609..36e43b5e88d1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "ixgbe.h" @@ -3540,7 +3541,8 @@ static const struct { { IXGBE_LINK_SPEED_10_FULL, ETHTOOL_LINK_MODE_10baseT_Full_BIT }, { IXGBE_LINK_SPEED_100_FULL, ETHTOOL_LINK_MODE_100baseT_Full_BIT }, { IXGBE_LINK_SPEED_1GB_FULL, ETHTOOL_LINK_MODE_1000baseT_Full_BIT }, - { IXGBE_LINK_SPEED_2_5GB_FULL, ETHTOOL_LINK_MODE_2500baseX_Full_BIT }, + { IXGBE_LINK_SPEED_2_5GB_FULL, ETHTOOL_LINK_MODE_2500baseT_Full_BIT }, + { IXGBE_LINK_SPEED_5GB_FULL, ETHTOOL_LINK_MODE_5000baseT_Full_BIT }, { IXGBE_LINK_SPEED_10GB_FULL, ETHTOOL_LINK_MODE_10000baseT_Full_BIT }, }; @@ -3556,6 +3558,165 @@ static const struct { { FW_PHY_ACT_UD_2_10G_KR_EEE, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT}, }; +static int ixgbe_validate_keee(struct net_device *netdev, + struct ethtool_keee *keee_requested) +{ + struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev); + struct ethtool_keee keee_stored = {}; + int err; + + if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE)) + return -EOPNOTSUPP; + + err = netdev->ethtool_ops->get_eee(netdev, &keee_stored); + if (err) + return err; + + if (keee_stored.tx_lpi_enabled != keee_requested->tx_lpi_enabled) { + e_err(drv, "Setting EEE tx-lpi is not supported\n"); + return -EINVAL; + } + + if (keee_stored.tx_lpi_timer != keee_requested->tx_lpi_timer) { + e_err(drv, + "Setting EEE Tx LPI timer is not supported\n"); + return -EINVAL; + } + + if (!linkmode_equal(keee_stored.advertised, + keee_requested->advertised)) { + e_err(drv, + "Setting EEE advertised speeds is not supported\n"); + return -EINVAL; + } + + /* -EALREADY here is for internal use only, must be converted into + * early bail out with 0 by caller + */ + if (keee_stored.eee_enabled == keee_requested->eee_enabled) + return -EALREADY; + + return 0; +} + +/** + * ixgbe_is_eee_link_speed_supported_e610 - Check if EEE can be enabled + * @adapter: pointer to the adapter struct + * + * Check whether current link configuration is capable of enabling EEE feature. + * + * E610 specific function - for other adapters supporting EEE there might be + * no such limitation. + * + * Return: true if EEE can be enabled, false otherwise. + */ +static bool +ixgbe_is_eee_link_speed_supported_e610(struct ixgbe_adapter *adapter) +{ + switch (adapter->link_speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + case IXGBE_LINK_SPEED_2_5GB_FULL: + case IXGBE_LINK_SPEED_5GB_FULL: + return true; + case IXGBE_LINK_SPEED_100_FULL: + case IXGBE_LINK_SPEED_1GB_FULL: + e_dev_info("Energy Efficient Ethernet (EEE) feature is not supported on link speeds equal to or below 1Gbps. EEE is supported on speeds above 1Gbps.\n"); + fallthrough; + default: + return false; + } +} + +static int ixgbe_get_eee_e610(struct net_device *netdev, + struct ethtool_keee *kedata) +{ + struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev); + struct ixgbe_aci_cmd_get_phy_caps_data pcaps; + struct ixgbe_hw *hw = &adapter->hw; + struct ixgbe_link_status link; + int err; + + linkmode_zero(kedata->lp_advertised); + linkmode_zero(kedata->supported); + linkmode_zero(kedata->advertised); + + err = ixgbe_aci_get_link_info(hw, true, &link); + if (err) + return err; + + err = ixgbe_aci_get_phy_caps(hw, false, IXGBE_ACI_REPORT_ACTIVE_CFG, + &pcaps); + if (err) + return err; + + kedata->eee_active = link.eee_status & IXGBE_ACI_LINK_EEE_ACTIVE; + kedata->eee_enabled = link.eee_status & IXGBE_ACI_LINK_EEE_ENABLED; + + /* for E610 devices EEE enablement implies TX LPI enablement */ + kedata->tx_lpi_enabled = kedata->eee_enabled; + + if (kedata->eee_enabled) + kedata->tx_lpi_timer = le16_to_cpu(pcaps.eee_entry_delay); + + for (int i = 0; i < ARRAY_SIZE(ixgbe_ls_map); i++) { + if (hw->phy.eee_speeds_supported & + ixgbe_ls_map[i].mac_speed) + linkmode_set_bit(ixgbe_ls_map[i].link_mode, + kedata->supported); + + if (hw->phy.eee_speeds_advertised & + ixgbe_ls_map[i].mac_speed) + linkmode_set_bit(ixgbe_ls_map[i].link_mode, + kedata->advertised); + } + + return 0; +} + +static int ixgbe_set_eee_e610(struct net_device *netdev, + struct ethtool_keee *kedata) +{ + struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev); + struct ixgbe_hw *hw = &adapter->hw; + int err; + + err = ixgbe_validate_keee(netdev, kedata); + + if (err == -EALREADY) { + return 0; + } else if (err) { + if (err == -EOPNOTSUPP) + e_dev_info("Energy Efficient Ethernet (EEE) feature is currently not supported on this device, please update the device NVM to the latest and try again\n"); + return err; + } + + if (!(ixgbe_is_eee_link_speed_supported_e610(adapter)) && + kedata->eee_enabled) + return -EOPNOTSUPP; + + hw->phy.eee_speeds_advertised = kedata->eee_enabled ? + hw->phy.eee_speeds_supported : 0; + + err = hw->mac.ops.setup_eee(hw, kedata->eee_enabled); + if (err) { + e_dev_err("Setting EEE %s failed.\n", + str_on_off(kedata->eee_enabled)); + return err; + } + + if (kedata->eee_enabled) + adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED; + else + adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED; + + if (netif_running(netdev)) + ixgbe_reinit_locked(adapter); + else + ixgbe_reset(adapter); + + return 0; +} + static int ixgbe_get_eee_fw(struct ixgbe_adapter *adapter, struct ethtool_keee *edata) { @@ -3614,53 +3775,28 @@ static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_keee *edata) { struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev); struct ixgbe_hw *hw = &adapter->hw; - struct ethtool_keee eee_data; int ret_val; - if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE)) - return -EOPNOTSUPP; - - memset(&eee_data, 0, sizeof(struct ethtool_keee)); - - ret_val = ixgbe_get_eee(netdev, &eee_data); - if (ret_val) + ret_val = ixgbe_validate_keee(netdev, edata); + if (ret_val == -EALREADY) + return 0; + else if (ret_val) return ret_val; - if (eee_data.eee_enabled && !edata->eee_enabled) { - if (eee_data.tx_lpi_enabled != edata->tx_lpi_enabled) { - e_err(drv, "Setting EEE tx-lpi is not supported\n"); - return -EINVAL; - } - - if (eee_data.tx_lpi_timer != edata->tx_lpi_timer) { - e_err(drv, - "Setting EEE Tx LPI timer is not supported\n"); - return -EINVAL; - } - - if (!linkmode_equal(eee_data.advertised, edata->advertised)) { - e_err(drv, - "Setting EEE advertised speeds is not supported\n"); - return -EINVAL; - } + if (edata->eee_enabled) { + adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED; + hw->phy.eee_speeds_advertised = + hw->phy.eee_speeds_supported; + } else { + adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED; + hw->phy.eee_speeds_advertised = 0; } - if (eee_data.eee_enabled != edata->eee_enabled) { - if (edata->eee_enabled) { - adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED; - hw->phy.eee_speeds_advertised = - hw->phy.eee_speeds_supported; - } else { - adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED; - hw->phy.eee_speeds_advertised = 0; - } - - /* reset link */ - if (netif_running(netdev)) - ixgbe_reinit_locked(adapter); - else - ixgbe_reset(adapter); - } + /* reset link */ + if (netif_running(netdev)) + ixgbe_reinit_locked(adapter); + else + ixgbe_reset(adapter); return 0; } @@ -3807,8 +3943,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops_e610 = { .set_rxfh = ixgbe_set_rxfh, .get_rxfh_fields = ixgbe_get_rxfh_fields, .set_rxfh_fields = ixgbe_set_rxfh_fields, - .get_eee = ixgbe_get_eee, - .set_eee = ixgbe_set_eee, + .get_eee = ixgbe_get_eee_e610, + .set_eee = ixgbe_set_eee_e610, .get_channels = ixgbe_get_channels, .set_channels = ixgbe_set_channels, .get_priv_flags = ixgbe_get_priv_flags, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 2646ee6f295f..8873a8cc4a18 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -88,60 +88,60 @@ static const struct ixgbe_info *ixgbe_info_tbl[] = { * Class, Class Mask, private data (not used) } */ static const struct pci_device_id ixgbe_pci_tbl[] = { - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT2), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_BX), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_XAUI_LOM), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KR), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_BACKPLANE_FCOE), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_FCOE), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_T3_LOM), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_COMBO_BACKPLANE), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T), board_X540 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF2), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_LS), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_QSFP_SF_QP), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599EN_SFP), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF_QP), board_82599 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T1), board_X540 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550T), board_X550}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550T1), board_X550}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KX4), board_X550EM_x}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_XFI), board_X550EM_x}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), board_X550EM_x}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), board_X550EM_x}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), board_X550EM_x}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_1G_T), board_x550em_x_fw}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_KR), board_x550em_a }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_KR_L), board_x550em_a }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP_N), board_x550em_a }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SGMII), board_x550em_a }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SGMII_L), board_x550em_a }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_10G_T), board_x550em_a}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP), board_x550em_a }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T), board_x550em_a_fw }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T_L), board_x550em_a_fw }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_BACKPLANE), board_e610}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_SFP), board_e610}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_10G_T), board_e610}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_2_5G_T), board_e610}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_SGMII), board_e610}, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT2), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_BX), .driver_data = board_82598 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_XAUI_LOM), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KR), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_BACKPLANE_FCOE), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_FCOE), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_T3_LOM), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_COMBO_BACKPLANE), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T), .driver_data = board_X540 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF2), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_LS), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_QSFP_SF_QP), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599EN_SFP), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF_QP), .driver_data = board_82599 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T1), .driver_data = board_X540 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550T), .driver_data = board_X550 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550T1), .driver_data = board_X550 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KX4), .driver_data = board_X550EM_x }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_XFI), .driver_data = board_X550EM_x }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), .driver_data = board_X550EM_x }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), .driver_data = board_X550EM_x }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), .driver_data = board_X550EM_x }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_1G_T), .driver_data = board_x550em_x_fw }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_KR), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_KR_L), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP_N), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SGMII), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SGMII_L), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_10G_T), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP), .driver_data = board_x550em_a }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T), .driver_data = board_x550em_a_fw }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T_L), .driver_data = board_x550em_a_fw }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_BACKPLANE), .driver_data = board_e610 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_SFP), .driver_data = board_e610 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_10G_T), .driver_data = board_e610 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_2_5G_T), .driver_data = board_e610 }, + { PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_SGMII), .driver_data = board_e610 }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, ixgbe_pci_tbl); @@ -3958,7 +3958,8 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, } /* initialize XPS */ - if (!test_and_set_bit(__IXGBE_TX_XPS_INIT_DONE, ring->state)) { + if (!ring_is_xdp(ring) && + !test_and_set_bit(__IXGBE_TX_XPS_INIT_DONE, ring->state)) { struct ixgbe_q_vector *q_vector = ring->q_vector; if (q_vector) @@ -6758,6 +6759,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter) /** * ixgbe_set_eee_capable - helper function to determine EEE support on X550 + * and E610 * @adapter: board private structure */ static void ixgbe_set_eee_capable(struct ixgbe_adapter *adapter) @@ -6774,6 +6776,20 @@ static void ixgbe_set_eee_capable(struct ixgbe_adapter *adapter) break; adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED; break; + case IXGBE_DEV_ID_E610_BACKPLANE: + case IXGBE_DEV_ID_E610_SFP: + case IXGBE_DEV_ID_E610_10G_T: + case IXGBE_DEV_ID_E610_2_5G_T: + if (hw->dev_caps.common_cap.eee_support && + hw->phy.eee_speeds_supported) { + adapter->flags2 |= IXGBE_FLAG2_EEE_CAPABLE; + /* For E610 adapters EEE should be enabled by default + * if the feature is supported by FW. + */ + adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED; + break; + } + fallthrough; default: adapter->flags2 &= ~IXGBE_FLAG2_EEE_CAPABLE; adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED; @@ -8082,6 +8098,7 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter) struct net_device *netdev = adapter->netdev; struct ixgbe_hw *hw = &adapter->hw; u32 link_speed = adapter->link_speed; + struct ethtool_keee keee = {}; const char *speed_str; bool flow_rx, flow_tx; @@ -8122,6 +8139,8 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter) if (test_bit(__IXGBE_PTP_RUNNING, &adapter->state)) ixgbe_ptp_start_cyclecounter(adapter); + netdev->ethtool_ops->get_eee(netdev, &keee); + switch (link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: speed_str = "10 Gbps"; @@ -8145,10 +8164,11 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter) speed_str = "unknown speed"; break; } - e_info(drv, "NIC Link is Up %s, Flow Control: %s\n", speed_str, + e_info(drv, "NIC Link is Up %s, Flow Control: %s, EEE: %s\n", speed_str, ((flow_rx && flow_tx) ? "RX/TX" : (flow_rx ? "RX" : - (flow_tx ? "TX" : "None")))); + (flow_tx ? "TX" : "None"))), + str_on_off(keee.eee_enabled)); netif_carrier_on(netdev); ixgbe_check_vf_rate_limit(adapter); @@ -12003,6 +12023,13 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_netdev; + if (hw->mac.type == ixgbe_mac_e610 && + (adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE)) { + bool eee_enable = adapter->flags2 & IXGBE_FLAG2_EEE_ENABLED; + + hw->mac.ops.setup_eee(hw, eee_enable); + } + ixgbe_devlink_init_regions(adapter); devl_register(adapter->devlink); devl_unlock(adapter->devlink); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 61f2ef67defd..a461b6542f96 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -2798,6 +2798,7 @@ struct ixgbe_hic_hdr2_rsp { }; union ixgbe_hic_hdr2 { + u32 buf[1]; struct ixgbe_hic_hdr2_req req; struct ixgbe_hic_hdr2_rsp rsp; }; @@ -3524,6 +3525,7 @@ struct ixgbe_mac_operations { int (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *, bool *); void (*set_rate_select_speed)(struct ixgbe_hw *, ixgbe_link_speed); + int (*setup_eee)(struct ixgbe_hw *hw, bool enable_eee); /* Packet Buffer Manipulation */ void (*set_rxpba)(struct ixgbe_hw *, int, u32, int); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h index ff8d640a50b1..959cacecae49 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type_e610.h @@ -323,10 +323,8 @@ struct ixgbe_aci_cmd_get_phy_caps_data { #define IXGBE_ACI_PHY_EEE_EN_100BASE_TX BIT(0) #define IXGBE_ACI_PHY_EEE_EN_1000BASE_T BIT(1) #define IXGBE_ACI_PHY_EEE_EN_10GBASE_T BIT(2) -#define IXGBE_ACI_PHY_EEE_EN_1000BASE_KX BIT(3) -#define IXGBE_ACI_PHY_EEE_EN_10GBASE_KR BIT(4) -#define IXGBE_ACI_PHY_EEE_EN_25GBASE_KR BIT(5) -#define IXGBE_ACI_PHY_EEE_EN_10BASE_T BIT(11) +#define IXGBE_ACI_PHY_EEE_EN_5GBASE_T BIT(11) +#define IXGBE_ACI_PHY_EEE_EN_2_5GBASE_T BIT(12) __le16 eeer_value; u8 phy_id_oui[4]; /* PHY/Module ID connected on the port */ u8 phy_fw_ver[8]; @@ -356,7 +354,9 @@ struct ixgbe_aci_cmd_get_phy_caps_data { #define IXGBE_ACI_MOD_TYPE_BYTE2_SFP_PLUS 0xA0 #define IXGBE_ACI_MOD_TYPE_BYTE2_QSFP_PLUS 0x86 u8 qualified_module_count; - u8 rsvd2[7]; /* Bytes 47:41 reserved */ + u8 rsvd2; + __le16 eee_entry_delay; + u8 rsvd3[4]; #define IXGBE_ACI_QUAL_MOD_COUNT_MAX 16 struct { u8 v_oui[3]; @@ -382,6 +382,15 @@ struct ixgbe_aci_cmd_set_phy_cfg_data { __le64 phy_type_low; /* Use values from IXGBE_PHY_TYPE_LOW_* */ __le64 phy_type_high; /* Use values from IXGBE_PHY_TYPE_HIGH_* */ u8 caps; + u8 low_power_ctrl_an; + __le16 eee_cap; /* Value from ixgbe_aci_get_phy_caps */ + __le16 eeer_value; /* Use defines from ixgbe_aci_get_phy_caps */ + u8 link_fec_opt; /* Use defines from ixgbe_aci_get_phy_caps */ + u8 module_compliance_enforcement; + __le16 eee_entry_delay; +} __packed; + +/* Set PHY config capabilities (@caps) defines */ #define IXGBE_ACI_PHY_ENA_VALID_MASK 0xef #define IXGBE_ACI_PHY_ENA_TX_PAUSE_ABILITY BIT(0) #define IXGBE_ACI_PHY_ENA_RX_PAUSE_ABILITY BIT(1) @@ -390,12 +399,6 @@ struct ixgbe_aci_cmd_set_phy_cfg_data { #define IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT BIT(5) #define IXGBE_ACI_PHY_ENA_LESM BIT(6) #define IXGBE_ACI_PHY_ENA_AUTO_FEC BIT(7) - u8 low_power_ctrl_an; - __le16 eee_cap; /* Value from ixgbe_aci_get_phy_caps */ - __le16 eeer_value; /* Use defines from ixgbe_aci_get_phy_caps */ - u8 link_fec_opt; /* Use defines from ixgbe_aci_get_phy_caps */ - u8 module_compliance_enforcement; -}; /* Restart AN command data structure (direct 0x0605) * Also used for response, with only the lport_num field present. @@ -509,8 +512,9 @@ struct ixgbe_aci_cmd_get_link_status_data { #define IXGBE_ACI_LINK_SPEED_200GB BIT(11) #define IXGBE_ACI_LINK_SPEED_UNKNOWN BIT(15) __le16 reserved3; - u8 ext_fec_status; -#define IXGBE_ACI_LINK_RS_272_FEC_EN BIT(0) /* RS 272 FEC enabled */ + u8 eee_status; +#define IXGBE_ACI_LINK_EEE_ENABLED BIT(2) +#define IXGBE_ACI_LINK_EEE_ACTIVE BIT(3) u8 reserved4; __le64 phy_type_low; /* Use values from ICE_PHY_TYPE_LOW_* */ __le64 phy_type_high; /* Use values from ICE_PHY_TYPE_HIGH_* */ @@ -812,6 +816,7 @@ struct ixgbe_link_status { * of ixgbe_aci_get_phy_caps structure */ u8 module_type[IXGBE_ACI_MODULE_TYPE_TOTAL_BYTE]; + u8 eee_status; }; /* Common HW capabilities for SW use */ @@ -891,6 +896,7 @@ struct ixgbe_hw_caps { u8 apm_wol_support; u8 acpi_prog_mthd; u8 proxy_support; + u8 eee_support; bool nvm_update_pending_nvm; bool nvm_update_pending_orom; bool nvm_update_pending_netlist; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 76d2fa3ef518..4a0ccbf448a2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1228,7 +1228,7 @@ static int ixgbe_update_flash_X550(struct ixgbe_hw *hw) buffer.req.buf_lenl = FW_SHADOW_RAM_DUMP_LEN; buffer.req.checksum = FW_DEFAULT_CHECKSUM; - status = ixgbe_host_interface_command(hw, &buffer, sizeof(buffer), + status = ixgbe_host_interface_command(hw, buffer.buf, sizeof(buffer), IXGBE_HI_COMMAND_TIMEOUT, false); return status; } diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 4ba3be961ab6..7ce46b4b4821 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -64,20 +64,43 @@ static const struct ixgbevf_info *ixgbevf_info_tbl[] = { * Class, Class Mask, private data (not used) } */ static const struct pci_device_id ixgbevf_pci_tbl[] = { - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF), board_82599_vf }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF_HV), board_82599_vf_hv }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF), board_X540_vf }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF_HV), board_X540_vf_hv }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF), board_X550_vf }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF_HV), board_X550_vf_hv }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF), board_X550EM_x_vf }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF_HV), board_X550EM_x_vf_hv}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_VF), board_x550em_a_vf }, - {PCI_VDEVICE_SUB(INTEL, IXGBE_DEV_ID_E610_VF, PCI_ANY_ID, - IXGBE_SUBDEV_ID_E610_VF_HV), board_e610_vf_hv}, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_VF), board_e610_vf}, + { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF), + .driver_data = board_82599_vf, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF_HV), + .driver_data = board_82599_vf_hv, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF), + .driver_data = board_X540_vf, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF_HV), + .driver_data = board_X540_vf_hv, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF), + .driver_data = board_X550_vf, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF_HV), + .driver_data = board_X550_vf_hv, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF), + .driver_data = board_X550EM_x_vf, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF_HV), + .driver_data = board_X550EM_x_vf_hv + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_VF), + .driver_data = board_x550em_a_vf, + }, { + PCI_VDEVICE_SUB(INTEL, IXGBE_DEV_ID_E610_VF, + PCI_ANY_ID, IXGBE_SUBDEV_ID_E610_VF_HV), + .driver_data = board_e610_vf_hv, + }, { + PCI_VDEVICE(INTEL, IXGBE_DEV_ID_E610_VF), + .driver_data = board_e610_vf, + }, /* required last entry */ - {0, } + { } }; MODULE_DEVICE_TABLE(pci, ixgbevf_pci_tbl); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c index 6f13296303cb..b6fda42e44c7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c @@ -13,6 +13,7 @@ #include "struct.h" #include "rvu.h" #include "debugfs.h" +#include "cn20k/reg.h" #include "cn20k/npc.h" static int npc_mcam_layout_show(struct seq_file *s, void *unused) @@ -58,7 +59,8 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused) "v:%u", vidx0); } - seq_printf(s, "\t%u(%#x) %s\n", idx0, pf1, + seq_printf(s, "\t%u(%#x)%c %s\n", idx0, pf1, + test_bit(idx0, npc_priv->en_map) ? '+' : ' ', map ? buf0 : " "); } goto next; @@ -101,9 +103,13 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused) vidx1); } - seq_printf(s, "%05u(%#x) %s\t\t%05u(%#x) %s\n", - idx1, pf2, v1 ? buf1 : " ", - idx0, pf1, v0 ? buf0 : " "); + seq_printf(s, "%05u(%#x)%c %s\t\t%05u(%#x)%c %s\n", + idx1, pf2, + test_bit(idx1, npc_priv->en_map) ? '+' : ' ', + v1 ? buf1 : " ", + idx0, pf1, + test_bit(idx0, npc_priv->en_map) ? '+' : ' ', + v0 ? buf0 : " "); continue; } @@ -120,8 +126,9 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused) vidx0); } - seq_printf(s, "\t\t \t\t%05u(%#x) %s\n", idx0, - pf1, map ? buf0 : " "); + seq_printf(s, "\t\t \t\t%05u(%#x)%c %s\n", idx0, pf1, + test_bit(idx0, npc_priv->en_map) ? '+' : ' ', + map ? buf0 : " "); continue; } @@ -134,7 +141,8 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused) snprintf(buf1, sizeof(buf1), "v:%05u", vidx1); } - seq_printf(s, "%05u(%#x) %s\n", idx1, pf1, + seq_printf(s, "%05u(%#x)%c %s\n", idx1, pf1, + test_bit(idx1, npc_priv->en_map) ? '+' : ' ', map ? buf1 : " "); } next: @@ -145,6 +153,137 @@ static int npc_mcam_layout_show(struct seq_file *s, void *unused) DEFINE_SHOW_ATTRIBUTE(npc_mcam_layout); +#define __OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name) \ +static const struct file_operations __name ## _fops = { \ + .owner = THIS_MODULE, \ + .open = __name ## _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +#define DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(__name, __size) \ +static int __name ## _open(struct inode *inode, struct file *file) \ +{ \ + return single_open_size(file, __name ## _show, inode->i_private, \ + __size); \ +} \ +__OCTEONTX2_DEBUGFS_ATTRIBUTE_FOPS(__name) + +static DEFINE_MUTEX(stats_lock); + +/* MAX_NUM_BANKS, MAX_SUBBANK_DEPTH and MAX_NUM_SUB_BANKS represent + * hard limit on all silicon variants, preventing any possibility of + * out-of-bounds access. + */ +static u64 (*dstats)[MAX_NUM_BANKS][MAX_SUBBANK_DEPTH * MAX_NUM_SUB_BANKS]; + +static int npc_mcam_dstats_show(struct seq_file *s, void *unused) +{ + struct npc_priv_t *npc_priv; + int blkaddr, pf, mcam_idx; + u64 stats, delta; + struct rvu *rvu; + char buff[64]; + u8 key_type; + void *map; + + npc_priv = npc_priv_get(); + rvu = s->private; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return 0; + + mutex_lock(&stats_lock); + seq_puts(s, "idx\tpfunc\tstats\n"); + for (int bank = npc_priv->num_banks - 1; bank >= 0; bank--) { + for (int idx = npc_priv->bank_depth - 1; idx >= 0; idx--) { + mcam_idx = bank * npc_priv->bank_depth + idx; + + if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &key_type)) + continue; + + if (key_type == NPC_MCAM_KEY_X4 && bank != 0) + continue; + + if (!test_bit(mcam_idx, npc_priv->en_map)) + continue; + + stats = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(idx, bank)); + if (!stats) + continue; + if (stats == dstats[0][bank][idx]) + continue; + + if (stats < dstats[0][bank][idx]) + dstats[0][bank][idx] = 0; + + pf = 0xFFFF; + map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx); + if (map) + pf = xa_to_value(map); + + delta = stats - dstats[0][bank][idx]; + + snprintf(buff, sizeof(buff), "%u\t%#04x\t%llu\n", + mcam_idx, pf, delta); + seq_puts(s, buff); + + dstats[0][bank][idx] = stats; + } + } + + mutex_unlock(&stats_lock); + return 0; +} + +/* "%u\t%#04x\t%llu\n" needs less than 64 characters to print */ +#define TOTAL_SZ (MAX_NUM_BANKS * MAX_NUM_SUB_BANKS * MAX_SUBBANK_DEPTH * 64) +DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_dstats, TOTAL_SZ); + +static int npc_mcam_mismatch_show(struct seq_file *s, void *unused) +{ + struct npc_priv_t *npc_priv; + struct npc_subbank *sb; + int mcam_idx, sb_off; + struct rvu *rvu; + char buff[64]; + void *map; + int rc; + + npc_priv = npc_priv_get(); + rvu = s->private; + + seq_puts(s, "index\tsb idx\tkw type\n"); + for (int bank = npc_priv->num_banks - 1; bank >= 0; bank--) { + for (int idx = npc_priv->bank_depth - 1; idx >= 0; idx--) { + mcam_idx = bank * npc_priv->bank_depth + idx; + + if (!test_bit(mcam_idx, npc_priv->en_map)) + continue; + + map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx); + if (map) + continue; + + rc = npc_mcam_idx_2_subbank_idx(rvu, mcam_idx, + &sb, &sb_off); + if (rc) + continue; + + snprintf(buff, sizeof(buff), "%u\t%d\t%u\n", + mcam_idx, sb->idx, sb->key_type); + + seq_puts(s, buff); + } + } + return 0; +} + +/* "%u\t%d\t%u\n" needs less than 64 characters to print. */ +DEFINE_OCTEONTX2_DEBUGFS_ATTRIBUTE_WITH_SIZE(npc_mcam_mismatch, TOTAL_SZ); + static int npc_mcam_default_show(struct seq_file *s, void *unused) { struct npc_priv_t *npc_priv; @@ -259,6 +398,16 @@ int npc_cn20k_debugfs_init(struct rvu *rvu) debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc, npc_priv, &npc_vidx2idx_map_fops); + dstats = devm_kzalloc(rvu->dev, sizeof(*dstats), GFP_KERNEL); + if (!dstats) + return -ENOMEM; + + debugfs_create_file("dstats", 0444, rvu->rvu_dbg.npc, rvu, + &npc_mcam_dstats_fops); + + debugfs_create_file("mismatch", 0444, rvu->rvu_dbg.npc, rvu, + &npc_mcam_mismatch_fops); + debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc, npc_priv, &npc_idx2vidx_map_fops); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index fe8c4ffcd8f7..354c4e881c6a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -16,9 +16,7 @@ #include "cn20k/reg.h" #include "rvu_npc_fs.h" -static struct npc_priv_t npc_priv = { - .num_banks = MAX_NUM_BANKS, -}; +static struct npc_priv_t *npc_priv; static const char *npc_kw_name[NPC_MCAM_KEY_MAX] = { [NPC_MCAM_KEY_DYN] = "DYNAMIC", @@ -57,7 +55,7 @@ static struct npc_mcam_kex_extr npc_mkex_extr_default = { .keyx_cfg = { /* nibble: LA..LE (ltype only) + Error code + Channel */ [NIX_INTF_RX] = ((u64)NPC_MCAM_KEY_DYN << 32) | - NPC_PARSE_NIBBLE_INTF_RX | + NPC_CN20K_PARSE_NIBBLE_INTF_RX | NPC_CN20K_PARSE_NIBBLE_ERRCODE, /* nibble: LA..LE (ltype only) */ @@ -226,7 +224,7 @@ static u16 npc_idx2vidx(u16 idx) vidx = idx; index = idx; - map = xa_load(&npc_priv.xa_idx2vidx_map, index); + map = xa_load(&npc_priv->xa_idx2vidx_map, index); if (!map) goto done; @@ -242,7 +240,7 @@ static u16 npc_idx2vidx(u16 idx) static bool npc_is_vidx(u16 vidx) { - return vidx >= npc_priv.bank_depth * 2; + return vidx >= npc_priv->bank_depth * 2; } static u16 npc_vidx2idx(u16 vidx) @@ -256,7 +254,7 @@ static u16 npc_vidx2idx(u16 vidx) idx = vidx; index = vidx; - map = xa_load(&npc_priv.xa_vidx2idx_map, index); + map = xa_load(&npc_priv->xa_vidx2idx_map, index); if (!map) goto done; @@ -272,7 +270,7 @@ static u16 npc_vidx2idx(u16 vidx) u16 npc_cn20k_vidx2idx(u16 idx) { - if (!npc_priv.init_done) + if (!npc_priv) return idx; if (!npc_is_vidx(idx)) @@ -283,7 +281,7 @@ u16 npc_cn20k_vidx2idx(u16 idx) u16 npc_cn20k_idx2vidx(u16 idx) { - if (!npc_priv.init_done) + if (!npc_priv) return idx; if (npc_is_vidx(idx)) @@ -306,7 +304,7 @@ static int npc_vidx_maps_del_entry(struct rvu *rvu, u16 vidx, u16 *old_midx) mcam_idx = npc_vidx2idx(vidx); - map = xa_erase(&npc_priv.xa_vidx2idx_map, vidx); + map = xa_erase(&npc_priv->xa_vidx2idx_map, vidx); if (!map) { dev_err(rvu->dev, "%s: vidx(%u) does not map to proper mcam idx\n", @@ -314,7 +312,7 @@ static int npc_vidx_maps_del_entry(struct rvu *rvu, u16 vidx, u16 *old_midx) return -ESRCH; } - map = xa_erase(&npc_priv.xa_idx2vidx_map, mcam_idx); + map = xa_erase(&npc_priv->xa_idx2vidx_map, mcam_idx); if (!map) { dev_err(rvu->dev, "%s: vidx(%u) is not valid\n", @@ -341,7 +339,7 @@ static int npc_vidx_maps_modify(struct rvu *rvu, u16 vidx, u16 new_midx) return -ESRCH; } - map = xa_erase(&npc_priv.xa_vidx2idx_map, vidx); + map = xa_erase(&npc_priv->xa_vidx2idx_map, vidx); if (!map) { dev_err(rvu->dev, "%s: vidx(%u) could not be deleted from vidx2idx map\n", @@ -351,7 +349,7 @@ static int npc_vidx_maps_modify(struct rvu *rvu, u16 vidx, u16 new_midx) old_midx = xa_to_value(map); - rc = xa_insert(&npc_priv.xa_vidx2idx_map, vidx, + rc = xa_insert(&npc_priv->xa_vidx2idx_map, vidx, xa_mk_value(new_midx), GFP_KERNEL); if (rc) { dev_err(rvu->dev, @@ -360,7 +358,7 @@ static int npc_vidx_maps_modify(struct rvu *rvu, u16 vidx, u16 new_midx) goto fail1; } - map = xa_erase(&npc_priv.xa_idx2vidx_map, old_midx); + map = xa_erase(&npc_priv->xa_idx2vidx_map, old_midx); if (!map) { dev_err(rvu->dev, "%s: old_midx(%u, vidx(%u)) cannot be added to idx2vidx map\n", @@ -369,7 +367,7 @@ static int npc_vidx_maps_modify(struct rvu *rvu, u16 vidx, u16 new_midx) goto fail2; } - rc = xa_insert(&npc_priv.xa_idx2vidx_map, new_midx, + rc = xa_insert(&npc_priv->xa_idx2vidx_map, new_midx, xa_mk_value(vidx), GFP_KERNEL); if (rc) { dev_err(rvu->dev, @@ -382,21 +380,21 @@ static int npc_vidx_maps_modify(struct rvu *rvu, u16 vidx, u16 new_midx) fail3: /* Restore vidx at old_midx location */ - if (xa_insert(&npc_priv.xa_idx2vidx_map, old_midx, + if (xa_insert(&npc_priv->xa_idx2vidx_map, old_midx, xa_mk_value(vidx), GFP_KERNEL)) dev_err(rvu->dev, "%s: Error to roll back idx2vidx old_midx=%u vidx=%u\n", __func__, old_midx, vidx); fail2: /* Erase new_midx inserted at vidx */ - if (!xa_erase(&npc_priv.xa_vidx2idx_map, vidx)) + if (!xa_erase(&npc_priv->xa_vidx2idx_map, vidx)) dev_err(rvu->dev, "%s: Failed to roll back vidx2idx vidx=%u\n", __func__, vidx); fail1: /* Restore old_midx at vidx location */ - if (xa_insert(&npc_priv.xa_vidx2idx_map, vidx, + if (xa_insert(&npc_priv->xa_vidx2idx_map, vidx, xa_mk_value(old_midx), GFP_KERNEL)) dev_err(rvu->dev, "%s: Failed to roll back vidx2idx to old_midx=%u, vidx=%u\n", @@ -412,10 +410,10 @@ static int npc_vidx_maps_add_entry(struct rvu *rvu, u16 mcam_idx, int pcifunc, u32 id; /* Virtual index start from maximum mcam index + 1 */ - max = npc_priv.bank_depth * 2 * 2 - 1; - min = npc_priv.bank_depth * 2; + max = npc_priv->bank_depth * 2 * 2 - 1; + min = npc_priv->bank_depth * 2; - rc = xa_alloc(&npc_priv.xa_vidx2idx_map, &id, + rc = xa_alloc(&npc_priv->xa_vidx2idx_map, &id, xa_mk_value(mcam_idx), XA_LIMIT(min, max), GFP_KERNEL); if (rc) { @@ -425,7 +423,7 @@ static int npc_vidx_maps_add_entry(struct rvu *rvu, u16 mcam_idx, int pcifunc, goto fail1; } - rc = xa_insert(&npc_priv.xa_idx2vidx_map, mcam_idx, + rc = xa_insert(&npc_priv->xa_idx2vidx_map, mcam_idx, xa_mk_value(id), GFP_KERNEL); if (rc) { dev_err(rvu->dev, @@ -440,7 +438,7 @@ static int npc_vidx_maps_add_entry(struct rvu *rvu, u16 mcam_idx, int pcifunc, return 0; fail2: - xa_erase(&npc_priv.xa_vidx2idx_map, id); + xa_erase(&npc_priv->xa_vidx2idx_map, id); fail1: return rc; } @@ -521,13 +519,17 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr, int kpm, int start_entry, const struct npc_kpu_profile *profile) { + int num_cam_entries, num_action_entries; int entry, num_entries, max_entries; u64 idx; - if (profile->cam_entries != profile->action_entries) { + num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile); + num_action_entries = npc_get_num_kpu_action_entries(rvu, profile); + + if (num_cam_entries != num_action_entries) { dev_err(rvu->dev, "kpm%d: CAM and action entries [%d != %d] not equal\n", - kpm, profile->cam_entries, profile->action_entries); + kpm, num_cam_entries, num_action_entries); WARN(1, "Fatal error\n"); return; @@ -536,16 +538,18 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr, max_entries = rvu->hw->npc_kpu_entries / 2; entry = start_entry; /* Program CAM match entries for previous kpm extracted data */ - num_entries = min_t(int, profile->cam_entries, max_entries); + num_entries = min_t(int, num_cam_entries, max_entries); for (idx = 0; entry < num_entries + start_entry; entry++, idx++) - npc_config_kpmcam(rvu, blkaddr, &profile->cam[idx], + npc_config_kpmcam(rvu, blkaddr, + npc_get_kpu_cam_nth_entry(rvu, profile, idx), kpm, entry); entry = start_entry; /* Program this kpm's actions */ - num_entries = min_t(int, profile->action_entries, max_entries); + num_entries = min_t(int, num_action_entries, max_entries); for (idx = 0; entry < num_entries + start_entry; entry++, idx++) - npc_config_kpmaction(rvu, blkaddr, &profile->action[idx], + npc_config_kpmaction(rvu, blkaddr, + npc_get_kpu_action_nth_entry(rvu, profile, idx), kpm, entry, false); } @@ -611,20 +615,23 @@ npc_enable_kpm_entry(struct rvu *rvu, int blkaddr, int kpm, int num_entries) static void npc_program_kpm_profile(struct rvu *rvu, int blkaddr, int num_kpms) { const struct npc_kpu_profile *profile1, *profile2; + int pfl1_num_cam_entries, pfl2_num_cam_entries; int idx, total_cam_entries; for (idx = 0; idx < num_kpms; idx++) { profile1 = &rvu->kpu.kpu[idx]; + pfl1_num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile1); npc_program_single_kpm_profile(rvu, blkaddr, idx, 0, profile1); profile2 = &rvu->kpu.kpu[idx + KPU_OFFSET]; + pfl2_num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile2); + npc_program_single_kpm_profile(rvu, blkaddr, idx, - profile1->cam_entries, + pfl1_num_cam_entries, profile2); - total_cam_entries = profile1->cam_entries + - profile2->cam_entries; + total_cam_entries = pfl1_num_cam_entries + pfl2_num_cam_entries; npc_enable_kpm_entry(rvu, blkaddr, idx, total_cam_entries); rvu_write64(rvu, blkaddr, NPC_AF_KPMX_PASS2_OFFSET(idx), - profile1->cam_entries); + pfl1_num_cam_entries); /* Enable the KPUs associated with this KPM */ rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx), 0x01); rvu_write64(rvu, blkaddr, NPC_AF_KPUX_CFG(idx + KPU_OFFSET), @@ -634,6 +641,7 @@ static void npc_program_kpm_profile(struct rvu *rvu, int blkaddr, int num_kpms) void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr) { + struct npc_kpu_profile_action *act; struct rvu_hwinfo *hw = rvu->hw; int num_pkinds, idx; @@ -665,9 +673,15 @@ void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr) num_pkinds = rvu->kpu.pkinds; num_pkinds = min_t(int, hw->npc_pkinds, num_pkinds); - for (idx = 0; idx < num_pkinds; idx++) - npc_config_kpmaction(rvu, blkaddr, &rvu->kpu.ikpu[idx], + /* Cn20k does not support Custom profile from filesystem */ + for (idx = 0; idx < num_pkinds; idx++) { + act = npc_get_ikpu_nth_entry(rvu, idx); + if (!act) + continue; + + npc_config_kpmaction(rvu, blkaddr, act, 0, idx, true); + } /* Program KPM CAM and Action profiles */ npc_program_kpm_profile(rvu, blkaddr, hw->npc_kpms); @@ -675,11 +689,11 @@ void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr) struct npc_priv_t *npc_priv_get(void) { - return &npc_priv; + return npc_priv; } static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr, - struct npc_mcam_kex_extr *mkex_extr, + const struct npc_mcam_kex_extr *mkex_extr, u8 intf) { u8 num_extr = rvu->hw->npc_kex_extr; @@ -708,7 +722,7 @@ static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr, } static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr, - struct npc_mcam_kex_extr *mkex_extr, + const struct npc_mcam_kex_extr *mkex_extr, u8 intf) { u8 num_extr = rvu->hw->npc_kex_extr; @@ -737,7 +751,7 @@ static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr, } static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr, - struct npc_mcam_kex_extr *mkex_extr) + const struct npc_mcam_kex_extr *mkex_extr) { struct rvu_hwinfo *hw = rvu->hw; u8 intf; @@ -824,7 +838,7 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, rvu_write64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), cfg); - return 0; + goto update_en_map; } /* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks @@ -842,6 +856,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, cfg); } +update_en_map: + if (enable) + set_bit(index, npc_priv->en_map); + else + clear_bit(index, npc_priv->en_map); + return 0; } @@ -1620,8 +1640,8 @@ npc_cn20k_update_action_entries_n_flags(struct rvu *rvu, int npc_cn20k_apply_custom_kpu(struct rvu *rvu, struct npc_kpu_profile_adapter *profile) { + const struct npc_cn20k_kpu_profile_fwdata *fw = rvu->kpu_fwdata; size_t hdr_sz = sizeof(struct npc_cn20k_kpu_profile_fwdata); - struct npc_cn20k_kpu_profile_fwdata *fw = rvu->kpu_fwdata; struct npc_kpu_profile_action *action; struct npc_kpu_profile_cam *cam; struct npc_kpu_fwdata *fw_kpu; @@ -1666,8 +1686,15 @@ int npc_cn20k_apply_custom_kpu(struct rvu *rvu, } /* Verify if profile fits the HW */ + if (fw->kpus > rvu->hw->npc_kpus) { + dev_warn(rvu->dev, "Not enough KPUs: %d > %d\n", fw->kpus, + rvu->hw->npc_kpus); + return -EINVAL; + } + + /* Check if there is enough memory */ if (fw->kpus > profile->kpus) { - dev_warn(rvu->dev, "Not enough KPUs: %d > %ld\n", fw->kpus, + dev_warn(rvu->dev, "Not enough KPUs: %d > %zu\n", fw->kpus, profile->kpus); return -EINVAL; } @@ -1718,28 +1745,28 @@ int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type) int bank_off, sb_id; /* mcam_idx should be less than (2 * bank depth) */ - if (mcam_idx >= npc_priv.bank_depth * 2) { + if (mcam_idx >= npc_priv->bank_depth * 2) { dev_err(rvu->dev, "%s: bad params\n", __func__); return -EINVAL; } /* find mcam offset per bank */ - bank_off = mcam_idx & (npc_priv.bank_depth - 1); + bank_off = mcam_idx & (npc_priv->bank_depth - 1); /* Find subbank id */ - sb_id = bank_off / npc_priv.subbank_depth; + sb_id = bank_off / npc_priv->subbank_depth; /* Check if subbank id is more than maximum * number of subbanks available */ - if (sb_id >= npc_priv.num_subbanks) { + if (sb_id >= npc_priv->num_subbanks) { dev_err(rvu->dev, "%s: invalid subbank %d\n", __func__, sb_id); return -EINVAL; } - sb = &npc_priv.sb[sb_id]; + sb = &npc_priv->sb[sb_id]; *key_type = sb->key_type; @@ -1755,7 +1782,7 @@ static int npc_subbank_idx_2_mcam_idx(struct rvu *rvu, struct npc_subbank *sb, * subsection depth - 1 */ if (sb->key_type == NPC_MCAM_KEY_X4 && - sub_off >= npc_priv.subbank_depth) { + sub_off >= npc_priv->subbank_depth) { dev_err(rvu->dev, "%s: Failed to get mcam idx (x4) sb->idx=%u sub_off=%u", __func__, sb->idx, sub_off); @@ -1766,7 +1793,7 @@ static int npc_subbank_idx_2_mcam_idx(struct rvu *rvu, struct npc_subbank *sb, * 2 * subsection depth - 1 */ if (sb->key_type == NPC_MCAM_KEY_X2 && - sub_off >= npc_priv.subbank_depth * 2) { + sub_off >= npc_priv->subbank_depth * 2) { dev_err(rvu->dev, "%s: Failed to get mcam idx (x2) sb->idx=%u sub_off=%u", __func__, sb->idx, sub_off); @@ -1774,55 +1801,55 @@ static int npc_subbank_idx_2_mcam_idx(struct rvu *rvu, struct npc_subbank *sb, } /* Find subbank offset from respective subbank (w.r.t bank) */ - off = sub_off & (npc_priv.subbank_depth - 1); + off = sub_off & (npc_priv->subbank_depth - 1); /* if subsection idx is in bank1, add bank depth, * which is part of sb->b1b */ - bot = sub_off >= npc_priv.subbank_depth ? sb->b1b : sb->b0b; + bot = sub_off >= npc_priv->subbank_depth ? sb->b1b : sb->b0b; *mcam_idx = bot + off; return 0; } -static int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx, - struct npc_subbank **sb, - int *sb_off) +int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx, + struct npc_subbank **sb, + int *sb_off) { int bank_off, sb_id; /* mcam_idx should be less than (2 * bank depth) */ - if (mcam_idx >= npc_priv.bank_depth * 2) { + if (mcam_idx >= npc_priv->bank_depth * 2) { dev_err(rvu->dev, "%s: Invalid mcam idx %u\n", __func__, mcam_idx); return -EINVAL; } /* find mcam offset per bank */ - bank_off = mcam_idx & (npc_priv.bank_depth - 1); + bank_off = mcam_idx & (npc_priv->bank_depth - 1); /* Find subbank id */ - sb_id = bank_off / npc_priv.subbank_depth; + sb_id = bank_off / npc_priv->subbank_depth; /* Check if subbank id is more than maximum * number of subbanks available */ - if (sb_id >= npc_priv.num_subbanks) { + if (sb_id >= npc_priv->num_subbanks) { dev_err(rvu->dev, "%s: invalid subbank %d\n", __func__, sb_id); return -EINVAL; } - *sb = &npc_priv.sb[sb_id]; + *sb = &npc_priv->sb[sb_id]; /* Subbank offset per bank */ - *sb_off = bank_off % npc_priv.subbank_depth; + *sb_off = bank_off % npc_priv->subbank_depth; /* Index in a subbank should add subbank depth * if it is in bank1 */ - if (mcam_idx >= npc_priv.bank_depth) - *sb_off += npc_priv.subbank_depth; + if (mcam_idx >= npc_priv->bank_depth) + *sb_off += npc_priv->subbank_depth; return 0; } @@ -1838,9 +1865,9 @@ static int __npc_subbank_contig_alloc(struct rvu *rvu, int k, offset, delta = 0; int cnt = 0, sbd; - sbd = npc_priv.subbank_depth; + sbd = npc_priv->subbank_depth; - if (sidx >= npc_priv.bank_depth) + if (sidx >= npc_priv->bank_depth) delta = sbd; switch (prio) { @@ -1907,8 +1934,8 @@ static int __npc_subbank_non_contig_alloc(struct rvu *rvu, int cnt = 0, delta; int k, sbd; - sbd = npc_priv.subbank_depth; - delta = sidx >= npc_priv.bank_depth ? sbd : 0; + sbd = npc_priv->subbank_depth; + delta = sidx >= npc_priv->bank_depth ? sbd : 0; switch (prio) { /* Find an area of size 'count' from sidx to eidx */ @@ -1969,7 +1996,7 @@ static void __npc_subbank_sboff_2_off(struct rvu *rvu, struct npc_subbank *sb, { int sbd; - sbd = npc_priv.subbank_depth; + sbd = npc_priv->subbank_depth; *off = sb_off & (sbd - 1); *bmap = (sb_off >= sbd) ? sb->b1map : sb->b0map; @@ -2018,20 +2045,20 @@ static int __npc_subbank_mark_free(struct rvu *rvu, struct npc_subbank *sb) sb->flags = NPC_SUBBANK_FLAG_FREE; sb->key_type = 0; - bitmap_clear(sb->b0map, 0, npc_priv.subbank_depth); - bitmap_clear(sb->b1map, 0, npc_priv.subbank_depth); + bitmap_clear(sb->b0map, 0, npc_priv->subbank_depth); + bitmap_clear(sb->b1map, 0, npc_priv->subbank_depth); - if (!xa_erase(&npc_priv.xa_sb_used, sb->arr_idx)) { + if (!xa_erase(&npc_priv->xa_sb_used, sb->arr_idx)) { dev_err(rvu->dev, "%s: Error to delete from xa_sb_used array\n", __func__); return -EFAULT; } - rc = xa_insert(&npc_priv.xa_sb_free, sb->arr_idx, + rc = xa_insert(&npc_priv->xa_sb_free, sb->arr_idx, xa_mk_value(sb->idx), GFP_KERNEL); if (rc) { - rc = xa_insert(&npc_priv.xa_sb_used, sb->arr_idx, + rc = xa_insert(&npc_priv->xa_sb_used, sb->arr_idx, xa_mk_value(sb->idx), GFP_KERNEL); if (rc) dev_err(rvu->dev, @@ -2060,21 +2087,21 @@ static int __npc_subbank_mark_used(struct rvu *rvu, struct npc_subbank *sb, sb->flags = NPC_SUBBANK_FLAG_USED; sb->key_type = key_type; if (key_type == NPC_MCAM_KEY_X4) - sb->free_cnt = npc_priv.subbank_depth; + sb->free_cnt = npc_priv->subbank_depth; else - sb->free_cnt = 2 * npc_priv.subbank_depth; + sb->free_cnt = 2 * npc_priv->subbank_depth; - bitmap_clear(sb->b0map, 0, npc_priv.subbank_depth); - bitmap_clear(sb->b1map, 0, npc_priv.subbank_depth); + bitmap_clear(sb->b0map, 0, npc_priv->subbank_depth); + bitmap_clear(sb->b1map, 0, npc_priv->subbank_depth); - if (!xa_erase(&npc_priv.xa_sb_free, sb->arr_idx)) { + if (!xa_erase(&npc_priv->xa_sb_free, sb->arr_idx)) { dev_err(rvu->dev, "%s: Error to delete from xa_sb_free array\n", __func__); return -EFAULT; } - rc = xa_insert(&npc_priv.xa_sb_used, sb->arr_idx, + rc = xa_insert(&npc_priv->xa_sb_used, sb->arr_idx, xa_mk_value(sb->idx), GFP_KERNEL); if (rc) dev_err(rvu->dev, @@ -2098,10 +2125,10 @@ static bool __npc_subbank_free(struct rvu *rvu, struct npc_subbank *sb, /* Check whether we can mark whole subbank as free */ if (sb->key_type == NPC_MCAM_KEY_X4) { - if (sb->free_cnt < npc_priv.subbank_depth) + if (sb->free_cnt < npc_priv->subbank_depth) goto done; } else { - if (sb->free_cnt < 2 * npc_priv.subbank_depth) + if (sb->free_cnt < 2 * npc_priv->subbank_depth) goto done; } @@ -2180,7 +2207,7 @@ static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb, /* x4 indexes are from 0 to bank size as it combines two x2 banks */ if (key_type == NPC_MCAM_KEY_X4 && - (ref >= npc_priv.bank_depth || limit >= npc_priv.bank_depth)) { + (ref >= npc_priv->bank_depth || limit >= npc_priv->bank_depth)) { dev_err(rvu->dev, "%s: Wrong ref_enty(%d) or limit(%d) for x4\n", __func__, ref, limit); @@ -2190,8 +2217,8 @@ static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb, /* This function is called either bank0 or bank1 portion of a subbank. * so ref and limit should be on same bank. */ - diffbank = !!((ref & npc_priv.bank_depth) ^ - (limit & npc_priv.bank_depth)); + diffbank = !!((ref & npc_priv->bank_depth) ^ + (limit & npc_priv->bank_depth)); if (diffbank) { dev_err(rvu->dev, "%s: request ref and limit should be from same bank\n", @@ -2215,7 +2242,7 @@ static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb, * or equal to mcam entries available in the subbank if contig. */ if (sb->flags & NPC_SUBBANK_FLAG_FREE) { - if (contig && count > npc_priv.subbank_depth) { + if (contig && count > npc_priv->subbank_depth) { dev_err(rvu->dev, "%s: Less number of entries\n", __func__); return -ENOSPC; @@ -2238,10 +2265,10 @@ static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb, } process: - /* if ref or limit >= npc_priv.bank_depth, index are in bank1. + /* if ref or limit >= npc_priv->bank_depth, index are in bank1. * else bank0. */ - if (ref >= npc_priv.bank_depth) { + if (ref >= npc_priv->bank_depth) { bmap = sb->b1map; t = sb->b1t; b = sb->b1b; @@ -2252,8 +2279,8 @@ static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb, } /* Calculate free slots */ - bw = bitmap_weight(bmap, npc_priv.subbank_depth); - bfree = npc_priv.subbank_depth - bw; + bw = bitmap_weight(bmap, npc_priv->subbank_depth); + bfree = npc_priv->subbank_depth - bw; if (!bfree) { dev_dbg(rvu->dev, "%s: subbank is full\n", __func__); @@ -2382,7 +2409,7 @@ npc_del_from_pf_maps(struct rvu *rvu, u16 mcam_idx) int pcifunc, idx; void *map; - map = xa_erase(&npc_priv.xa_idx2pf_map, mcam_idx); + map = xa_erase(&npc_priv->xa_idx2pf_map, mcam_idx); if (!map) { dev_err(rvu->dev, "%s: failed to erase mcam_idx(%u) from xa_idx2pf map\n", @@ -2391,7 +2418,7 @@ npc_del_from_pf_maps(struct rvu *rvu, u16 mcam_idx) } pcifunc = xa_to_value(map); - map = xa_load(&npc_priv.xa_pf_map, pcifunc); + map = xa_load(&npc_priv->xa_pf_map, pcifunc); if (!map) { dev_err(rvu->dev, "%s: failed to find entry for (%u) from xa_pf_map, mcam=%u\n", @@ -2401,7 +2428,7 @@ npc_del_from_pf_maps(struct rvu *rvu, u16 mcam_idx) idx = xa_to_value(map); - map = xa_erase(&npc_priv.xa_pf2idx_map[idx], mcam_idx); + map = xa_erase(&npc_priv->xa_pf2idx_map[idx], mcam_idx); if (!map) { dev_err(rvu->dev, "%s: failed to erase mcam_idx(%u) from xa_pf2idx_map map\n", @@ -2421,18 +2448,18 @@ npc_add_to_pf_maps(struct rvu *rvu, u16 mcam_idx, int pcifunc) "%s: add2maps mcam_idx(%u) to xa_idx2pf map pcifunc=%#x\n", __func__, mcam_idx, pcifunc); - rc = xa_insert(&npc_priv.xa_idx2pf_map, mcam_idx, + rc = xa_insert(&npc_priv->xa_idx2pf_map, mcam_idx, xa_mk_value(pcifunc), GFP_KERNEL); if (rc) { - map = xa_load(&npc_priv.xa_idx2pf_map, mcam_idx); + map = xa_load(&npc_priv->xa_idx2pf_map, mcam_idx); dev_err(rvu->dev, "%s: failed to insert mcam_idx(%u) to xa_idx2pf map, existing value=%lu\n", __func__, mcam_idx, xa_to_value(map)); return -EFAULT; } - map = xa_load(&npc_priv.xa_pf_map, pcifunc); + map = xa_load(&npc_priv->xa_pf_map, pcifunc); if (!map) { dev_err(rvu->dev, "%s: failed to find pf map entry for pcifunc=%#x, mcam=%u\n", @@ -2442,12 +2469,12 @@ npc_add_to_pf_maps(struct rvu *rvu, u16 mcam_idx, int pcifunc) idx = xa_to_value(map); - rc = xa_insert(&npc_priv.xa_pf2idx_map[idx], mcam_idx, + rc = xa_insert(&npc_priv->xa_pf2idx_map[idx], mcam_idx, xa_mk_value(pcifunc), GFP_KERNEL); if (rc) { - map = xa_load(&npc_priv.xa_pf2idx_map[idx], mcam_idx); - xa_erase(&npc_priv.xa_idx2pf_map, mcam_idx); + map = xa_load(&npc_priv->xa_pf2idx_map[idx], mcam_idx); + xa_erase(&npc_priv->xa_idx2pf_map, mcam_idx); dev_err(rvu->dev, "%s: failed to insert mcam_idx(%u) to xa_pf2idx_map map, earlier value=%lu idx=%u\n", __func__, mcam_idx, xa_to_value(map), idx); @@ -2477,9 +2504,9 @@ npc_subbank_suits(struct npc_subbank *sb, int key_type) return false; } -#define SB_ALIGN_UP(val) (((val) + npc_priv.subbank_depth) & \ - ~((npc_priv.subbank_depth) - 1)) -#define SB_ALIGN_DOWN(val) ALIGN_DOWN((val), npc_priv.subbank_depth) +#define SB_ALIGN_UP(val) (((val) + npc_priv->subbank_depth) & \ + ~((npc_priv->subbank_depth) - 1)) +#define SB_ALIGN_DOWN(val) ALIGN_DOWN((val), npc_priv->subbank_depth) static void npc_subbank_iter_down(struct rvu *rvu, int ref, int limit, @@ -2505,7 +2532,7 @@ static void npc_subbank_iter_down(struct rvu *rvu, } *cur_ref = *cur_limit - 1; - align = *cur_ref - npc_priv.subbank_depth + 1; + align = *cur_ref - npc_priv->subbank_depth + 1; if (align <= limit) { *stop = true; *cur_limit = limit; @@ -2545,7 +2572,7 @@ static void npc_subbank_iter_up(struct rvu *rvu, } *cur_ref = *cur_limit + 1; - align = *cur_ref + npc_priv.subbank_depth - 1; + align = *cur_ref + npc_priv->subbank_depth - 1; if (align >= limit) { *stop = true; @@ -2573,17 +2600,17 @@ npc_subbank_iter(struct rvu *rvu, int key_type, /* limit and ref should < bank_depth for x4 */ if (key_type == NPC_MCAM_KEY_X4) { - if (*cur_ref >= npc_priv.bank_depth) + if (*cur_ref >= npc_priv->bank_depth) return -EINVAL; - if (*cur_limit >= npc_priv.bank_depth) + if (*cur_limit >= npc_priv->bank_depth) return -EINVAL; } /* limit and ref should < 2 * bank_depth, for x2 */ - if (*cur_ref >= 2 * npc_priv.bank_depth) + if (*cur_ref >= 2 * npc_priv->bank_depth) return -EINVAL; - if (*cur_limit >= 2 * npc_priv.bank_depth) + if (*cur_limit >= 2 * npc_priv->bank_depth) return -EINVAL; return 0; @@ -2618,7 +2645,7 @@ static int npc_idx_free(struct rvu *rvu, u16 *mcam_idx, int count, vidx = npc_idx2vidx(midx); } - if (midx >= npc_priv.bank_depth * npc_priv.num_banks) { + if (midx >= npc_priv->bank_depth * npc_priv->num_banks) { dev_err(rvu->dev, "%s: Invalid mcam_idx=%u cannot be deleted\n", __func__, mcam_idx[i]); @@ -2813,7 +2840,7 @@ static int npc_subbank_free_cnt(struct rvu *rvu, struct npc_subbank *sb, { int cnt, spd; - spd = npc_priv.subbank_depth; + spd = npc_priv->subbank_depth; mutex_lock(&sb->lock); if (sb->flags & NPC_SUBBANK_FLAG_FREE) @@ -2972,7 +2999,7 @@ static int npc_subbank_noref_alloc(struct rvu *rvu, int key_type, bool contig, max_alloc = !contig; /* Check used subbanks for free slots */ - xa_for_each(&npc_priv.xa_sb_used, index, val) { + xa_for_each(&npc_priv->xa_sb_used, index, val) { idx = xa_to_value(val); /* Minimize allocation from restricted subbanks @@ -2981,7 +3008,7 @@ static int npc_subbank_noref_alloc(struct rvu *rvu, int key_type, bool contig, if (npc_subbank_restrict_usage(rvu, idx)) continue; - sb = &npc_priv.sb[idx]; + sb = &npc_priv->sb[idx]; /* Skip if not suitable subbank */ if (!npc_subbank_suits(sb, key_type)) @@ -3038,9 +3065,9 @@ static int npc_subbank_noref_alloc(struct rvu *rvu, int key_type, bool contig, } /* Allocate in free subbanks */ - xa_for_each(&npc_priv.xa_sb_free, index, val) { + xa_for_each(&npc_priv->xa_sb_free, index, val) { idx = xa_to_value(val); - sb = &npc_priv.sb[idx]; + sb = &npc_priv->sb[idx]; /* Minimize allocation from restricted subbanks * in noref allocations. @@ -3096,7 +3123,7 @@ static int npc_subbank_noref_alloc(struct rvu *rvu, int key_type, bool contig, for (i = 0; restrict_valid && (i < ARRAY_SIZE(npc_subbank_restricted_idxs)); i++) { idx = npc_subbank_restricted_idxs[i]; - sb = &npc_priv.sb[idx]; + sb = &npc_priv->sb[idx]; /* Skip if not suitable subbank */ if (!npc_subbank_suits(sb, key_type)) @@ -3176,7 +3203,7 @@ int npc_cn20k_ref_idx_alloc(struct rvu *rvu, int pcifunc, int key_type, bool ref_valid; u16 vidx; - bd = npc_priv.bank_depth; + bd = npc_priv->bank_depth; /* Special case: ref == 0 && limit= 0 && prio == HIGH && count == 1 * Here user wants to allocate 0th entry @@ -3194,7 +3221,7 @@ int npc_cn20k_ref_idx_alloc(struct rvu *rvu, int pcifunc, int key_type, ref_valid = !!(limit || ref); defrag_candidate = !ref_valid && !contig && virt; if (!ref_valid) { - if (contig && count > npc_priv.subbank_depth) + if (contig && count > npc_priv->subbank_depth) goto try_noref_multi_subbank; rc = npc_subbank_noref_alloc(rvu, key_type, contig, @@ -3239,7 +3266,7 @@ int npc_cn20k_ref_idx_alloc(struct rvu *rvu, int pcifunc, int key_type, return -EINVAL; } - if (contig && count > npc_priv.subbank_depth) + if (contig && count > npc_priv->subbank_depth) goto try_ref_multi_subbank; rc = npc_subbank_ref_alloc(rvu, key_type, ref, limit, @@ -3301,8 +3328,8 @@ void npc_cn20k_subbank_calc_free(struct rvu *rvu, int *x2_free, *x4_free = 0; *sb_free = 0; - for (i = 0; i < npc_priv.num_subbanks; i++) { - sb = &npc_priv.sb[i]; + for (i = 0; i < npc_priv->num_subbanks; i++) { + sb = &npc_priv->sb[i]; mutex_lock(&sb->lock); /* Count number of free subbanks */ @@ -3366,7 +3393,7 @@ rvu_mbox_handler_npc_cn20k_get_kex_cfg(struct rvu *rvu, return 0; } -static int *subbank_srch_order; +static u32 *subbank_srch_order; static void npc_populate_restricted_idxs(int num_subbanks) { @@ -3378,7 +3405,7 @@ static int npc_create_srch_order(int cnt) { int val = 0; - subbank_srch_order = kcalloc(cnt, sizeof(int), + subbank_srch_order = kcalloc(cnt, sizeof(u32), GFP_KERNEL); if (!subbank_srch_order) return -ENOMEM; @@ -3400,11 +3427,11 @@ static void npc_subbank_init(struct rvu *rvu, struct npc_subbank *sb, int idx) { mutex_init(&sb->lock); - sb->b0b = idx * npc_priv.subbank_depth; - sb->b0t = sb->b0b + npc_priv.subbank_depth - 1; + sb->b0b = idx * npc_priv->subbank_depth; + sb->b0t = sb->b0b + npc_priv->subbank_depth - 1; - sb->b1b = npc_priv.bank_depth + idx * npc_priv.subbank_depth; - sb->b1t = sb->b1b + npc_priv.subbank_depth - 1; + sb->b1b = npc_priv->bank_depth + idx * npc_priv->subbank_depth; + sb->b1t = sb->b1b + npc_priv->subbank_depth - 1; sb->flags = NPC_SUBBANK_FLAG_FREE; sb->idx = idx; @@ -3416,7 +3443,7 @@ static void npc_subbank_init(struct rvu *rvu, struct npc_subbank *sb, int idx) /* Keep first and last subbank at end of free array; so that * it will be used at last */ - xa_store(&npc_priv.xa_sb_free, sb->arr_idx, + xa_store(&npc_priv->xa_sb_free, sb->arr_idx, xa_mk_value(sb->idx), GFP_KERNEL); } @@ -3441,7 +3468,7 @@ static int npc_pcifunc_map_create(struct rvu *rvu) pcifunc = pf << 9; - xa_store(&npc_priv.xa_pf_map, (unsigned long)pcifunc, + xa_store(&npc_priv->xa_pf_map, (unsigned long)pcifunc, xa_mk_value(cnt), GFP_KERNEL); cnt++; @@ -3450,7 +3477,7 @@ static int npc_pcifunc_map_create(struct rvu *rvu) for (vf = 0; vf < numvfs; vf++) { pcifunc = (pf << 9) | (vf + 1); - xa_store(&npc_priv.xa_pf_map, (unsigned long)pcifunc, + xa_store(&npc_priv->xa_pf_map, (unsigned long)pcifunc, xa_mk_value(cnt), GFP_KERNEL); cnt++; } @@ -3536,7 +3563,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, int rc, sb_off, i, err; bool deleted; - sb = &npc_priv.sb[f->idx]; + sb = &npc_priv->sb[f->idx]; alloc_cnt1 = 0; alloc_cnt2 = 0; @@ -3606,9 +3633,9 @@ static int npc_defrag_add_2_show_list(struct rvu *rvu, u16 old_midx, node->vidx = vidx; INIT_LIST_HEAD(&node->list); - mutex_lock(&npc_priv.lock); - list_add_tail(&node->list, &npc_priv.defrag_lh); - mutex_unlock(&npc_priv.lock); + mutex_lock(&npc_priv->lock); + list_add_tail(&node->list, &npc_priv->defrag_lh); + mutex_unlock(&npc_priv->lock); return 0; } @@ -3712,7 +3739,7 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, } /* save pcifunc */ - map = xa_load(&npc_priv.xa_idx2pf_map, old_midx); + map = xa_load(&npc_priv->xa_idx2pf_map, old_midx); pcifunc = xa_to_value(map); /* delete from pf maps */ @@ -3871,29 +3898,145 @@ static void npc_defrag_list_clear(void) { struct npc_defrag_show_node *node, *next; - mutex_lock(&npc_priv.lock); - list_for_each_entry_safe(node, next, &npc_priv.defrag_lh, list) { + mutex_lock(&npc_priv->lock); + list_for_each_entry_safe(node, next, &npc_priv->defrag_lh, list) { list_del_init(&node->list); kfree(node); } - mutex_unlock(&npc_priv.lock); + mutex_unlock(&npc_priv->lock); } static void npc_lock_all_subbank(void) { int i; - for (i = 0; i < npc_priv.num_subbanks; i++) - mutex_lock(&npc_priv.sb[i].lock); + for (i = 0; i < npc_priv->num_subbanks; i++) + mutex_lock(&npc_priv->sb[i].lock); } static void npc_unlock_all_subbank(void) { int i; - for (i = npc_priv.num_subbanks - 1; i >= 0; i--) - mutex_unlock(&npc_priv.sb[i].lock); + for (i = npc_priv->num_subbanks - 1; i >= 0; i--) + mutex_unlock(&npc_priv->sb[i].lock); +} + +int npc_cn20k_search_order_set(struct rvu *rvu, + u64 narr[MAX_NUM_SUB_BANKS], int cnt) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + int rsrc[2][MAX_NUM_SUB_BANKS] = { }; + u8 save[MAX_NUM_SUB_BANKS] = { }; + struct npc_subbank *sb; + struct xarray *xa; + int prio, rc, err; + int sb_idx; + enum { + FREE = 0, + USED = 1, + }; + + if (cnt != npc_priv->num_subbanks) { + dev_err(rvu->dev, "Number of entries(%u) != %u\n", + cnt, npc_priv->num_subbanks); + return -EINVAL; + } + + mutex_lock(&mcam->lock); + npc_lock_all_subbank(); + + for (sb_idx = 0; sb_idx < cnt; sb_idx++) { + sb = &npc_priv->sb[sb_idx]; + save[sb->idx] = sb->arr_idx; + } + + for (prio = 0; prio < cnt; prio++) { + sb_idx = narr[prio]; + sb = &npc_priv->sb[sb_idx]; + + if (sb->flags & NPC_SUBBANK_FLAG_USED) + xa = &npc_priv->xa_sb_used; + else + xa = &npc_priv->xa_sb_free; + + rc = xa_err(xa_store(xa, prio, + xa_mk_value(sb_idx), GFP_KERNEL)); + if (rc) { + dev_err(rvu->dev, + "Setting arr_idx=%d for sb=%d failed\n", + sb->arr_idx, sb_idx); + goto fail; + } + + if (sb->flags & NPC_SUBBANK_FLAG_USED) { + rsrc[USED][sb->arr_idx] -= 1; + rsrc[USED][prio] += 1; + } else { + rsrc[FREE][sb->arr_idx] -= 1; + rsrc[FREE][prio] += 1; + } + + sb->arr_idx = prio; + } + + for (prio = 0; prio < cnt; prio++) { + if (rsrc[FREE][prio] == -1) + xa_erase(&npc_priv->xa_sb_free, prio); + + if (rsrc[USED][prio] == -1) + xa_erase(&npc_priv->xa_sb_used, prio); + } + + for (int i = 0; i < cnt; i++) + subbank_srch_order[i] = (u32)narr[i]; + + restrict_valid = false; + + npc_unlock_all_subbank(); + mutex_unlock(&mcam->lock); + + return 0; + +fail: + for (prio = 0; prio < cnt; prio++) { + if (rsrc[FREE][prio] == 1) + xa_erase(&npc_priv->xa_sb_free, prio); + + if (rsrc[USED][prio] == 1) + xa_erase(&npc_priv->xa_sb_used, prio); + } + + for (sb_idx = 0; sb_idx < cnt; sb_idx++) { + sb = &npc_priv->sb[sb_idx]; + sb->arr_idx = save[sb_idx]; + + if (sb->flags & NPC_SUBBANK_FLAG_USED) + xa = &npc_priv->xa_sb_used; + else + xa = &npc_priv->xa_sb_free; + + /* Since the entry already exists, xa_store() replaces + * the value without a kmalloc(), making failure highly unlikely. + */ + err = xa_err(xa_store(xa, sb->arr_idx, + xa_mk_value(sb->idx), GFP_KERNEL)); + WARN(!!err, "Failed to rollback sb=%u idx=%u\n", + sb->idx, sb->arr_idx); + } + + npc_unlock_all_subbank(); + mutex_unlock(&mcam->lock); + + return rc; +} + +const u32 *npc_cn20k_search_order_get(bool *restricted_order, u32 *sz) +{ + *restricted_order = restrict_valid; + *sz = npc_priv->num_subbanks; + return subbank_srch_order; } /* Only non-ref non-contigous mcam indexes @@ -3916,7 +4059,7 @@ int npc_cn20k_defrag(struct rvu *rvu) INIT_LIST_HEAD(&x4lh); INIT_LIST_HEAD(&x2lh); - node = kcalloc(npc_priv.num_subbanks, sizeof(*node), GFP_KERNEL); + node = kcalloc(npc_priv->num_subbanks, sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; @@ -3925,13 +4068,13 @@ int npc_cn20k_defrag(struct rvu *rvu) npc_lock_all_subbank(); /* Fill in node with subbank properties */ - for (i = 0; i < npc_priv.num_subbanks; i++) { - sb = &npc_priv.sb[i]; + for (i = 0; i < npc_priv->num_subbanks; i++) { + sb = &npc_priv->sb[i]; node[i].idx = i; node[i].key_type = sb->key_type; node[i].free_cnt = sb->free_cnt; - node[i].vidx = kcalloc(npc_priv.subbank_depth * 2, + node[i].vidx = kcalloc(npc_priv->subbank_depth * 2, sizeof(*node[i].vidx), GFP_KERNEL); if (!node[i].vidx) { @@ -3961,8 +4104,8 @@ int npc_cn20k_defrag(struct rvu *rvu) } /* Filling vidx[] array with all vidx in that subbank */ - xa_for_each_start(&npc_priv.xa_vidx2idx_map, index, map, - npc_priv.bank_depth * 2) { + xa_for_each_start(&npc_priv->xa_vidx2idx_map, index, map, + npc_priv->bank_depth * 2) { midx = xa_to_value(map); rc = npc_mcam_idx_2_subbank_idx(rvu, midx, &sb, &sb_off); @@ -3979,14 +4122,14 @@ int npc_cn20k_defrag(struct rvu *rvu) } /* Mark all subbank which has ref allocation */ - for (i = 0; i < npc_priv.num_subbanks; i++) { + for (i = 0; i < npc_priv->num_subbanks; i++) { tnode = &node[i]; if (!tnode->valid) continue; tot = (tnode->key_type == NPC_MCAM_KEY_X2) ? - npc_priv.subbank_depth * 2 : npc_priv.subbank_depth; + npc_priv->subbank_depth * 2 : npc_priv->subbank_depth; if (node[i].vidx_cnt != tot - tnode->free_cnt) tnode->refs = true; @@ -4003,7 +4146,7 @@ int npc_cn20k_defrag(struct rvu *rvu) free_vidx: npc_unlock_all_subbank(); mutex_unlock(&mcam->lock); - for (i = 0; i < npc_priv.num_subbanks; i++) + for (i = 0; i < npc_priv->num_subbanks; i++) kfree(node[i].vidx); kfree(node); return rc; @@ -4031,7 +4174,7 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, *ptr[i] = USHRT_MAX; } - if (!npc_priv.init_done) + if (!npc_priv) return 0; if (is_lbk_vf(rvu, pcifunc)) { @@ -4039,7 +4182,7 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, return -EINVAL; idx = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_PROMISC_ID); - val = xa_load(&npc_priv.xa_pf2dfl_rmap, idx); + val = xa_load(&npc_priv->xa_pf2dfl_rmap, idx); if (!val) { pr_debug("%s: Failed to find %s index for pcifunc=%#x\n", __func__, @@ -4058,7 +4201,7 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, return -EINVAL; idx = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_UCAST_ID); - val = xa_load(&npc_priv.xa_pf2dfl_rmap, idx); + val = xa_load(&npc_priv->xa_pf2dfl_rmap, idx); if (!val) { pr_debug("%s: Failed to find %s index for pcifunc=%#x\n", __func__, @@ -4078,7 +4221,7 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, continue; idx = NPC_DFT_RULE_ID_MK(pcifunc, i); - val = xa_load(&npc_priv.xa_pf2dfl_rmap, idx); + val = xa_load(&npc_priv->xa_pf2dfl_rmap, idx); if (!val) { pr_debug("%s: Failed to find %s index for pcifunc=%#x\n", __func__, @@ -4102,8 +4245,8 @@ int rvu_mbox_handler_npc_get_pfl_info(struct rvu *rvu, struct msg_req *req, return -EOPNOTSUPP; } - rsp->kw_type = npc_priv.kw; - rsp->x4_slots = npc_priv.bank_depth; + rsp->kw_type = npc_priv->kw; + rsp->x4_slots = npc_priv->bank_depth; return 0; } @@ -4193,7 +4336,7 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) int blkaddr, rc, i; void *map; - if (!npc_priv.init_done) + if (!npc_priv) return; if (!npc_is_cgx_or_lbk(rvu, pcifunc)) { @@ -4211,7 +4354,7 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) /* LBK */ if (is_lbk_vf(rvu, pcifunc)) { index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_PROMISC_ID); - map = xa_erase(&npc_priv.xa_pf2dfl_rmap, index); + map = xa_erase(&npc_priv->xa_pf2dfl_rmap, index); if (!map) dev_dbg(rvu->dev, "%s: Err from delete %s mcam idx from xarray (pcifunc=%#x\n", @@ -4225,7 +4368,7 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) /* VF */ if (is_vf(pcifunc)) { index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_UCAST_ID); - map = xa_erase(&npc_priv.xa_pf2dfl_rmap, index); + map = xa_erase(&npc_priv->xa_pf2dfl_rmap, index); if (!map) dev_dbg(rvu->dev, "%s: Err from delete %s mcam idx from xarray (pcifunc=%#x\n", @@ -4239,7 +4382,7 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) /* PF */ for (i = NPC_DFT_RULE_START_ID; i < NPC_DFT_RULE_MAX_ID; i++) { index = NPC_DFT_RULE_ID_MK(pcifunc, i); - map = xa_erase(&npc_priv.xa_pf2dfl_rmap, index); + map = xa_erase(&npc_priv->xa_pf2dfl_rmap, index); if (!map) dev_dbg(rvu->dev, "%s: Err from delete %s mcam idx from xarray (pcifunc=%#x\n", @@ -4299,7 +4442,7 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) struct msg_rsp free_rsp; u16 b, m, p, u; - if (!npc_priv.init_done) + if (!npc_priv) return 0; if (!npc_is_cgx_or_lbk(rvu, pcifunc)) { @@ -4322,7 +4465,7 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) } /* Set ref index as lowest priority index */ - eidx = 2 * npc_priv.bank_depth - 1; + eidx = 2 * npc_priv->bank_depth - 1; /* Install only UCAST for VF */ cnt = is_vf(pcifunc) ? 1 : ARRAY_SIZE(mcam_idx); @@ -4351,10 +4494,16 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) pfvf = rvu_get_pfvf(rvu, pcifunc); pfvf->hw_prio = NPC_DFT_RULE_PRIO; + if (npc_priv->kw == NPC_MCAM_KEY_X4) { + req.kw_type = NPC_MCAM_KEY_X4; + req.ref_entry = eidx & (npc_priv->bank_depth - 1); + } else { + req.kw_type = NPC_MCAM_KEY_X2; + req.ref_entry = eidx; + } + req.contig = false; req.ref_prio = NPC_MCAM_HIGHER_PRIO; - req.ref_entry = eidx; - req.kw_type = NPC_MCAM_KEY_X2; req.count = cnt; req.hdr.pcifunc = pcifunc; @@ -4384,11 +4533,18 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) * as NPC_DFT_RULE_PRIO - 1 (higher hw priority) */ req.contig = false; - req.kw_type = NPC_MCAM_KEY_X2; req.count = cnt; req.hdr.pcifunc = pcifunc; req.ref_prio = NPC_MCAM_LOWER_PRIO; - req.ref_entry = eidx + 1; + + if (npc_priv->kw == NPC_MCAM_KEY_X4) { + req.kw_type = NPC_MCAM_KEY_X4; + req.ref_entry = eidx & (npc_priv->bank_depth - 1); + } else { + req.kw_type = NPC_MCAM_KEY_X2; + req.ref_entry = eidx; + } + ret = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &req, &rsp); if (ret) { dev_err(rvu->dev, @@ -4407,7 +4563,7 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) /* LBK */ if (is_lbk_vf(rvu, pcifunc)) { index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_PROMISC_ID); - ret = xa_insert(&npc_priv.xa_pf2dfl_rmap, index, + ret = xa_insert(&npc_priv->xa_pf2dfl_rmap, index, xa_mk_value(mcam_idx[0]), GFP_KERNEL); if (ret) { dev_err(rvu->dev, @@ -4424,7 +4580,7 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) /* VF */ if (is_vf(pcifunc)) { index = NPC_DFT_RULE_ID_MK(pcifunc, NPC_DFT_RULE_UCAST_ID); - ret = xa_insert(&npc_priv.xa_pf2dfl_rmap, index, + ret = xa_insert(&npc_priv->xa_pf2dfl_rmap, index, xa_mk_value(mcam_idx[0]), GFP_KERNEL); if (ret) { dev_err(rvu->dev, @@ -4442,7 +4598,7 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) for (i = NPC_DFT_RULE_START_ID, k = 0; i < NPC_DFT_RULE_MAX_ID && k < cnt; i++, k++) { index = NPC_DFT_RULE_ID_MK(pcifunc, i); - ret = xa_insert(&npc_priv.xa_pf2dfl_rmap, index, + ret = xa_insert(&npc_priv->xa_pf2dfl_rmap, index, xa_mk_value(mcam_idx[k]), GFP_KERNEL); if (ret) { dev_err(rvu->dev, @@ -4451,7 +4607,7 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) pcifunc); for (int p = NPC_DFT_RULE_START_ID; p < i; p++) { index = NPC_DFT_RULE_ID_MK(pcifunc, p); - xa_erase(&npc_priv.xa_pf2dfl_rmap, index); + xa_erase(&npc_priv->xa_pf2dfl_rmap, index); } goto err; } @@ -4494,11 +4650,17 @@ static int npc_priv_init(struct rvu *rvu) npc_const2 = rvu_read64(rvu, blkaddr, NPC_AF_CONST2); num_banks = mcam->banks; - bank_depth = mcam->banksize; + if (!num_banks || num_banks > MAX_NUM_BANKS) { + dev_err(rvu->dev, + "Number of banks(%u) is invalid\n", num_banks); + return -EINVAL; + } num_subbanks = FIELD_GET(GENMASK_ULL(39, 32), npc_const2); - if (!num_subbanks) { - dev_err(rvu->dev, "Number of subbanks is zero\n"); + if (!num_subbanks || num_subbanks > MAX_NUM_SUB_BANKS) { + dev_err(rvu->dev, + "Number of subbanks is invalid %u\n", + num_subbanks); return -EFAULT; } @@ -4509,74 +4671,95 @@ static int npc_priv_init(struct rvu *rvu) return -EINVAL; } - npc_priv.num_subbanks = num_subbanks; + bank_depth = mcam->banksize; + if (!bank_depth || bank_depth % num_subbanks) { + dev_err(rvu->dev, + "Bank depth(%u) should be a multiple of num_subbanks(%u)\n", + bank_depth, num_subbanks); + return -EINVAL; + } subbank_depth = bank_depth / num_subbanks; + if (!subbank_depth || subbank_depth > MAX_SUBBANK_DEPTH) { + dev_err(rvu->dev, + "Invalid subbank depth %u\n", + subbank_depth); + return -EINVAL; + } - npc_priv.bank_depth = bank_depth; - npc_priv.subbank_depth = subbank_depth; + npc_priv = kcalloc(1, sizeof(*npc_priv), GFP_KERNEL); + if (!npc_priv) + return -ENOMEM; + + npc_priv->num_banks = num_banks; + npc_priv->num_subbanks = num_subbanks; + npc_priv->bank_depth = bank_depth; + npc_priv->subbank_depth = subbank_depth; /* Get kex configured key size */ cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(0)); - npc_priv.kw = FIELD_GET(GENMASK_ULL(34, 32), cfg); + npc_priv->kw = FIELD_GET(GENMASK_ULL(34, 32), cfg); dev_info(rvu->dev, "banks=%u depth=%u, subbanks=%u depth=%u, key type=%s\n", num_banks, bank_depth, num_subbanks, subbank_depth, - npc_kw_name[npc_priv.kw]); + npc_kw_name[npc_priv->kw]); - npc_priv.sb = kcalloc(num_subbanks, sizeof(struct npc_subbank), - GFP_KERNEL); - if (!npc_priv.sb) - return -ENOMEM; + npc_priv->sb = kcalloc(num_subbanks, sizeof(struct npc_subbank), + GFP_KERNEL); + if (!npc_priv->sb) + goto fail1; - xa_init_flags(&npc_priv.xa_sb_used, XA_FLAGS_ALLOC); - xa_init_flags(&npc_priv.xa_sb_free, XA_FLAGS_ALLOC); - xa_init_flags(&npc_priv.xa_idx2pf_map, XA_FLAGS_ALLOC); - xa_init_flags(&npc_priv.xa_pf_map, XA_FLAGS_ALLOC); - xa_init_flags(&npc_priv.xa_pf2dfl_rmap, XA_FLAGS_ALLOC); - xa_init_flags(&npc_priv.xa_idx2vidx_map, XA_FLAGS_ALLOC); - xa_init_flags(&npc_priv.xa_vidx2idx_map, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_sb_used, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_sb_free, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_idx2pf_map, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_pf_map, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_pf2dfl_rmap, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_idx2vidx_map, XA_FLAGS_ALLOC); + xa_init_flags(&npc_priv->xa_vidx2idx_map, XA_FLAGS_ALLOC); if (npc_create_srch_order(num_subbanks)) - goto fail1; + goto fail2; npc_populate_restricted_idxs(num_subbanks); /* Initialize subbanks */ - for (i = 0, sb = npc_priv.sb; i < num_subbanks; i++, sb++) + for (i = 0, sb = npc_priv->sb; i < num_subbanks; i++, sb++) npc_subbank_init(rvu, sb, i); /* Get number of pcifuncs in the system */ - npc_priv.pf_cnt = npc_pcifunc_map_create(rvu); - npc_priv.xa_pf2idx_map = kcalloc(npc_priv.pf_cnt, - sizeof(struct xarray), - GFP_KERNEL); - if (!npc_priv.xa_pf2idx_map) - goto fail2; + npc_priv->pf_cnt = npc_pcifunc_map_create(rvu); + npc_priv->xa_pf2idx_map = kcalloc(npc_priv->pf_cnt, + sizeof(struct xarray), + GFP_KERNEL); + if (!npc_priv->xa_pf2idx_map) + goto fail3; - for (i = 0; i < npc_priv.pf_cnt; i++) - xa_init_flags(&npc_priv.xa_pf2idx_map[i], XA_FLAGS_ALLOC); + for (i = 0; i < npc_priv->pf_cnt; i++) + xa_init_flags(&npc_priv->xa_pf2idx_map[i], XA_FLAGS_ALLOC); - INIT_LIST_HEAD(&npc_priv.defrag_lh); - mutex_init(&npc_priv.lock); + INIT_LIST_HEAD(&npc_priv->defrag_lh); + mutex_init(&npc_priv->lock); return 0; -fail2: +fail3: kfree(subbank_srch_order); subbank_srch_order = NULL; +fail2: + xa_destroy(&npc_priv->xa_sb_used); + xa_destroy(&npc_priv->xa_sb_free); + xa_destroy(&npc_priv->xa_idx2pf_map); + xa_destroy(&npc_priv->xa_pf_map); + xa_destroy(&npc_priv->xa_pf2dfl_rmap); + xa_destroy(&npc_priv->xa_idx2vidx_map); + xa_destroy(&npc_priv->xa_vidx2idx_map); + kfree(npc_priv->sb); + npc_priv->sb = NULL; fail1: - xa_destroy(&npc_priv.xa_sb_used); - xa_destroy(&npc_priv.xa_sb_free); - xa_destroy(&npc_priv.xa_idx2pf_map); - xa_destroy(&npc_priv.xa_pf_map); - xa_destroy(&npc_priv.xa_pf2dfl_rmap); - xa_destroy(&npc_priv.xa_idx2vidx_map); - xa_destroy(&npc_priv.xa_vidx2idx_map); - kfree(npc_priv.sb); - npc_priv.sb = NULL; + kfree(npc_priv); + npc_priv = NULL; return -ENOMEM; } @@ -4584,23 +4767,31 @@ void npc_cn20k_deinit(struct rvu *rvu) { int i; - xa_destroy(&npc_priv.xa_sb_used); - xa_destroy(&npc_priv.xa_sb_free); - xa_destroy(&npc_priv.xa_idx2pf_map); - xa_destroy(&npc_priv.xa_pf_map); - xa_destroy(&npc_priv.xa_pf2dfl_rmap); - xa_destroy(&npc_priv.xa_idx2vidx_map); - xa_destroy(&npc_priv.xa_vidx2idx_map); + if (!npc_priv) + return; - for (i = 0; i < npc_priv.pf_cnt; i++) - xa_destroy(&npc_priv.xa_pf2idx_map[i]); + xa_destroy(&npc_priv->xa_sb_used); + xa_destroy(&npc_priv->xa_sb_free); + xa_destroy(&npc_priv->xa_idx2pf_map); + xa_destroy(&npc_priv->xa_pf_map); + xa_destroy(&npc_priv->xa_pf2dfl_rmap); + xa_destroy(&npc_priv->xa_idx2vidx_map); + xa_destroy(&npc_priv->xa_vidx2idx_map); - kfree(npc_priv.xa_pf2idx_map); + for (i = 0; i < npc_priv->pf_cnt; i++) + xa_destroy(&npc_priv->xa_pf2idx_map[i]); + + kfree(npc_priv->xa_pf2idx_map); /* No need to destroy mutex lock as it is * part of subbank structure */ - kfree(npc_priv.sb); + kfree(npc_priv->sb); kfree(subbank_srch_order); + bitmap_clear(npc_priv->en_map, 0, MAX_NUM_BANKS * MAX_NUM_SUB_BANKS * + MAX_SUBBANK_DEPTH); + npc_defrag_list_clear(); + kfree(npc_priv); + npc_priv = NULL; } static int npc_setup_mcam_section(struct rvu *rvu, int key_type) @@ -4613,7 +4804,7 @@ static int npc_setup_mcam_section(struct rvu *rvu, int key_type) return -ENODEV; } - for (sec = 0; sec < npc_priv.num_subbanks; sec++) + for (sec = 0; sec < npc_priv->num_subbanks; sec++) rvu_write64(rvu, blkaddr, NPC_AF_MCAM_SECTIONX_CFG_EXT(sec), key_type); @@ -4635,10 +4826,12 @@ int npc_cn20k_init(struct rvu *rvu) if (err) { dev_err(rvu->dev, "%s: mcam section configuration failure\n", __func__); - return err; + goto fail; } - npc_priv.init_done = true; - return 0; + +fail: + npc_cn20k_deinit(rvu); + return err; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h index 3d5eb952cc07..10e5bab50f62 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h @@ -10,6 +10,10 @@ #define MKEX_CN20K_SIGN 0x19bbfdbd160 +/* MAX_NUM_BANKS, MAX_SUBBANK_DEPTH and MAX_NUM_SUB_BANKS represent + * hard limit on all silicon variants, preventing any possibility of + * out-of-bounds access on matrix defined using these values. + */ #define MAX_NUM_BANKS 2 #define MAX_NUM_SUB_BANKS 32 #define MAX_SUBBANK_DEPTH 256 @@ -170,6 +174,7 @@ struct npc_defrag_show_node { * @num_banks: Number of banks. * @num_subbanks: Number of subbanks. * @subbank_depth: Depth of subbank. + * @en_map: Enable/disable status. * @kw: Kex configured key type. * @sb: Subbank array. * @xa_sb_used: Array of used subbanks. @@ -178,7 +183,6 @@ struct npc_defrag_show_node { * @xa_idx2pf_map: Mcam index to PF map. * @xa_pf_map: Pcifunc to index map. * @pf_cnt: Number of PFs. - * @init_done: Indicates MCAM initialization is done. * @xa_pf2dfl_rmap: PF to default rule index map. * @xa_idx2vidx_map: Mcam index to virtual index map. * @xa_vidx2idx_map: virtual index to mcam index map. @@ -190,9 +194,12 @@ struct npc_defrag_show_node { */ struct npc_priv_t { int bank_depth; - const int num_banks; + int num_banks; int num_subbanks; int subbank_depth; + DECLARE_BITMAP(en_map, MAX_NUM_BANKS * + MAX_NUM_SUB_BANKS * + MAX_SUBBANK_DEPTH); u8 kw; struct npc_subbank *sb; struct xarray xa_sb_used; @@ -206,7 +213,6 @@ struct npc_priv_t { struct list_head defrag_lh; struct mutex lock; /* protect defrag nodes */ int pf_cnt; - bool init_done; }; struct npc_kpm_action0 { @@ -336,5 +342,11 @@ u16 npc_cn20k_vidx2idx(u16 index); u16 npc_cn20k_idx2vidx(u16 idx); int npc_cn20k_defrag(struct rvu *rvu); bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc); +int npc_mcam_idx_2_subbank_idx(struct rvu *rvu, u16 mcam_idx, + struct npc_subbank **sb, + int *sb_off); +const u32 *npc_cn20k_search_order_get(bool *restricted_order, u32 *sz); +int npc_cn20k_search_order_set(struct rvu *rvu, u64 narr[MAX_NUM_SUB_BANKS], + int cnt); #endif /* NPC_CN20K_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index dc42c81c0942..714e47f68d93 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -283,31 +283,30 @@ M(NPC_GET_FIELD_HASH_INFO, 0x6013, npc_get_field_hash_info, M(NPC_GET_FIELD_STATUS, 0x6014, npc_get_field_status, \ npc_get_field_status_req, \ npc_get_field_status_rsp) \ -M(NPC_CN20K_MCAM_GET_FREE_COUNT, 0x6015, npc_cn20k_get_fcnt, \ - msg_req, npc_cn20k_get_fcnt_rsp) \ -M(NPC_CN20K_GET_KEX_CFG, 0x6016, npc_cn20k_get_kex_cfg, \ +M(NPC_MCAM_DEFRAG, 0x6016, npc_defrag, \ + msg_req, \ + msg_rsp) \ +M(NPC_CN20K_GET_KEX_CFG, 0x6017, npc_cn20k_get_kex_cfg, \ msg_req, npc_cn20k_get_kex_cfg_rsp) \ -M(NPC_CN20K_MCAM_WRITE_ENTRY, 0x6017, npc_cn20k_mcam_write_entry, \ - npc_cn20k_mcam_write_entry_req, msg_rsp) \ -M(NPC_CN20K_MCAM_ALLOC_AND_WRITE_ENTRY, 0x6018, \ -npc_cn20k_mcam_alloc_and_write_entry, \ +M(NPC_CN20K_MCAM_GET_FREE_COUNT, 0x6018, npc_cn20k_get_fcnt, \ + msg_req, npc_cn20k_get_fcnt_rsp) \ +M(NPC_CN20K_MCAM_WRITE_ENTRY, 0x6019, npc_cn20k_mcam_write_entry, \ + npc_cn20k_mcam_write_entry_req, msg_rsp) \ +M(NPC_CN20K_MCAM_ALLOC_AND_WRITE_ENTRY, 0x601a, npc_cn20k_mcam_alloc_and_write_entry, \ npc_cn20k_mcam_alloc_and_write_entry_req, \ npc_mcam_alloc_and_write_entry_rsp) \ -M(NPC_CN20K_MCAM_READ_ENTRY, 0x6019, npc_cn20k_mcam_read_entry, \ +M(NPC_CN20K_MCAM_READ_ENTRY, 0x601b, npc_cn20k_mcam_read_entry, \ npc_mcam_read_entry_req, \ npc_cn20k_mcam_read_entry_rsp) \ -M(NPC_CN20K_MCAM_READ_BASE_RULE, 0x601a, npc_cn20k_read_base_steer_rule, \ - msg_req, npc_cn20k_mcam_read_base_rule_rsp) \ -M(NPC_MCAM_DEFRAG, 0x601b, npc_defrag, \ - msg_req, \ - msg_rsp) \ -M(NPC_MCAM_GET_NUM_KWS, 0x601c, npc_get_num_kws, \ +M(NPC_CN20K_MCAM_READ_BASE_RULE, 0x601c, npc_cn20k_read_base_steer_rule, \ + msg_req, npc_cn20k_mcam_read_base_rule_rsp) \ +M(NPC_MCAM_GET_NUM_KWS, 0x601d, npc_get_num_kws, \ npc_get_num_kws_req, \ npc_get_num_kws_rsp) \ -M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601d, npc_get_dft_rl_idxs, \ +M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs, \ msg_req, \ npc_get_dft_rl_idxs_rsp)\ -M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601e, npc_get_pfl_info, \ +M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \ msg_req, \ npc_get_pfl_info_rsp) \ /* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \ @@ -1009,6 +1008,7 @@ struct nix_lf_free_req { struct mbox_msghdr hdr; #define NIX_LF_DISABLE_FLOWS BIT_ULL(0) #define NIX_LF_DONT_FREE_TX_VTAG BIT_ULL(1) +#define NIX_LF_DONT_FREE_DFT_IDXS BIT_ULL(2) u64 flags; }; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index cefc5d70f3e4..eaed172f1606 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -41,6 +41,7 @@ enum npc_kpu_la_ltype { NPC_LT_LA_CPT_HDR, NPC_LT_LA_CUSTOM_L2_24B_ETHER, NPC_LT_LA_CUSTOM_PRE_L2_ETHER, + NPC_LT_LA_FP_ETHER, NPC_LT_LA_CUSTOM0 = 0xE, NPC_LT_LA_CUSTOM1 = 0xF, }; @@ -114,6 +115,7 @@ enum npc_kpu_le_ltype { NPC_LT_LE_TU_MPLS_IN_GRE, NPC_LT_LE_TU_NSH_IN_GRE, NPC_LT_LE_TU_MPLS_IN_UDP, + NPC_LT_LE_ROCEV2, NPC_LT_LE_CUSTOM0 = 0xE, NPC_LT_LE_CUSTOM1 = 0xF, }; @@ -265,6 +267,19 @@ struct npc_kpu_profile_cam { u16 dp2_mask; } __packed; +struct npc_kpu_profile_cam2 { + u8 state; + u8 state_mask; + u16 dp0; + u16 dp0_mask; + u16 dp1; + u16 dp1_mask; + u16 dp2; + u16 dp2_mask; + u8 ptype; + u8 ptype_mask; +} __packed; + struct npc_kpu_profile_action { u8 errlev; u8 errcode; @@ -290,6 +305,10 @@ struct npc_kpu_profile { int action_entries; struct npc_kpu_profile_cam *cam; struct npc_kpu_profile_action *action; + int cam_entries2; + int action_entries2; + struct npc_kpu_profile_action *action2; + struct npc_kpu_profile_cam2 *cam2; }; /* NPC KPU register formats */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h index db74f7fdf028..62902f0ad686 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h @@ -30,12 +30,14 @@ #define NPC_ETYPE_PTP 0x88f7 #define NPC_ETYPE_FCOE 0x8906 #define NPC_ETYPE_QINQ 0x9100 +#define NPC_ETYPE_QINQ2 0x9200 #define NPC_ETYPE_TRANS_ETH_BR 0x6558 #define NPC_ETYPE_PPP 0x880b #define NPC_ETYPE_NSH 0x894f #define NPC_ETYPE_DSA 0xdada #define NPC_ETYPE_PPPOE 0x8864 #define NPC_ETYPE_ERSPA 0x88be +#define NPC_ETYPE_FP 0x8903 #define NPC_PPP_IP 0x0021 #define NPC_PPP_IP6 0x0057 @@ -73,6 +75,7 @@ #define NPC_UDP_PORT_GENEVE 6081 #define NPC_UDP_PORT_MPLS 6635 #define NPC_UDP_PORT_ESP 4500 +#define NPC_UDP_PORT_ROCEV2 4791 #define NPC_VXLANGPE_NP_IP 0x1 #define NPC_VXLANGPE_NP_IP6 0x2 @@ -191,6 +194,8 @@ enum npc_kpu_parser_state { NPC_S_KPU2_EXDSA, NPC_S_KPU2_CPT_CTAG, NPC_S_KPU2_CPT_QINQ, + NPC_S_KPU2_CPT_CTAG2, + NPC_S_KPU2_CPT_SBTAG, NPC_S_KPU2_MT, NPC_S_KPU3_CTAG, NPC_S_KPU3_STAG, @@ -200,6 +205,9 @@ enum npc_kpu_parser_state { NPC_S_KPU3_QINQ_C, NPC_S_KPU3_DSA, NPC_S_KPU3_VLAN_EXDSA, + NPC_S_KPU3_CPT_QINQ, + NPC_S_KPU3_CPT_CTAG, + NPC_S_KPU3_CPT_STAG, NPC_S_KPU4_MPLS, NPC_S_KPU4_NSH, NPC_S_KPU4_FDSA, @@ -281,6 +289,14 @@ enum npc_kpu_parser_state { NPC_S_LAST /* has to be the last item */ }; +enum npc_kpu9_extra_parser_state { + NPC_S_KPU9_ROCEV2 = NPC_S_LAST + 6, +}; + +enum npc_kpu4_extra_parser_state { + NPC_S_KPU4_SBTAG_PTP = NPC_S_LAST + 7, +}; + enum npc_kpu_la_uflag { NPC_F_LA_U_HAS_TAG = 0x10, NPC_F_LA_U_HAS_IH_NIX = 0x20, @@ -504,7 +520,7 @@ enum NPC_ERRLEV_E { static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -513,7 +529,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -522,7 +538,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -531,7 +547,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -540,7 +556,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -549,7 +565,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -558,7 +574,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -567,7 +583,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -576,7 +592,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -585,7 +601,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -594,7 +610,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -603,7 +619,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -612,7 +628,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -621,7 +637,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -630,7 +646,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -639,7 +655,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -648,7 +664,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -657,7 +673,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -666,7 +682,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -675,7 +691,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -684,7 +700,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -693,7 +709,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -702,7 +718,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -711,7 +727,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -720,7 +736,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -729,7 +745,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -738,7 +754,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -747,7 +763,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -756,7 +772,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -765,7 +781,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -774,7 +790,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -783,7 +799,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -792,7 +808,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -801,7 +817,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -810,7 +826,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -819,7 +835,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -828,7 +844,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -837,7 +853,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -846,7 +862,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -855,7 +871,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -864,7 +880,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -873,7 +889,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -882,7 +898,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -891,7 +907,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -900,7 +916,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -909,7 +925,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -918,7 +934,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -927,7 +943,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -936,7 +952,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -945,7 +961,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -954,7 +970,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -963,7 +979,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -972,7 +988,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -981,7 +997,7 @@ static struct npc_kpu_profile_action ikpu_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 12, 16, 20, 0, 0, + 12, 16, 28, 0, 0, NPC_S_KPU1_ETHER, 0, 0, NPC_LID_LA, NPC_LT_NA, 0, @@ -1087,6 +1103,141 @@ static struct npc_kpu_profile_cam kpu1_cam_entries[] __maybe_unused = { NPC_KPU_NOP_CAM, NPC_KPU_NOP_CAM, NPC_KPU_NOP_CAM, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_IP, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_IP6, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_ARP, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_RARP, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_PTP, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_FCOE, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_CTAG, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_SBTAG, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_QINQ, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_ETAG, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_MPLSU, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_MPLSM, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_NSH, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_DSA, + 0xffff, + }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_FP, + 0xffff, + 0x0000, + 0x0000, + NPC_ETYPE_PPPOE, + 0xffff, + }, { NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_IP, @@ -1159,6 +1310,15 @@ static struct npc_kpu_profile_cam kpu1_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_PTP, + 0xffff, + 0x0000, + 0x0000, + }, { NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_SBTAG, @@ -1177,6 +1337,15 @@ static struct npc_kpu_profile_cam kpu1_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU1_ETHER, 0xff, + NPC_ETYPE_QINQ2, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU1_ETHER, 0xff, NPC_ETYPE_ETAG, @@ -1852,6 +2021,15 @@ static struct npc_kpu_profile_cam kpu1_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU1_CPT_HDR, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, { NPC_S_KPU1_CPT_HDR, 0xff, NPC_ETYPE_CTAG, @@ -1870,6 +2048,24 @@ static struct npc_kpu_profile_cam kpu1_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU1_CPT_HDR, 0xff, + NPC_ETYPE_QINQ2, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU1_CPT_HDR, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU1_VLAN_EXDSA, 0xff, NPC_ETYPE_CTAG, @@ -2005,6 +2201,33 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] __maybe_unused = { NPC_PPP_IP6, 0xffff, }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_QINQ, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CTAG, 0xff, + NPC_ETYPE_QINQ2, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU2_CTAG, 0xff, 0x0000, @@ -2230,6 +2453,24 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_SBTAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU2_SBTAG, 0xff, 0x0000, @@ -2347,6 +2588,24 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_QINQ, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU2_QINQ, 0xff, 0x0000, @@ -2437,6 +2696,15 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU2_ETAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, { NPC_S_KPU2_ETAG, 0xff, NPC_ETYPE_CTAG, @@ -2806,6 +3074,105 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU2_CPT_QINQ, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_QINQ, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_CTAG2, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_CTAG2, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_CTAG2, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_CTAG2, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_SBTAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_SBTAG, 0xff, + NPC_ETYPE_SBTAG, + 0xffff, + NPC_ETYPE_CTAG, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_SBTAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU2_CPT_SBTAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU2_MT, 0xff, NPC_ETYPE_CTAG, @@ -2824,15 +3191,6 @@ static struct npc_kpu_profile_cam kpu2_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, - { - NPC_S_NA, 0X00, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - }, }; static struct npc_kpu_profile_cam kpu3_cam_entries[] __maybe_unused = { @@ -3256,6 +3614,42 @@ static struct npc_kpu_profile_cam kpu3_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSU, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CTAG_C, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_MPLSM, + 0xffff, + 0x0000, + 0x0000, + }, { NPC_S_KPU3_CTAG_C, 0xff, NPC_ETYPE_IP, @@ -3806,9 +4200,99 @@ static struct npc_kpu_profile_cam kpu3_cam_entries[] __maybe_unused = { 0x0000, }, { - NPC_S_NA, 0X00, + NPC_S_KPU3_CPT_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, 0x0000, 0x0000, + }, + { + NPC_S_KPU3_CPT_QINQ, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_QINQ, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_QINQ, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_QINQ, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_CTAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_CTAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_STAG, 0xff, + NPC_ETYPE_CTAG, + 0xffff, + NPC_ETYPE_IP6, + 0xffff, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_STAG, 0xff, + NPC_ETYPE_IP, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU3_CPT_STAG, 0xff, + NPC_ETYPE_IP6, + 0xffff, 0x0000, 0x0000, 0x0000, @@ -4120,6 +4604,15 @@ static struct npc_kpu_profile_cam kpu4_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU4_SBTAG_PTP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_NA, 0X00, 0x0000, @@ -6397,6 +6890,15 @@ static struct npc_kpu_profile_cam kpu8_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU8_UDP, 0xff, + NPC_UDP_PORT_ROCEV2, + 0xffff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU8_UDP, 0xff, NPC_UDP_PORT_VXLAN, @@ -7252,6 +7754,15 @@ static struct npc_kpu_profile_cam kpu9_cam_entries[] __maybe_unused = { 0x0000, 0x0000, }, + { + NPC_S_KPU9_ROCEV2, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, { NPC_S_KPU9_VXLAN, 0xff, 0x0000, @@ -8080,6 +8591,24 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_KPU_NOP_CAM, NPC_KPU_NOP_CAM, NPC_KPU_NOP_CAM, + { + NPC_S_KPU12_TU_IP, 0xff, + 0x0000, + NPC_IP_TTL_MASK, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + }, + { + NPC_S_KPU12_TU_IP, 0xff, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0001, + NPC_IP_HDR_FRAGOFF, + }, { NPC_S_KPU12_TU_IP, 0xff, NPC_IPNH_TCP, @@ -8087,7 +8616,7 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, - 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8096,7 +8625,7 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, - 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8105,7 +8634,7 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, - 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8114,7 +8643,7 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, - 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8123,7 +8652,7 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, - 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8132,7 +8661,7 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, - 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8141,7 +8670,16 @@ static struct npc_kpu_profile_cam kpu12_cam_entries[] __maybe_unused = { NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, + }, + { + NPC_S_KPU12_TU_IP, 0xff, 0x0000, + 0x0000, + NPC_IP_VER_4 | NPC_IP_HDR_LEN_5, + NPC_IP_VER_MASK | NPC_IP_HDR_LEN_MASK, + 0x0000, + NPC_IP_HDR_MF | NPC_IP_HDR_FRAGOFF, }, { NPC_S_KPU12_TU_IP, 0xff, @@ -8647,6 +9185,126 @@ static struct npc_kpu_profile_action kpu1_action_entries[] __maybe_unused = { NPC_KPU_NOP_ACTION, NPC_KPU_NOP_ACTION, NPC_KPU_NOP_ACTION, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 3, 0, + NPC_S_KPU5_IP, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 3, 0, + NPC_S_KPU5_IP6, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_ARP, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_RARP, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_PTP, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 3, 0, + NPC_S_KPU5_FCOE, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 12, 0, 0, + NPC_S_KPU2_CTAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 0, 0, + NPC_S_KPU2_SBTAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_QINQ, 28, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 26, 0, 0, + NPC_S_KPU2_ETAG, 28, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_ETAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 2, 0, + NPC_S_KPU4_MPLS, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_L_WITH_MPLS, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 0, 0, 2, 0, + NPC_S_KPU4_NSH, 30, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + NPC_F_LA_L_WITH_NSH, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 1, 0, + NPC_S_KPU3_DSA, 28, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 2, 0, + NPC_S_KPU4_PPPOE, 28, 1, + NPC_LID_LA, NPC_LT_LA_FP_ETHER, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 6, 3, 0, @@ -8711,6 +9369,14 @@ static struct npc_kpu_profile_action kpu1_action_entries[] __maybe_unused = { NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 2, 0, + NPC_S_KPU4_SBTAG_PTP, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 4, 8, 22, 0, 0, @@ -8727,6 +9393,14 @@ static struct npc_kpu_profile_action kpu1_action_entries[] __maybe_unused = { NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_QINQ, 12, 1, + NPC_LID_LA, NPC_LT_LA_ETHER, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 12, 26, 0, 0, @@ -9346,6 +10020,14 @@ static struct npc_kpu_profile_action kpu1_action_entries[] __maybe_unused = { 0, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 12, 0, 0, 0, + NPC_S_KPU2_CPT_CTAG2, 12, 1, + NPC_LID_LA, NPC_LT_LA_CPT_HDR, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 4, 8, 0, 0, 0, @@ -9362,6 +10044,22 @@ static struct npc_kpu_profile_action kpu1_action_entries[] __maybe_unused = { NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 0, 0, 0, + NPC_S_KPU2_CPT_QINQ, 12, 1, + NPC_LID_LA, NPC_LT_LA_CPT_HDR, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 4, 8, 22, 0, 0, + NPC_S_KPU2_CPT_SBTAG, 12, 1, + NPC_LID_LA, NPC_LT_LA_CPT_HDR, + NPC_F_LA_U_HAS_TAG | NPC_F_LA_L_WITH_VLAN, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 0, 0, 1, 0, @@ -9483,6 +10181,30 @@ static struct npc_kpu_profile_action kpu2_action_entries[] __maybe_unused = { 0, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_STAG, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_STAG_STAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_STAG, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_STAG_STAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_STAG, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_STAG_STAG, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, @@ -9683,6 +10405,22 @@ static struct npc_kpu_profile_action kpu2_action_entries[] __maybe_unused = { NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_ITAG_UNK, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 2, 0, + NPC_S_KPU5_IP6, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, @@ -9787,6 +10525,22 @@ static struct npc_kpu_profile_action kpu2_action_entries[] __maybe_unused = { NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_QINQ_QINQ, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_IP, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 2, 0, + NPC_S_KPU5_IP6, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, @@ -9869,8 +10623,16 @@ static struct npc_kpu_profile_action kpu2_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 2, 0, 0, 0, 0, - NPC_S_KPU3_CTAG, 10, 1, + 0, 4, 8, 0, 0, + NPC_S_KPU3_CTAG_C, 16, 1, + NPC_LID_LB, NPC_LT_LB_ETAG, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 4, 8, 0, 0, + NPC_S_KPU3_CTAG_C, 12, 1, NPC_LID_LB, NPC_LT_LB_ETAG, NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, 0, 0, 0, 0, @@ -10195,6 +10957,94 @@ static struct npc_kpu_profile_action kpu2_action_entries[] __maybe_unused = { NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_CPT_IP, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 2, 0, + NPC_S_KPU5_CPT_IP6, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_CPT_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 2, 0, + NPC_S_KPU5_CPT_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_CPT_QINQ, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_QINQ_QINQ, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 1, + NPC_S_KPU3_CPT_QINQ, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_QINQ_QINQ, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_CPT_IP, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 2, 0, + NPC_S_KPU5_CPT_IP6, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 0, 0, 0, + NPC_S_KPU3_CPT_CTAG, 10, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_STAG_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 2, 0, + NPC_S_KPU5_CPT_IP, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 2, 0, + NPC_S_KPU5_CPT_IP6, 6, 1, + NPC_LID_LB, NPC_LT_LB_STAG_QINQ, + NPC_F_LB_U_MORE_TAG | NPC_F_LB_L_WITH_CTAG, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 4, 0, 0, 0, 0, @@ -10211,14 +11061,6 @@ static struct npc_kpu_profile_action kpu2_action_entries[] __maybe_unused = { 0, 0, 0, 0, 0, }, - { - NPC_ERRLEV_LB, NPC_EC_L2_K3, - 0, 0, 0, 0, 1, - NPC_S_NA, 0, 0, - NPC_LID_LB, NPC_LT_NA, - 0, - 0, 0, 0, 0, - }, }; static struct npc_kpu_profile_action kpu3_action_entries[] __maybe_unused = { @@ -10589,13 +11431,45 @@ static struct npc_kpu_profile_action kpu3_action_entries[] __maybe_unused = { 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3_ETYPE_UNK, + NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 0, 0, 1, NPC_S_NA, 0, 0, NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_IP, 6, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 0, + NPC_S_KPU5_IP6, 6, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 6, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 2, 6, 10, 0, 0, + NPC_S_KPU4_MPLS, 6, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 8, 0, 6, 1, 0, @@ -11085,9 +11959,89 @@ static struct npc_kpu_profile_action kpu3_action_entries[] __maybe_unused = { 0, 0, 0, 0, }, { - NPC_ERRLEV_LB, NPC_EC_L2_K3, - 0, 0, 0, 0, 1, - NPC_S_NA, 0, 0, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_CPT_IP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 0, + NPC_S_KPU5_CPT_IP6, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_CPT_IP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 0, + NPC_S_KPU5_CPT_IP6, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 1, + NPC_S_KPU5_CPT_IP6, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_CPT_IP, 6, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 0, + NPC_S_KPU5_CPT_IP6, 6, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_CPT_IP, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 0, + NPC_S_KPU5_CPT_IP6, 8, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 1, 0, + NPC_S_KPU5_CPT_IP, 4, 0, + NPC_LID_LB, NPC_LT_NA, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 6, 0, 42, 1, 0, + NPC_S_KPU5_CPT_IP6, 4, 0, NPC_LID_LB, NPC_LT_NA, 0, 0, 0, 0, 0, @@ -11365,6 +12319,14 @@ static struct npc_kpu_profile_action kpu4_action_entries[] __maybe_unused = { 0, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 8, 0, 6, 0, 0, + NPC_S_KPU5_PTP, 4, 1, + NPC_LID_LB, NPC_LT_LB_CTAG, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_LB, NPC_EC_L2_K4, 0, 0, 0, 0, 1, @@ -11696,7 +12658,7 @@ static struct npc_kpu_profile_action kpu5_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 2, 0, + 2, 0, 0, 2, 0, NPC_S_KPU8_GRE, 40, 1, NPC_LID_LC, NPC_LT_LC_IP6, 0, @@ -12176,7 +13138,7 @@ static struct npc_kpu_profile_action kpu5_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 2, 0, + 2, 0, 0, 2, 0, NPC_S_KPU8_GRE, 40, 1, NPC_LID_LC, NPC_LT_LC_IP6, 0, @@ -12481,7 +13443,7 @@ static struct npc_kpu_profile_action kpu6_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 1, 0, + 2, 0, 0, 1, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -12593,7 +13555,7 @@ static struct npc_kpu_profile_action kpu6_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 1, 0, + 2, 0, 0, 1, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -12697,7 +13659,7 @@ static struct npc_kpu_profile_action kpu6_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 1, 0, + 2, 0, 0, 1, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -12793,7 +13755,7 @@ static struct npc_kpu_profile_action kpu6_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 1, 0, + 2, 0, 0, 1, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -12905,7 +13867,7 @@ static struct npc_kpu_profile_action kpu6_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 1, 0, + 2, 0, 0, 1, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -13034,7 +13996,7 @@ static struct npc_kpu_profile_action kpu7_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -13226,7 +14188,7 @@ static struct npc_kpu_profile_action kpu7_action_entries[] __maybe_unused = { }, { NPC_ERRLEV_RE, NPC_EC_NOERR, - 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, NPC_S_KPU8_GRE, 8, 0, NPC_LID_LC, NPC_LT_NA, 0, @@ -13393,6 +14355,14 @@ static struct npc_kpu_profile_action kpu8_action_entries[] __maybe_unused = { NPC_F_LD_L_TCP_UNK_PORT_HAS_OPTIONS, 12, 0xf0, 1, 2, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 2, 0, 0, + NPC_S_KPU9_ROCEV2, 8, 1, + NPC_LID_LD, NPC_LT_LD_UDP, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 0, 0, 2, 0, 0, @@ -13538,10 +14508,10 @@ static struct npc_kpu_profile_action kpu8_action_entries[] __maybe_unused = { 0, 0, 0, 0, }, { - NPC_ERRLEV_LD, NPC_EC_NVGRE, - 0, 0, 0, 0, 1, - NPC_S_NA, 0, 0, - NPC_LID_LD, NPC_LT_NA, + NPC_ERRLEV_RE, NPC_EC_NOERR, + 12, 16, 20, 2, 0, + NPC_S_KPU11_TU_ETHER, 4, 1, + NPC_LID_LD, NPC_LT_LD_GRE, 0, 0, 0, 0, 0, }, @@ -14154,6 +15124,14 @@ static struct npc_kpu_profile_action kpu9_action_entries[] __maybe_unused = { 0, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 8, 1, + NPC_LID_LE, NPC_LT_LE_ROCEV2, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 12, 16, 20, 1, 0, @@ -14893,6 +15871,22 @@ static struct npc_kpu_profile_action kpu12_action_entries[] __maybe_unused = { NPC_KPU_NOP_ACTION, NPC_KPU_NOP_ACTION, NPC_KPU_NOP_ACTION, + { + NPC_ERRLEV_LC, NPC_EC_IP_TTL_0, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, + }, + { + NPC_ERRLEV_LC, NPC_EC_IP_FRAG_OFFSET_1, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, 2, 0, @@ -14957,6 +15951,14 @@ static struct npc_kpu_profile_action kpu12_action_entries[] __maybe_unused = { NPC_F_LG_U_UNK_IP_PROTO, 0, 0, 0, 0, }, + { + NPC_ERRLEV_RE, NPC_EC_NOERR, + 0, 0, 0, 0, 1, + NPC_S_NA, 0, 1, + NPC_LID_LG, NPC_LT_LG_TU_IP, + 0, + 0, 0, 0, 0, + }, { NPC_ERRLEV_RE, NPC_EC_NOERR, 2, 12, 0, 2, 0, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 6e907ee19164..ffba56ee8a60 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -3542,19 +3542,29 @@ static void rvu_update_module_params(struct rvu *rvu) kpu_profile ? kpu_profile : default_pfl_name, KPU_NAME_LEN); } +static atomic_t device_bound = ATOMIC_INIT(0); + static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *dev = &pdev->dev; struct rvu *rvu; int err; + if (atomic_cmpxchg(&device_bound, 0, 1) != 0) { + dev_warn(dev, "Only one af device is supported.\n"); + return -EBUSY; + } + rvu = devm_kzalloc(dev, sizeof(*rvu), GFP_KERNEL); - if (!rvu) + if (!rvu) { + atomic_set(&device_bound, 0); return -ENOMEM; + } rvu->hw = devm_kzalloc(dev, sizeof(struct rvu_hwinfo), GFP_KERNEL); if (!rvu->hw) { devm_kfree(dev, rvu); + atomic_set(&device_bound, 0); return -ENOMEM; } @@ -3687,6 +3697,7 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, NULL); devm_kfree(&pdev->dev, rvu->hw); devm_kfree(dev, rvu); + atomic_set(&device_bound, 0); return err; } @@ -3716,6 +3727,7 @@ static void rvu_remove(struct pci_dev *pdev) cn20k_free_mbox_memory(rvu); kfree(rvu->ng_rvu); devm_kfree(&pdev->dev, rvu); + atomic_set(&device_bound, 0); } static void rvu_shutdown(struct pci_dev *pdev) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 65397daae4c2..7f3505ae6860 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -553,17 +553,19 @@ struct npc_kpu_profile_adapter { const char *name; u64 version; const struct npc_lt_def_cfg *lt_def; - const struct npc_kpu_profile_action *ikpu; /* array[pkinds] */ - const struct npc_kpu_profile *kpu; /* array[kpus] */ + struct npc_kpu_profile_action *ikpu; /* array[pkinds] */ + struct npc_kpu_profile_action *ikpu2; /* array[pkinds] */ + struct npc_kpu_profile *kpu; /* array[kpus] */ union npc_mcam_key_prfl { - struct npc_mcam_kex *mkex; + const struct npc_mcam_kex *mkex; /* used for cn9k and cn10k */ - struct npc_mcam_kex_extr *mkex_extr; /* used for cn20k */ + const struct npc_mcam_kex_extr *mkex_extr; /* used for cn20k */ } mcam_kex_prfl; struct npc_mcam_kex_hash *mkex_hash; bool custom; size_t pkinds; size_t kpus; + bool from_fs; }; #define RVU_SWITCH_LBK_CHAN 63 @@ -634,7 +636,7 @@ struct rvu { /* Firmware data */ struct rvu_fwdata *fwdata; - void *kpu_fwdata; + const void *kpu_fwdata; size_t kpu_fwdata_sz; void __iomem *kpu_prfl_addr; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index 6494a9ee2f0d..aa3ecab5ebd8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -1180,12 +1180,12 @@ static void rvu_health_reporters_destroy(struct rvu *rvu) /* Devlink Params APIs */ static int rvu_af_dl_dwrr_mtu_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; - int dwrr_mtu = val.vu32; + int dwrr_mtu = val->vu32; struct nix_txsch *txsch; struct nix_hw *nix_hw; @@ -1258,6 +1258,7 @@ enum rvu_af_dl_param_id { RVU_AF_DEVLINK_PARAM_ID_NPC_EXACT_FEATURE_DISABLE, RVU_AF_DEVLINK_PARAM_ID_NPC_DEF_RULE_CNTR_ENABLE, RVU_AF_DEVLINK_PARAM_ID_NPC_DEFRAG, + RVU_AF_DEVLINK_PARAM_ID_NPC_SRCH_ORDER, RVU_AF_DEVLINK_PARAM_ID_NIX_MAXLF, }; @@ -1295,14 +1296,14 @@ static int rvu_af_npc_defrag(struct devlink *devlink, u32 id, } static int rvu_af_npc_defrag_feature_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; u64 enable; - if (kstrtoull(val.vstr, 10, &enable)) { + if (kstrtoull(val->vstr, 10, &enable)) { NL_SET_ERR_MSG_MOD(extack, "Only 1 value is supported"); return -EINVAL; @@ -1351,14 +1352,14 @@ static int rvu_af_npc_exact_feature_disable(struct devlink *devlink, u32 id, } static int rvu_af_npc_exact_feature_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); struct rvu *rvu = rvu_dl->rvu; u64 enable; - if (kstrtoull(val.vstr, 10, &enable)) { + if (kstrtoull(val->vstr, 10, &enable)) { NL_SET_ERR_MSG_MOD(extack, "Only 1 value is supported"); return -EINVAL; @@ -1414,7 +1415,7 @@ static int rvu_af_dl_npc_mcam_high_zone_percent_set(struct devlink *devlink, u32 } static int rvu_af_dl_npc_mcam_high_zone_percent_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); @@ -1422,7 +1423,7 @@ static int rvu_af_dl_npc_mcam_high_zone_percent_validate(struct devlink *devlink struct npc_mcam *mcam; /* The percent of high prio zone must range from 12% to 100% of unreserved mcam space */ - if (val.vu8 < 12 || val.vu8 > 100) { + if (val->vu8 < 12 || val->vu8 > 100) { NL_SET_ERR_MSG_MOD(extack, "mcam high zone percent must be between 12% to 100%"); return -EINVAL; @@ -1504,7 +1505,7 @@ static int rvu_af_dl_nix_maxlf_set(struct devlink *devlink, u32 id, } static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct rvu_devlink *rvu_dl = devlink_priv(devlink); @@ -1528,13 +1529,13 @@ static int rvu_af_dl_nix_maxlf_validate(struct devlink *devlink, u32 id, return -EPERM; } - if (max_nix0_lf && val.vu16 > max_nix0_lf) { + if (max_nix0_lf && val->vu16 > max_nix0_lf) { NL_SET_ERR_MSG_MOD(extack, "requested nixlf is greater than the max supported nix0_lf"); return -EPERM; } - if (max_nix1_lf && val.vu16 > max_nix1_lf) { + if (max_nix1_lf && val->vu16 > max_nix1_lf) { NL_SET_ERR_MSG_MOD(extack, "requested nixlf is greater than the max supported nix1_lf"); return -EINVAL; @@ -1619,12 +1620,83 @@ static int rvu_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, return 0; } +static int rvu_af_dl_npc_srch_order_set(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx, + struct netlink_ext_ack *extack) +{ + struct rvu_devlink *rvu_dl = devlink_priv(devlink); + struct rvu *rvu = rvu_dl->rvu; + + return npc_cn20k_search_order_set(rvu, + ctx->val.u64arr.val, + ctx->val.u64arr.size); +} + +static int rvu_af_dl_npc_srch_order_get(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx, + struct netlink_ext_ack *extack) +{ + bool restricted_order; + const u32 *order; + u32 sz; + + order = npc_cn20k_search_order_get(&restricted_order, &sz); + ctx->val.u64arr.size = sz; + for (int i = 0; i < sz; i++) + ctx->val.u64arr.val[i] = order[i]; + + return 0; +} + +static int rvu_af_dl_npc_srch_order_validate(struct devlink *devlink, u32 id, + union devlink_param_value *val, + struct netlink_ext_ack *extack) +{ + struct rvu_devlink *rvu_dl = devlink_priv(devlink); + struct rvu *rvu = rvu_dl->rvu; + bool restricted_order; + unsigned long w = 0; + u64 *arr; + u32 sz; + + npc_cn20k_search_order_get(&restricted_order, &sz); + if (sz != val->u64arr.size) { + dev_err(rvu->dev, + "Wrong size %llu, should be %u\n", + val->u64arr.size, sz); + return -EINVAL; + } + + arr = val->u64arr.val; + for (int i = 0; i < sz; i++) { + if (arr[i] >= sz) + return -EINVAL; + + w |= BIT_ULL(arr[i]); + } + + if (bitmap_weight(&w, sz) != sz) { + dev_err(rvu->dev, + "Duplicate or out-of-range subbank index. %lu\n", + find_first_zero_bit(&w, sz)); + return -EINVAL; + } + + return 0; +} + static const struct devlink_ops rvu_devlink_ops = { .eswitch_mode_get = rvu_devlink_eswitch_mode_get, .eswitch_mode_set = rvu_devlink_eswitch_mode_set, }; -static const struct devlink_param rvu_af_dl_param_defrag[] = { +static const struct devlink_param rvu_af_dl_cn20k_params[] = { + DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_NPC_SRCH_ORDER, + "npc_srch_order", DEVLINK_PARAM_TYPE_U64_ARRAY, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + rvu_af_dl_npc_srch_order_get, + rvu_af_dl_npc_srch_order_set, + rvu_af_dl_npc_srch_order_validate), DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_NPC_DEFRAG, "npc_defrag", DEVLINK_PARAM_TYPE_STRING, BIT(DEVLINK_PARAM_CMODE_RUNTIME), @@ -1666,13 +1738,13 @@ int rvu_register_dl(struct rvu *rvu) } if (is_cn20k(rvu->pdev)) { - err = devlink_params_register(dl, rvu_af_dl_param_defrag, - ARRAY_SIZE(rvu_af_dl_param_defrag)); + err = devlink_params_register(dl, rvu_af_dl_cn20k_params, + ARRAY_SIZE(rvu_af_dl_cn20k_params)); if (err) { dev_err(rvu->dev, - "devlink defrag params register failed with error %d", + "devlink cn20k params register failed with error %d", err); - goto err_dl_defrag; + goto err_dl_cn20k_params; } } @@ -1695,10 +1767,10 @@ int rvu_register_dl(struct rvu *rvu) err_dl_exact_match: if (is_cn20k(rvu->pdev)) - devlink_params_unregister(dl, rvu_af_dl_param_defrag, - ARRAY_SIZE(rvu_af_dl_param_defrag)); + devlink_params_unregister(dl, rvu_af_dl_cn20k_params, + ARRAY_SIZE(rvu_af_dl_cn20k_params)); -err_dl_defrag: +err_dl_cn20k_params: devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params)); err_dl_health: @@ -1717,8 +1789,8 @@ void rvu_unregister_dl(struct rvu *rvu) devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params)); if (is_cn20k(rvu->pdev)) - devlink_params_unregister(dl, rvu_af_dl_param_defrag, - ARRAY_SIZE(rvu_af_dl_param_defrag)); + devlink_params_unregister(dl, rvu_af_dl_cn20k_params, + ARRAY_SIZE(rvu_af_dl_cn20k_params)); /* Unregister exact match devlink only for CN10K-B */ if (rvu_npc_exact_has_match_table(rvu)) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index f977734ae712..d8989395e875 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -16,6 +16,7 @@ #include "cgx.h" #include "lmac_common.h" #include "rvu_npc_hash.h" +#include "cn20k/npc.h" static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc); static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, @@ -1499,9 +1500,11 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, struct nix_lf_alloc_req *req, struct nix_lf_alloc_rsp *rsp) { - int nixlf, qints, hwctx_size, intf, err, rc = 0; + int nixlf, qints, hwctx_size, intf, rc = 0; + u16 bcast, mcast, promisc, ucast; struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; + bool rules_created = false; struct rvu_block *block; struct rvu_pfvf *pfvf; u64 cfg, ctx_cfg; @@ -1555,8 +1558,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, return NIX_AF_ERR_RSS_GRPS_INVALID; /* Reset this NIX LF */ - err = rvu_lf_reset(rvu, block, nixlf); - if (err) { + rc = rvu_lf_reset(rvu, block, nixlf); + if (rc) { dev_err(rvu->dev, "Failed to reset NIX%d LF%d\n", block->addr - BLKADDR_NIX0, nixlf); return NIX_AF_ERR_LF_RESET; @@ -1566,13 +1569,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, /* Alloc NIX RQ HW context memory and config the base */ hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF); - err = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size); - if (err) + rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size); + if (rc) goto free_mem; pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL); - if (!pfvf->rq_bmap) + if (!pfvf->rq_bmap) { + rc = -ENOMEM; goto free_mem; + } rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_BASE(nixlf), (u64)pfvf->rq_ctx->iova); @@ -1583,13 +1588,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, /* Alloc NIX SQ HW context memory and config the base */ hwctx_size = 1UL << (ctx_cfg & 0xF); - err = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size); - if (err) + rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size); + if (rc) goto free_mem; pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL); - if (!pfvf->sq_bmap) + if (!pfvf->sq_bmap) { + rc = -ENOMEM; goto free_mem; + } rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf), (u64)pfvf->sq_ctx->iova); @@ -1599,13 +1606,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, /* Alloc NIX CQ HW context memory and config the base */ hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF); - err = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size); - if (err) + rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size); + if (rc) goto free_mem; pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL); - if (!pfvf->cq_bmap) + if (!pfvf->cq_bmap) { + rc = -ENOMEM; goto free_mem; + } rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf), (u64)pfvf->cq_ctx->iova); @@ -1615,18 +1624,18 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, /* Initialize receive side scaling (RSS) */ hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF); - err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz, - req->rss_grps, hwctx_size, req->way_mask, - !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER)); - if (err) + rc = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz, + req->rss_grps, hwctx_size, req->way_mask, + !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER)); + if (rc) goto free_mem; /* Alloc memory for CQINT's HW contexts */ cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); qints = (cfg >> 24) & 0xFFF; hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF); - err = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size); - if (err) + rc = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size); + if (rc) goto free_mem; rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf), @@ -1639,8 +1648,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); qints = (cfg >> 12) & 0xFFF; hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF); - err = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size); - if (err) + rc = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size); + if (rc) goto free_mem; rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf), @@ -1684,10 +1693,22 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, if (is_sdp_pfvf(rvu, pcifunc)) intf = NIX_INTF_TYPE_SDP; - err = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp, - !!(req->flags & NIX_LF_LBK_BLK_SEL)); - if (err) - goto free_mem; + if (is_cn20k(rvu->pdev)) { + rc = npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &bcast, &mcast, + &promisc, &ucast); + if (rc) { + rc = npc_cn20k_dft_rules_alloc(rvu, pcifunc); + if (rc) + goto free_mem; + + rules_created = true; + } + } + + rc = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp, + !!(req->flags & NIX_LF_LBK_BLK_SEL)); + if (rc) + goto free_dft; /* Disable NPC entries as NIXLF's contexts are not initialized yet */ rvu_npc_disable_default_entries(rvu, pcifunc, nixlf); @@ -1699,9 +1720,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, goto exit; +free_dft: + if (is_cn20k(rvu->pdev) && rules_created) + npc_cn20k_dft_rules_free(rvu, pcifunc); + free_mem: nix_ctx_free(rvu, pfvf); - rc = -ENOMEM; exit: /* Set macaddr of this PF/VF */ @@ -1775,6 +1799,9 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req, nix_ctx_free(rvu, pfvf); + if (is_cn20k(rvu->pdev) && !(req->flags & NIX_LF_DONT_FREE_DFT_IDXS)) + npc_cn20k_dft_rules_free(rvu, pcifunc); + return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index d301a3f0f87a..c7bc0b3a29b9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -1285,11 +1285,18 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc, struct nix_mce_list *mce_list; int index, blkaddr, mce_idx; struct rvu_pfvf *pfvf; + u16 ptr[4]; /* multicast pkt replication is not enabled for AF's VFs & SDP links */ if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc)) return; + /* In cn20k, only CGX mapped devices have default MCAST entry */ + if (is_cn20k(rvu->pdev) && + npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1], + &ptr[2], &ptr[3])) + return; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return; @@ -1329,9 +1336,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); struct npc_mcam *mcam = &rvu->hw->mcam; int index, blkaddr; + u16 ptr[4]; /* only CGX or LBK interfaces have default entries */ - if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc)) + if (is_cn20k(rvu->pdev) && + npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1], + &ptr[2], &ptr[3])) return; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -1485,7 +1495,8 @@ void rvu_npc_free_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf) } static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr, - struct npc_mcam_kex *mkex, u8 intf) + const struct npc_mcam_kex *mkex, + u8 intf) { int lid, lt, ld, fl; @@ -1514,7 +1525,8 @@ static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr, } static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr, - struct npc_mcam_kex *mkex, u8 intf) + const struct npc_mcam_kex *mkex, + u8 intf) { int lid, lt, ld, fl; @@ -1543,7 +1555,7 @@ static void npc_program_mkex_tx(struct rvu *rvu, int blkaddr, } static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr, - struct npc_mcam_kex *mkex) + const struct npc_mcam_kex *mkex) { struct rvu_hwinfo *hw = rvu->hw; u8 intf; @@ -1683,8 +1695,12 @@ static void npc_config_kpucam(struct rvu *rvu, int blkaddr, const struct npc_kpu_profile_cam *kpucam, int kpu, int entry) { + const struct npc_kpu_profile_cam2 *kpucam2 = (void *)kpucam; + struct npc_kpu_profile_adapter *profile = &rvu->kpu; struct npc_kpu_cam cam0 = {0}; struct npc_kpu_cam cam1 = {0}; + u64 *val = (u64 *)&cam1; + u64 *mask = (u64 *)&cam0; cam1.state = kpucam->state & kpucam->state_mask; cam1.dp0_data = kpucam->dp0 & kpucam->dp0_mask; @@ -1696,6 +1712,14 @@ static void npc_config_kpucam(struct rvu *rvu, int blkaddr, cam0.dp1_data = ~kpucam->dp1 & kpucam->dp1_mask; cam0.dp2_data = ~kpucam->dp2 & kpucam->dp2_mask; + if (profile->from_fs) { + u8 ptype = kpucam2->ptype; + u8 pmask = kpucam2->ptype_mask; + + *val |= FIELD_PREP(GENMASK_ULL(57, 56), ptype & pmask); + *mask |= FIELD_PREP(GENMASK_ULL(57, 56), ~ptype & pmask); + } + rvu_write64(rvu, blkaddr, NPC_AF_KPUX_ENTRYX_CAMX(kpu, entry, 0), *(u64 *)&cam0); rvu_write64(rvu, blkaddr, @@ -1707,34 +1731,104 @@ u64 npc_enable_mask(int count) return (((count) < 64) ? ~(BIT_ULL(count) - 1) : (0x00ULL)); } +struct npc_kpu_profile_action * +npc_get_ikpu_nth_entry(struct rvu *rvu, int n) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + + if (profile->from_fs) + return &profile->ikpu2[n]; + + return &profile->ikpu[n]; +} + +int +npc_get_num_kpu_cam_entries(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + + if (profile->from_fs) + return kpu_pfl->cam_entries2; + + return kpu_pfl->cam_entries; +} + +struct npc_kpu_profile_cam * +npc_get_kpu_cam_nth_entry(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl, int n) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + + if (profile->from_fs) + return (void *)&kpu_pfl->cam2[n]; + + return (void *)&kpu_pfl->cam[n]; +} + +int +npc_get_num_kpu_action_entries(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + + if (profile->from_fs) + return kpu_pfl->action_entries2; + + return kpu_pfl->action_entries; +} + +struct npc_kpu_profile_action * +npc_get_kpu_action_nth_entry(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl, + int n) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + + if (profile->from_fs) + return (void *)&kpu_pfl->action2[n]; + + return (void *)&kpu_pfl->action[n]; +} + static void npc_program_kpu_profile(struct rvu *rvu, int blkaddr, int kpu, const struct npc_kpu_profile *profile) { + int num_cam_entries, num_action_entries; int entry, num_entries, max_entries; u64 entry_mask; - if (profile->cam_entries != profile->action_entries) { + num_cam_entries = npc_get_num_kpu_cam_entries(rvu, profile); + num_action_entries = npc_get_num_kpu_action_entries(rvu, profile); + + if (num_cam_entries != num_action_entries) { dev_err(rvu->dev, "KPU%d: CAM and action entries [%d != %d] not equal\n", - kpu, profile->cam_entries, profile->action_entries); + kpu, num_cam_entries, num_action_entries); } max_entries = rvu->hw->npc_kpu_entries; + WARN(num_cam_entries > max_entries, + "KPU%u: err: hw max entries=%u, input entries=%u\n", + kpu, rvu->hw->npc_kpu_entries, num_cam_entries); + /* Program CAM match entries for previous KPU extracted data */ - num_entries = min_t(int, profile->cam_entries, max_entries); + num_entries = min_t(int, num_cam_entries, max_entries); for (entry = 0; entry < num_entries; entry++) npc_config_kpucam(rvu, blkaddr, - &profile->cam[entry], kpu, entry); + (void *)npc_get_kpu_cam_nth_entry(rvu, profile, entry), + kpu, entry); /* Program this KPU's actions */ - num_entries = min_t(int, profile->action_entries, max_entries); + num_entries = min_t(int, num_action_entries, max_entries); for (entry = 0; entry < num_entries; entry++) - npc_config_kpuaction(rvu, blkaddr, &profile->action[entry], + npc_config_kpuaction(rvu, blkaddr, + (void *)npc_get_kpu_action_nth_entry(rvu, profile, entry), kpu, entry, false); /* Enable all programmed entries */ - num_entries = min_t(int, profile->action_entries, profile->cam_entries); + num_entries = min_t(int, num_action_entries, num_cam_entries); entry_mask = npc_enable_mask(num_entries); /* Disable first KPU_MAX_CST_ENT entries for built-in profile */ if (!rvu->kpu.custom) @@ -1778,26 +1872,175 @@ static void npc_prepare_default_kpu(struct rvu *rvu, npc_cn20k_update_action_entries_n_flags(rvu, profile); } -static int npc_apply_custom_kpu(struct rvu *rvu, - struct npc_kpu_profile_adapter *profile) +static int npc_alloc_kpu_cam2_n_action2(struct rvu *rvu, int kpu_num, + int num_entries) +{ + struct npc_kpu_profile_adapter *adapter = &rvu->kpu; + struct npc_kpu_profile *kpu; + + kpu = &adapter->kpu[kpu_num]; + + kpu->cam2 = devm_kcalloc(rvu->dev, num_entries, + sizeof(*kpu->cam2), GFP_KERNEL); + if (!kpu->cam2) + return -ENOMEM; + + kpu->action2 = devm_kcalloc(rvu->dev, num_entries, + sizeof(*kpu->action2), GFP_KERNEL); + if (!kpu->action2) + return -ENOMEM; + + return 0; +} + +static int npc_apply_custom_kpu_from_fw(struct rvu *rvu, + struct npc_kpu_profile_adapter *profile) { size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata), offset = 0; + const struct npc_kpu_profile_fwdata *fw; struct npc_kpu_profile_action *action; - struct npc_kpu_profile_fwdata *fw; struct npc_kpu_profile_cam *cam; struct npc_kpu_fwdata *fw_kpu; - int entries; - u16 kpu, entry; + int entries, entry, kpu; + + fw = rvu->kpu_fwdata; + + for (kpu = 0; kpu < fw->kpus; kpu++) { + if (rvu->kpu_fwdata_sz < hdr_sz + offset) { + dev_warn(rvu->dev, + "Profile size mismatch on KPU%i parsing\n", + kpu + 1); + return -EINVAL; + } + + fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset); + if (fw_kpu->entries < 0) { + dev_warn(rvu->dev, + "Profile entries is negative on KPU%i parsing\n", + kpu + 1); + return -EINVAL; + } + + if (fw_kpu->entries > KPU_MAX_CST_ENT) + dev_warn(rvu->dev, + "Too many custom entries on KPU%d: %d > %d\n", + kpu, fw_kpu->entries, KPU_MAX_CST_ENT); + entries = min_t(int, fw_kpu->entries, KPU_MAX_CST_ENT); + cam = (struct npc_kpu_profile_cam *)fw_kpu->data; + offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam); + action = (struct npc_kpu_profile_action *)(fw->data + offset); + offset += fw_kpu->entries * sizeof(*action); + if (rvu->kpu_fwdata_sz < hdr_sz + offset) { + dev_warn(rvu->dev, + "Profile size mismatch on KPU%i parsing.\n", + kpu + 1); + return -EINVAL; + } + for (entry = 0; entry < entries; entry++) { + profile->kpu[kpu].cam[entry] = cam[entry]; + profile->kpu[kpu].action[entry] = action[entry]; + } + } + + return 0; +} + +static int npc_apply_custom_kpu_from_fs(struct rvu *rvu, + struct npc_kpu_profile_adapter *profile) +{ + size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata), offset = 0; + const struct npc_kpu_profile_fwdata *fw; + struct npc_kpu_profile_action *action; + struct npc_kpu_profile_cam2 *cam2; + struct npc_kpu_fwdata *fw_kpu; + int entries, ret, entry, kpu; + + fw = rvu->kpu_fwdata; + + /* Binary blob contains ikpu actions entries at start of data[0] */ + profile->ikpu2 = devm_kcalloc(rvu->dev, 1, + sizeof(ikpu_action_entries), + GFP_KERNEL); + if (!profile->ikpu2) + return -ENOMEM; + + action = (struct npc_kpu_profile_action *)(fw->data + offset); + + if (rvu->kpu_fwdata_sz < hdr_sz + sizeof(ikpu_action_entries)) + return -EINVAL; + + /* The firmware layout does dependent on the internal size of + * ikpu_action_entries. + */ + memcpy((void *)profile->ikpu2, action, sizeof(ikpu_action_entries)); + offset += sizeof(ikpu_action_entries); + + for (kpu = 0; kpu < fw->kpus; kpu++) { + if (rvu->kpu_fwdata_sz < hdr_sz + offset + sizeof(*fw_kpu)) { + dev_warn(rvu->dev, + "profile size mismatch on kpu%i parsing\n", + kpu + 1); + return -EINVAL; + } + + fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset); + if (fw_kpu->entries <= 0) { + dev_warn(rvu->dev, + "Invalid kpu entries on KPU%d\n", kpu); + return -EINVAL; + } + + entries = min_t(int, fw_kpu->entries, rvu->hw->npc_kpu_entries); + dev_info(rvu->dev, + "Loading %u entries on KPU%d\n", entries, kpu); + + cam2 = (struct npc_kpu_profile_cam2 *)fw_kpu->data; + offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam2); + action = (struct npc_kpu_profile_action *)(fw->data + offset); + offset += fw_kpu->entries * sizeof(*action); + if (rvu->kpu_fwdata_sz < hdr_sz + offset) { + dev_warn(rvu->dev, + "profile size mismatch on kpu%i parsing.\n", + kpu + 1); + return -EINVAL; + } + + profile->kpu[kpu].cam_entries2 = entries; + profile->kpu[kpu].action_entries2 = entries; + ret = npc_alloc_kpu_cam2_n_action2(rvu, kpu, entries); + if (ret) { + dev_warn(rvu->dev, + "profile entry allocation failed for kpu=%d for %d entries\n", + kpu, entries); + return -EINVAL; + } + + for (entry = 0; entry < entries; entry++) { + profile->kpu[kpu].cam2[entry] = cam2[entry]; + profile->kpu[kpu].action2[entry] = action[entry]; + } + } + + return 0; +} + +static int npc_apply_custom_kpu(struct rvu *rvu, + struct npc_kpu_profile_adapter *profile, + bool from_fs, int *fw_kpus) +{ + size_t hdr_sz = sizeof(struct npc_kpu_profile_fwdata); + const struct npc_kpu_profile_fwdata *fw; + struct npc_kpu_profile_fwdata *sfw; if (is_cn20k(rvu->pdev)) return npc_cn20k_apply_custom_kpu(rvu, profile); - fw = rvu->kpu_fwdata; - if (rvu->kpu_fwdata_sz < hdr_sz) { dev_warn(rvu->dev, "Invalid KPU profile size\n"); return -EINVAL; } + + fw = rvu->kpu_fwdata; if (le64_to_cpu(fw->signature) != KPU_SIGN) { dev_warn(rvu->dev, "Invalid KPU profile signature %llx\n", fw->signature); @@ -1825,42 +2068,38 @@ static int npc_apply_custom_kpu(struct rvu *rvu, return -EINVAL; } /* Verify if profile fits the HW */ + if (fw->kpus > rvu->hw->npc_kpus) { + dev_warn(rvu->dev, "Not enough KPUs: %d > %d\n", fw->kpus, + rvu->hw->npc_kpus); + return -EINVAL; + } + + /* Check if there is enough memory for fw loading. + * Check if there is enough entries for profile->kpu[] to + * set cam_entries2 and action_entries2 + */ if (fw->kpus > profile->kpus) { - dev_warn(rvu->dev, "Not enough KPUs: %d > %ld\n", fw->kpus, + dev_warn(rvu->dev, "Not enough KPUs: %d > %zu\n", fw->kpus, profile->kpus); return -EINVAL; } + *fw_kpus = fw->kpus; + + sfw = devm_kcalloc(rvu->dev, 1, sizeof(*sfw), GFP_KERNEL); + if (!sfw) + return -ENOMEM; + + memcpy(sfw, fw, sizeof(*sfw)); + profile->custom = 1; - profile->name = fw->name; + profile->name = sfw->name; profile->version = le64_to_cpu(fw->version); - profile->mcam_kex_prfl.mkex = &fw->mkex; - profile->lt_def = &fw->lt_def; + profile->mcam_kex_prfl.mkex = &sfw->mkex; + profile->lt_def = &sfw->lt_def; - for (kpu = 0; kpu < fw->kpus; kpu++) { - fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset); - if (fw_kpu->entries > KPU_MAX_CST_ENT) - dev_warn(rvu->dev, - "Too many custom entries on KPU%d: %d > %d\n", - kpu, fw_kpu->entries, KPU_MAX_CST_ENT); - entries = min(fw_kpu->entries, KPU_MAX_CST_ENT); - cam = (struct npc_kpu_profile_cam *)fw_kpu->data; - offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam); - action = (struct npc_kpu_profile_action *)(fw->data + offset); - offset += fw_kpu->entries * sizeof(*action); - if (rvu->kpu_fwdata_sz < hdr_sz + offset) { - dev_warn(rvu->dev, - "Profile size mismatch on KPU%i parsing.\n", - kpu + 1); - return -EINVAL; - } - for (entry = 0; entry < entries; entry++) { - profile->kpu[kpu].cam[entry] = cam[entry]; - profile->kpu[kpu].action[entry] = action[entry]; - } - } - - return 0; + return from_fs ? npc_apply_custom_kpu_from_fs(rvu, profile) : + npc_apply_custom_kpu_from_fw(rvu, profile); } static int npc_load_kpu_prfl_img(struct rvu *rvu, void __iomem *prfl_addr, @@ -1948,45 +2187,19 @@ static int npc_load_kpu_profile_fwdb(struct rvu *rvu, const char *kpu_profile) return ret; } -void npc_load_kpu_profile(struct rvu *rvu) +static int npc_load_kpu_profile_from_fw(struct rvu *rvu) { struct npc_kpu_profile_adapter *profile = &rvu->kpu; const char *kpu_profile = rvu->kpu_pfl_name; - const struct firmware *fw = NULL; - bool retry_fwdb = false; + int fw_kpus = 0; - /* If user not specified profile customization */ - if (!strncmp(kpu_profile, def_pfl_name, KPU_NAME_LEN)) - goto revert_to_default; - /* First prepare default KPU, then we'll customize top entries. */ - npc_prepare_default_kpu(rvu, profile); - - /* Order of preceedence for load loading NPC profile (high to low) - * Firmware binary in filesystem. - * Firmware database method. - * Default KPU profile. - */ - if (!request_firmware_direct(&fw, kpu_profile, rvu->dev)) { - dev_info(rvu->dev, "Loading KPU profile from firmware: %s\n", - kpu_profile); - rvu->kpu_fwdata = kzalloc(fw->size, GFP_KERNEL); - if (rvu->kpu_fwdata) { - memcpy(rvu->kpu_fwdata, fw->data, fw->size); - rvu->kpu_fwdata_sz = fw->size; - } - release_firmware(fw); - retry_fwdb = true; - goto program_kpu; - } - -load_image_fwdb: /* Loading the KPU profile using firmware database */ if (npc_load_kpu_profile_fwdb(rvu, kpu_profile)) - goto revert_to_default; + return -EFAULT; -program_kpu: /* Apply profile customization if firmware was loaded. */ - if (!rvu->kpu_fwdata_sz || npc_apply_custom_kpu(rvu, profile)) { + if (!rvu->kpu_fwdata_sz || + npc_apply_custom_kpu(rvu, profile, false, &fw_kpus)) { /* If image from firmware filesystem fails to load or invalid * retry with firmware database method. */ @@ -2000,10 +2213,6 @@ void npc_load_kpu_profile(struct rvu *rvu) } rvu->kpu_fwdata = NULL; rvu->kpu_fwdata_sz = 0; - if (retry_fwdb) { - retry_fwdb = false; - goto load_image_fwdb; - } } dev_warn(rvu->dev, @@ -2011,22 +2220,101 @@ void npc_load_kpu_profile(struct rvu *rvu) kpu_profile); kfree(rvu->kpu_fwdata); rvu->kpu_fwdata = NULL; - goto revert_to_default; + return -EFAULT; } - dev_info(rvu->dev, "Using custom profile '%s', version %d.%d.%d\n", + dev_info(rvu->dev, "Using custom profile '%.32s', version %d.%d.%d\n", profile->name, NPC_KPU_VER_MAJ(profile->version), NPC_KPU_VER_MIN(profile->version), NPC_KPU_VER_PATCH(profile->version)); - return; + return 0; +} + +static int npc_load_kpu_profile_from_fs(struct rvu *rvu) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + const char *kpu_profile = rvu->kpu_pfl_name; + const struct firmware *fw = NULL; + int ret, fw_kpus = 0; + char path[512] = "kpu/"; + + if (strlen(kpu_profile) > sizeof(path) - strlen("kpu/") - 1) { + dev_err(rvu->dev, "kpu profile name is too big\n"); + return -ENOSPC; + } + + strcat(path, kpu_profile); + + if (request_firmware_direct(&fw, path, rvu->dev)) + return -ENOENT; + + dev_info(rvu->dev, "Loading KPU profile from filesystem: %s\n", + path); + + rvu->kpu_fwdata = fw->data; + rvu->kpu_fwdata_sz = fw->size; + + ret = npc_apply_custom_kpu(rvu, profile, true, &fw_kpus); + release_firmware(fw); + rvu->kpu_fwdata = NULL; + + if (ret) { + rvu->kpu_fwdata_sz = 0; + dev_err(rvu->dev, + "Loading KPU profile from filesystem failed\n"); + return ret; + } + + /* In firmware loading from filesystem method, all entries are from + * same binary blob. + */ + rvu->kpu.kpus = fw_kpus; + profile->kpus = fw_kpus; + profile->from_fs = true; + return 0; +} + +void npc_load_kpu_profile(struct rvu *rvu) +{ + struct npc_kpu_profile_adapter *profile = &rvu->kpu; + const char *kpu_profile = rvu->kpu_pfl_name; + + profile->from_fs = false; + + npc_prepare_default_kpu(rvu, profile); + + /* If user not specified profile customization */ + if (!strncmp(kpu_profile, def_pfl_name, KPU_NAME_LEN)) + return; + + /* Order of preceedence for load loading NPC profile (high to low) + * Firmware binary in filesystem. + * Firmware database method. + * Default KPU profile. + */ + + /* Filesystem-based KPU loading is not supported on cn20k. + * npc_prepare_default_kpu() was invoked earlier, but control + * reached this point because the default profile was not selected. + * No need to call it again. + */ + if (!is_cn20k(rvu->pdev)) { + if (!npc_load_kpu_profile_from_fs(rvu)) + return; + } + + /* First prepare default KPU, then we'll customize top entries. */ + npc_prepare_default_kpu(rvu, profile); + if (!npc_load_kpu_profile_from_fw(rvu)) + return; -revert_to_default: npc_prepare_default_kpu(rvu, profile); } static void npc_parser_profile_init(struct rvu *rvu, int blkaddr) { + struct npc_kpu_profile_adapter *profile = &rvu->kpu; struct rvu_hwinfo *hw = rvu->hw; int num_pkinds, num_kpus, idx; @@ -2050,7 +2338,9 @@ static void npc_parser_profile_init(struct rvu *rvu, int blkaddr) num_pkinds = min_t(int, hw->npc_pkinds, num_pkinds); for (idx = 0; idx < num_pkinds; idx++) - npc_config_kpuaction(rvu, blkaddr, &rvu->kpu.ikpu[idx], 0, idx, true); + npc_config_kpuaction(rvu, blkaddr, + npc_get_ikpu_nth_entry(rvu, idx), + 0, idx, true); /* Program KPU CAM and Action profiles */ num_kpus = rvu->kpu.kpus; @@ -2058,6 +2348,11 @@ static void npc_parser_profile_init(struct rvu *rvu, int blkaddr) for (idx = 0; idx < num_kpus; idx++) npc_program_kpu_profile(rvu, blkaddr, idx, &rvu->kpu.kpu[idx]); + + if (profile->from_fs) { + rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_TYPE(54), 0x03); + rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_TYPE(58), 0x03); + } } void npc_mcam_rsrcs_deinit(struct rvu *rvu) @@ -2181,7 +2476,7 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) /* Alloc memory for MCAM entry to counter mapping and for tracking * counter's reference count. */ - mcam->entry2cntr_map = kcalloc(mcam->bmap_entries, sizeof(u16), + mcam->entry2cntr_map = kcalloc(mcam->total_entries, sizeof(u16), GFP_KERNEL); if (!mcam->entry2cntr_map) goto free_cntr_map; @@ -2197,10 +2492,11 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) if (!mcam->entry2target_pffunc) goto free_cntr_refcnt; - for (index = 0; index < mcam->bmap_entries; index++) { + for (index = 0; index < mcam->bmap_entries; index++) mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP; + + for (index = 0; index < mcam->total_entries; index++) mcam->entry2cntr_map[index] = NPC_MCAM_INVALID_MAP; - } for (cntr = 0; cntr < mcam->counters.max; cntr++) mcam->cntr2pfvf_map[cntr] = NPC_MCAM_INVALID_MAP; @@ -2287,18 +2583,21 @@ static void rvu_npc_hw_init(struct rvu *rvu, int blkaddr) static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) { - struct npc_mcam_kex_extr *mkex_extr = rvu->kpu.mcam_kex_prfl.mkex_extr; - struct npc_mcam_kex *mkex = rvu->kpu.mcam_kex_prfl.mkex; + const struct npc_mcam_kex_extr *mkex_extr; struct npc_mcam *mcam = &rvu->hw->mcam; struct rvu_hwinfo *hw = rvu->hw; + const struct npc_mcam_kex *mkex; u64 nibble_ena, rx_kex, tx_kex; u64 *keyx_cfg, reg; u8 intf; + mkex_extr = rvu->kpu.mcam_kex_prfl.mkex_extr; + mkex = rvu->kpu.mcam_kex_prfl.mkex; + if (is_cn20k(rvu->pdev)) { - keyx_cfg = mkex_extr->keyx_cfg; + keyx_cfg = (u64 *)mkex_extr->keyx_cfg; } else { - keyx_cfg = mkex->keyx_cfg; + keyx_cfg = (u64 *)mkex->keyx_cfg; /* Reserve last counter for MCAM RX miss action which is set to * drop packet. This way we will know how many pkts didn't * match any MCAM entry. @@ -3531,7 +3830,7 @@ static int __npc_mcam_free_counter(struct rvu *rvu, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; - u16 index, entry = 0; + u16 index; int blkaddr, err; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -3547,20 +3846,16 @@ static int __npc_mcam_free_counter(struct rvu *rvu, mcam->cntr2pfvf_map[req->cntr] = NPC_MCAM_INVALID_MAP; rvu_free_rsrc(&mcam->counters, req->cntr); - /* Disable all MCAM entry's stats which are using this counter */ - while (entry < mcam->bmap_entries) { + /* Disable all MCAM entry's stats which are using this counter. + * Scan the full MCAM index range: AF-reserved rules (e.g. CPT pass-2) + */ + for (index = 0; index < mcam->total_entries; index++) { if (!mcam->cntr_refcnt[req->cntr]) break; - - index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); - if (index >= mcam->bmap_entries) - break; - entry = index + 1; if (mcam->entry2cntr_map[index] != req->cntr) continue; - - npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, - index, req->cntr); + npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, + req->cntr); } return 0; @@ -3631,7 +3926,7 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp) { struct npc_mcam *mcam = &rvu->hw->mcam; - u16 index, entry = 0; + u16 index; int blkaddr, rc; /* Counter is not supported for CN20K */ @@ -3658,20 +3953,13 @@ int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu, } /* Disable all MCAM entry's stats which are using this counter */ - while (entry < mcam->bmap_entries) { + for (index = 0; index < mcam->total_entries; index++) { if (!mcam->cntr_refcnt[req->cntr]) break; - - index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry); - if (index >= mcam->bmap_entries) - break; - entry = index + 1; - if (mcam->entry2cntr_map[index] != req->cntr) continue; - - npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, - index, req->cntr); + npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr, index, + req->cntr); } exit: mutex_unlock(&mcam->lock); @@ -4085,12 +4373,10 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf) ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); - if (ucast_idx < 0) { - dev_err(rvu->dev, - "%s: Error to get ucast entry for pcifunc=%#x\n", - __func__, pcifunc); + + /* In cn20k, default rules are freed before detach rsrc */ + if (ucast_idx < 0) return; - } npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h index 83c5e32e2afc..662f6693cfe9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.h @@ -18,4 +18,21 @@ int npc_fwdb_prfl_img_map(struct rvu *rvu, void __iomem **prfl_img_addr, void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index); void npc_mcam_set_bit(struct npc_mcam *mcam, u16 index); + +struct npc_kpu_profile_action * +npc_get_ikpu_nth_entry(struct rvu *rvu, int n); + +int +npc_get_num_kpu_cam_entries(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl); +struct npc_kpu_profile_cam * +npc_get_kpu_cam_nth_entry(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl, int n); + +int +npc_get_num_kpu_action_entries(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl); +struct npc_kpu_profile_action * +npc_get_kpu_action_nth_entry(struct rvu *rvu, + const struct npc_kpu_profile *kpu_pfl, int n); #endif /* RVU_NPC_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 34f1e066707b..a22decbe3449 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1671,9 +1671,11 @@ rvu_npc_alloc_entry_for_flow_install(struct rvu *rvu, { struct npc_mcam_alloc_entry_req entry_req; struct npc_mcam_alloc_entry_rsp entry_rsp; + struct npc_get_pfl_info_rsp rsp = { 0 }; struct npc_get_num_kws_req kws_req; struct npc_get_num_kws_rsp kws_rsp; int off, kw_bits, rc; + struct msg_req req; u8 *src, *dst; if (!is_cn20k(rvu->pdev)) { @@ -1697,8 +1699,16 @@ rvu_npc_alloc_entry_for_flow_install(struct rvu *rvu, kw_bits = kws_rsp.kws * 64; *kw_type = NPC_MCAM_KEY_X2; - if (kw_bits > 256) + if (kw_bits > 256) { + rvu_mbox_handler_npc_get_pfl_info(rvu, &req, &rsp); + if (rsp.kw_type == NPC_MCAM_KEY_X2) { + dev_err(rvu->dev, + "Only X2 entries are supported in X2 profile\n"); + return -EOPNOTSUPP; + } + *kw_type = NPC_MCAM_KEY_X4; + } memset(&entry_req, 0, sizeof(entry_req)); memset(&entry_rsp, 0, sizeof(entry_rsp)); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h index 62cdc714ba57..ab89b8c6e490 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h @@ -596,6 +596,7 @@ #define NPC_AF_INTFX_KEX_CFG(a) (0x01010 | (a) << 8) #define NPC_AF_PKINDX_ACTION0(a) (0x80000ull | (a) << 6) #define NPC_AF_PKINDX_ACTION1(a) (0x80008ull | (a) << 6) +#define NPC_AF_PKINDX_TYPE(a) (0x80010ull | (a) << 6) #define NPC_AF_PKINDX_CPI_DEFX(a, b) (0x80020ull | (a) << 6 | (b) << 3) #define NPC_AF_KPUX_ENTRYX_CAMX(a, b, c) \ (0x100000 | (a) << 14 | (b) << 6 | (c) << 3) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c index a72694219df4..4a5ce0e67dda 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c @@ -8,7 +8,7 @@ /* Devlink Params APIs */ static int otx2_dl_mcam_count_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct otx2_devlink *otx2_dl = devlink_priv(devlink); @@ -97,7 +97,7 @@ static int otx2_dl_ucast_flt_cnt_get(struct devlink *devlink, u32 id, } static int otx2_dl_ucast_flt_cnt_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct otx2_devlink *otx2_dl = devlink_priv(devlink); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 38cc539d724d..5dd0591fed99 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -37,14 +37,13 @@ static void otx2_clear_ntuple_flow_info(struct otx2_nic *pfvf, struct otx2_flow_ flow_cfg->max_flows = 0; } -static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, bool *is_x2, - u16 *x4_slots) +static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, u16 *x4_slots, u8 *kw_type) { struct npc_get_pfl_info_rsp *rsp; struct msg_req *req; static struct { bool is_set; - bool is_x2; + u8 kw_type; u16 x4_slots; } pfl_info; @@ -53,8 +52,8 @@ static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, bool *is_x2, */ mutex_lock(&pfvf->mbox.lock); if (pfl_info.is_set) { - *is_x2 = pfl_info.is_x2; *x4_slots = pfl_info.x4_slots; + *kw_type = pfl_info.kw_type; mutex_unlock(&pfvf->mbox.lock); return 0; } @@ -79,16 +78,16 @@ static int otx2_mcam_pfl_info_get(struct otx2_nic *pfvf, bool *is_x2, return -EFAULT; } - *is_x2 = (rsp->kw_type == NPC_MCAM_KEY_X2); - if (*is_x2) - *x4_slots = 0; + pfl_info.kw_type = rsp->kw_type; + if (rsp->kw_type == NPC_MCAM_KEY_X2) + pfl_info.x4_slots = 0; else - *x4_slots = rsp->x4_slots; - - pfl_info.is_x2 = *is_x2; - pfl_info.x4_slots = *x4_slots; + pfl_info.x4_slots = rsp->x4_slots; pfl_info.is_set = true; + *x4_slots = pfl_info.x4_slots; + *kw_type = pfl_info.kw_type; + mutex_unlock(&pfvf->mbox.lock); return 0; } @@ -164,6 +163,7 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count) u16 dft_idx = 0, x4_slots = 0; int ent, allocated = 0, ref; bool is_x2 = false; + u8 kw_type = 0; int rc; /* Free current ones and allocate new ones with requested count */ @@ -182,12 +182,14 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count) } if (is_cn20k(pfvf->pdev)) { - rc = otx2_mcam_pfl_info_get(pfvf, &is_x2, &x4_slots); + rc = otx2_mcam_pfl_info_get(pfvf, &x4_slots, &kw_type); if (rc) { netdev_err(pfvf->netdev, "Error to retrieve profile info\n"); return rc; } + is_x2 = kw_type == NPC_MCAM_KEY_X2; + rc = otx2_get_dft_rl_idx(pfvf, &dft_idx); if (rc) { netdev_err(pfvf->netdev, @@ -289,6 +291,8 @@ int otx2_mcam_entry_init(struct otx2_nic *pfvf) struct npc_mcam_alloc_entry_rsp *rsp; int vf_vlan_max_flows, count; int rc, ref, prio, ent; + u8 kw_type = 0; + u16 x4_slots; u16 dft_idx; ref = 0; @@ -315,6 +319,16 @@ int otx2_mcam_entry_init(struct otx2_nic *pfvf) if (!flow_cfg->def_ent) return -ENOMEM; + kw_type = NPC_MCAM_KEY_X2; + if (is_cn20k(pfvf->pdev)) { + rc = otx2_mcam_pfl_info_get(pfvf, &x4_slots, &kw_type); + if (rc) { + netdev_err(pfvf->netdev, + "Error to get pfl info\n"); + return rc; + } + } + mutex_lock(&pfvf->mbox.lock); req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&pfvf->mbox); @@ -324,6 +338,10 @@ int otx2_mcam_entry_init(struct otx2_nic *pfvf) } req->kw_type = NPC_MCAM_KEY_X2; + if (is_cn20k(pfvf->pdev) && kw_type == NPC_MCAM_KEY_X4) { + req->kw_type = NPC_MCAM_KEY_X4; + ref &= (x4_slots - 1); + } req->contig = false; req->count = count; req->ref_prio = prio; @@ -1174,15 +1192,14 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) #ifdef CONFIG_DCB int vlan_prio, qidx, pfc_rule = 0; #endif + bool modify = false, is_x2; int err, vf = 0, off, sz; - bool modify = false; u8 kw_type = 0; u8 *src, *dst; u16 x4_slots; - bool is_x2; if (is_cn20k(pfvf->pdev)) { - err = otx2_mcam_pfl_info_get(pfvf, &is_x2, &x4_slots); + err = otx2_mcam_pfl_info_get(pfvf, &x4_slots, &kw_type); if (err) { netdev_err(pfvf->netdev, "Error to retrieve NPC profile info, pcifunc=%#x\n", @@ -1190,6 +1207,7 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow) return -EFAULT; } + is_x2 = kw_type == NPC_MCAM_KEY_X2; if (!is_x2) { err = otx2_prepare_flow_request(&flow->flow_spec, &treq); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index f9fbf0c17648..41a0ebdf201e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1053,7 +1053,6 @@ irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq) /* Clear the IRQ */ otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); - mbox_data = otx2_read64(pf, RVU_PF_PFAF_MBOX0); if (mbox_data & MBOX_UP_MSG) { @@ -1119,9 +1118,16 @@ int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) { struct otx2_hw *hw = &pf->hw; struct msg_req *req; + u64 mbox_int_mask; char *irq_name; int err; + mbox_int_mask = !is_cn20k(pf->pdev) ? BIT_ULL(0) : + BIT_ULL(0) | BIT_ULL(1); + + /* Clear stale mailbox interrupt state before installing the handler. */ + otx2_write64(pf, RVU_PF_INT, mbox_int_mask); + /* Register mailbox interrupt handler */ if (!is_cn20k(pf->pdev)) { irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE]; @@ -1147,17 +1153,8 @@ int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) return err; } - /* Enable mailbox interrupt for msgs coming from AF. - * First clear to avoid spurious interrupts, if any. - */ - if (!is_cn20k(pf->pdev)) { - otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); - otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0)); - } else { - otx2_write64(pf, RVU_PF_INT, BIT_ULL(0) | BIT_ULL(1)); - otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0) | - BIT_ULL(1)); - } + /* Enable mailbox interrupt for msgs coming from AF. */ + otx2_write64(pf, RVU_PF_INT_ENA_W1S, mbox_int_mask); if (!probe_af) return 0; @@ -1729,7 +1726,7 @@ int otx2_init_hw_resources(struct otx2_nic *pf) mutex_lock(&mbox->lock); free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox); if (free_req) { - free_req->flags = NIX_LF_DISABLE_FLOWS; + free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS; if (otx2_sync_mbox_msg(mbox)) dev_err(pf->dev, "%s failed to free nixlf\n", __func__); } @@ -1803,7 +1800,7 @@ void otx2_free_hw_resources(struct otx2_nic *pf) /* Reset NIX LF */ free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox); if (free_req) { - free_req->flags = NIX_LF_DISABLE_FLOWS; + free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS; if (!(pf->flags & OTX2_FLAG_PF_SHUTDOWN)) free_req->flags |= NIX_LF_DONT_FREE_TX_VTAG; if (otx2_sync_mbox_msg(mbox)) @@ -1926,7 +1923,6 @@ int otx2_alloc_queue_mem(struct otx2_nic *pf) struct otx2_qset *qset = &pf->qset; struct otx2_cq_poll *cq_poll; - /* RQ and SQs are mapped to different CQs, * so find out max CQ IRQs (i.e CINTs) needed. */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c index f4fdbfba8667..b022f52c6845 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -251,9 +251,17 @@ static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) { struct otx2_hw *hw = &vf->hw; struct msg_req *req; + u64 mbox_int_mask; char *irq_name; int err; + mbox_int_mask = !is_cn20k(vf->pdev) ? BIT_ULL(0) : + BIT_ULL(0) | BIT_ULL(1) | + BIT_ULL(2) | BIT_ULL(3); + + /* Clear stale mailbox interrupt state before installing the handler. */ + otx2_write64(vf, RVU_VF_INT, mbox_int_mask); + /* Register mailbox interrupt handler */ irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX * NAME_SIZE]; snprintf(irq_name, NAME_SIZE, "RVUVF%d AFVF Mbox", ((vf->pcifunc & @@ -274,18 +282,8 @@ static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf) return err; } - /* Enable mailbox interrupt for msgs coming from PF. - * First clear to avoid spurious interrupts, if any. - */ - if (!is_cn20k(vf->pdev)) { - otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); - otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0)); - } else { - otx2_write64(vf, RVU_VF_INT, BIT_ULL(0) | BIT_ULL(1) | - BIT_ULL(2) | BIT_ULL(3)); - otx2_write64(vf, RVU_VF_INT_ENA_W1S, BIT_ULL(0) | - BIT_ULL(1) | BIT_ULL(2) | BIT_ULL(3)); - } + /* Enable mailbox interrupt for msgs coming from PF. */ + otx2_write64(vf, RVU_VF_INT_ENA_W1S, mbox_int_mask); if (!probe_pf) return 0; diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c index 781c691473e1..519c364e87d1 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c @@ -310,9 +310,9 @@ wed_amsdu_show(struct seq_file *s, void *data) WED_AMSDU_ENG_MAX_QGPP_CNT), DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_CUR_ENTRY), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_MAX_BUF_MERGED), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), WED_AMSDU_ENG_MAX_MSDU_MERGED), DUMP_STR("WED AMDSU ENG2 INFO"), @@ -414,7 +414,7 @@ wed_amsdu_show(struct seq_file *s, void *data) WED_AMSDU_ENG_CUR_ENTRY), DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), WED_AMSDU_ENG_MAX_BUF_MERGED), - DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), + DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), WED_AMSDU_ENG_MAX_MSDU_MERGED), DUMP_STR("WED AMDSU ENG8 INFO"), diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c index fa6b21603416..0d38183c6ba7 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c @@ -367,8 +367,12 @@ mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) /* wo firmware reset */ wo_w32(MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00); - val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) | - MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; + val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR); + + if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index) + val |= MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK; + else + val |= MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; wo_w32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val); out: release_firmware(fw); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index e6b7e75894ff..c851daa5da9f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -213,10 +213,10 @@ static int mlx4_devlink_crdump_snapshot_set(struct devlink *devlink, u32 id, static int mlx4_devlink_max_macs_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - u32 value = val.vu32; + u32 value = val->vu32; if (value < 1 || value > 128) return -ERANGE; @@ -266,27 +266,27 @@ static void mlx4_devlink_set_params_init_values(struct devlink *devlink) value.vbool = !!mlx4_internal_err_reset; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET, - value); + &value); value.vu32 = 1UL << log_num_mac; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_MAX_MACS, - value); + &value); value.vbool = enable_64b_cqe_eqe; devl_param_driverinit_value_set(devlink, MLX4_DEVLINK_PARAM_ID_ENABLE_64B_CQE_EQE, - value); + &value); value.vbool = enable_4k_uar; devl_param_driverinit_value_set(devlink, MLX4_DEVLINK_PARAM_ID_ENABLE_4K_UAR, - value); + &value); value.vbool = false; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT, - value); + &value); } static inline void mlx4_set_num_reserved_uars(struct mlx4_dev *dev, @@ -4274,9 +4274,9 @@ int mlx4_restart_one(struct pci_dev *pdev) return mlx4_restart_one_up(pdev, false, NULL); } -#define MLX_SP(id) { PCI_VDEVICE(MELLANOX, id), MLX4_PCI_DEV_FORCE_SENSE_PORT } -#define MLX_VF(id) { PCI_VDEVICE(MELLANOX, id), MLX4_PCI_DEV_IS_VF } -#define MLX_GN(id) { PCI_VDEVICE(MELLANOX, id), 0 } +#define MLX_SP(id) { PCI_VDEVICE(MELLANOX, id), .driver_data = MLX4_PCI_DEV_FORCE_SENSE_PORT } +#define MLX_VF(id) { PCI_VDEVICE(MELLANOX, id), .driver_data = MLX4_PCI_DEV_IS_VF } +#define MLX_GN(id) { PCI_VDEVICE(MELLANOX, id), .driver_data = 0 } static const struct pci_device_id mlx4_pci_table[] = { #ifdef CONFIG_MLX4_CORE_GEN2 diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index d39fe9c4a87c..19e50f0d55af 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -41,7 +41,7 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o mlx5_core-$(CONFIG_MLX5_ESWITCH) += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \ en_rep.o en/rep/bond.o en/mod_hdr.o \ - en/mapping.o lag/mpesw.o + en/mapping.o lag/mpesw.o lag/shared_fdb.o mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \ lib/fs_chains.o en/tc_tun.o \ esw/indir_table.o en/tc_tun_encap.o \ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 202feab1558a..4fe9d7d4f143 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -37,10 +37,17 @@ #include #include #include +#include +#include +#include #include #include "mlx5_core.h" +#define MLX5_FRAG_BUF_POOL_MIN_BLOCK_SHIFT MLX5_ADAPTER_PAGE_SHIFT +#define MLX5_FRAG_BUF_POOLS_NUM \ + (PAGE_SHIFT - MLX5_FRAG_BUF_POOL_MIN_BLOCK_SHIFT + 1) + struct mlx5_db_pgdir { struct list_head list; unsigned long *bitmap; @@ -48,6 +55,34 @@ struct mlx5_db_pgdir { dma_addr_t db_dma; }; +struct mlx5_dma_pool { + /* Protects page_list and per-page allocation bitmaps. */ + struct mutex lock; + struct list_head page_list; + struct mlx5_core_dev *dev; + int node; + u8 block_shift; +}; + +struct mlx5_dma_pool_page { + struct mlx5_dma_pool *pool; + struct list_head pool_link; + unsigned long *bitmap; + void *buf; + dma_addr_t dma; +}; + +struct mlx5_frag_buf_node_pools { + struct mlx5_dma_pool *pools[MLX5_FRAG_BUF_POOLS_NUM]; +}; + +struct mlx5_dma_pool_stats { + int node; + size_t block_size; + size_t used_blocks; + size_t allocated_blocks; +}; + /* Handling for queue buffers -- we allocate a bunch of memory and * register it in a memory region at HCA virtual address 0. */ @@ -71,59 +106,339 @@ static void *mlx5_dma_zalloc_coherent_node(struct mlx5_core_dev *dev, return cpu_handle; } -int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, - struct mlx5_frag_buf *buf, int node) +static void mlx5_dma_pool_destroy(struct mlx5_dma_pool *pool) { - int i; + mutex_destroy(&pool->lock); + kfree(pool); +} - buf->size = size; - buf->npages = DIV_ROUND_UP(size, PAGE_SIZE); - buf->page_shift = PAGE_SHIFT; - buf->frags = kzalloc_objs(struct mlx5_buf_list, buf->npages); - if (!buf->frags) +static struct mlx5_dma_pool *mlx5_dma_pool_create(struct mlx5_core_dev *dev, + int node, u8 block_shift) +{ + struct mlx5_dma_pool *pool; + + pool = kzalloc_obj(*pool); + if (!pool) + return NULL; + + INIT_LIST_HEAD(&pool->page_list); + mutex_init(&pool->lock); + pool->dev = dev; + pool->node = node; + pool->block_shift = block_shift; + return pool; +} + +static struct mlx5_dma_pool_page * +mlx5_dma_pool_page_alloc(struct mlx5_dma_pool *pool) +{ + int blocks_per_page = BIT(PAGE_SHIFT - pool->block_shift); + struct mlx5_dma_pool_page *page; + + page = kzalloc_obj(*page); + if (!page) goto err_out; - for (i = 0; i < buf->npages; i++) { - struct mlx5_buf_list *frag = &buf->frags[i]; - int frag_sz = min_t(int, size, PAGE_SIZE); + page->pool = pool; + page->bitmap = bitmap_zalloc(blocks_per_page, GFP_KERNEL); + if (!page->bitmap) + goto err_free_page; - frag->buf = mlx5_dma_zalloc_coherent_node(dev, frag_sz, - &frag->map, node); - if (!frag->buf) - goto err_free_buf; - if (frag->map & ((1 << buf->page_shift) - 1)) { - dma_free_coherent(mlx5_core_dma_dev(dev), frag_sz, - buf->frags[i].buf, buf->frags[i].map); - mlx5_core_warn(dev, "unexpected map alignment: %pad, page_shift=%d\n", - &frag->map, buf->page_shift); - goto err_free_buf; + bitmap_fill(page->bitmap, blocks_per_page); + page->buf = mlx5_dma_zalloc_coherent_node(pool->dev, PAGE_SIZE, + &page->dma, pool->node); + if (!page->buf) + goto err_free_bitmap; + + return page; + +err_free_bitmap: + bitmap_free(page->bitmap); +err_free_page: + kfree(page); +err_out: + return NULL; +} + +static void mlx5_dma_pool_page_free(struct mlx5_core_dev *dev, + struct mlx5_dma_pool_page *page) +{ + dma_free_coherent(mlx5_core_dma_dev(dev), PAGE_SIZE, page->buf, + page->dma); + bitmap_free(page->bitmap); + kfree(page); +} + +static int mlx5_dma_pool_alloc_from_page(struct mlx5_dma_pool *pool, + struct mlx5_dma_pool_page *page, + unsigned long *idx_out) +{ + int blocks_per_page = BIT(PAGE_SHIFT - pool->block_shift); + + *idx_out = find_first_bit(page->bitmap, blocks_per_page); + if (*idx_out >= blocks_per_page) + return -ENOMEM; + + __clear_bit(*idx_out, page->bitmap); + + if (bitmap_empty(page->bitmap, blocks_per_page)) + list_move_tail(&page->pool_link, &pool->page_list); + + return 0; +} + +static struct mlx5_dma_pool_page * +mlx5_dma_pool_alloc(struct mlx5_dma_pool *pool, unsigned long *idx_out) +{ + struct mlx5_dma_pool_page *page; + + mutex_lock(&pool->lock); + + page = list_first_entry_or_null(&pool->page_list, + struct mlx5_dma_pool_page, pool_link); + if (page && !mlx5_dma_pool_alloc_from_page(pool, page, idx_out)) + goto unlock; /* successfully allocated from existing page */ + + page = mlx5_dma_pool_page_alloc(pool); + if (!page) + goto unlock; + + list_add(&page->pool_link, &pool->page_list); + mlx5_dma_pool_alloc_from_page(pool, page, idx_out); + +unlock: + mutex_unlock(&pool->lock); + return page; +} + +static void mlx5_dma_pool_free(struct mlx5_dma_pool *pool, + struct mlx5_dma_pool_page *page, + unsigned long idx) +{ + int blocks_per_page = BIT(PAGE_SHIFT - pool->block_shift); + bool was_full; + + mutex_lock(&pool->lock); + was_full = bitmap_empty(page->bitmap, blocks_per_page); + __set_bit(idx, page->bitmap); + + if (bitmap_full(page->bitmap, blocks_per_page)) { + list_del(&page->pool_link); + mlx5_dma_pool_page_free(pool->dev, page); + } else { + memset((u8 *)page->buf + (idx << pool->block_shift), 0, + BIT(pool->block_shift)); + if (was_full) + list_move(&page->pool_link, &pool->page_list); + } + mutex_unlock(&pool->lock); +} + +static void mlx5_dma_pool_debugfs_get_stats(struct mlx5_dma_pool *pool, + struct mlx5_dma_pool_stats *stats) +{ + int blocks_per_page = BIT(PAGE_SHIFT - pool->block_shift); + struct mlx5_dma_pool_page *page; + size_t free_blocks = 0; + size_t pages = 0; + + mutex_lock(&pool->lock); + list_for_each_entry(page, &pool->page_list, pool_link) { + pages++; + free_blocks += bitmap_weight(page->bitmap, blocks_per_page); + } + mutex_unlock(&pool->lock); + + stats->node = pool->node; + stats->block_size = BIT(pool->block_shift); + stats->allocated_blocks = pages * blocks_per_page; + stats->used_blocks = stats->allocated_blocks - free_blocks; +} + +static void mlx5_dma_pool_debugfs_stats_print(struct seq_file *file, + struct mlx5_dma_pool *pool) +{ + struct mlx5_dma_pool_stats stats = {}; + + mlx5_dma_pool_debugfs_get_stats(pool, &stats); + seq_printf(file, "%4d %5zu %7zu %7zu\n", + stats.node, stats.block_size, stats.used_blocks, + stats.allocated_blocks); +} + +static void mlx5_dma_pools_debugfs_print_header(struct seq_file *file) +{ + seq_puts(file, "node block_size used_blocks allocated_blocks\n"); +} + +static void +mlx5_frag_buf_node_pools_destroy(struct mlx5_frag_buf_node_pools *node_pools) +{ + for (int i = 0; i < MLX5_FRAG_BUF_POOLS_NUM; i++) + if (node_pools->pools[i]) + mlx5_dma_pool_destroy(node_pools->pools[i]); + kfree(node_pools); +} + +static struct mlx5_frag_buf_node_pools * +mlx5_frag_buf_node_pools_create(struct mlx5_core_dev *dev, int node) +{ + struct mlx5_frag_buf_node_pools *node_pools; + + node_pools = kzalloc_obj(*node_pools); + if (!node_pools) + return NULL; + + for (int i = 0; i < MLX5_FRAG_BUF_POOLS_NUM; i++) { + u8 block_shift = MLX5_FRAG_BUF_POOL_MIN_BLOCK_SHIFT + i; + + node_pools->pools[i] = mlx5_dma_pool_create(dev, node, + block_shift); + if (!node_pools->pools[i]) { + mlx5_frag_buf_node_pools_destroy(node_pools); + return NULL; + } + } + + return node_pools; +} + +static int +mlx5_frag_buf_dma_pools_debugfs_show(struct seq_file *file, void *priv) +{ + struct mlx5_core_dev *dev = file->private; + int node; + + mlx5_dma_pools_debugfs_print_header(file); + + if (!dev->priv.frag_buf_node_pools) + return 0; + + for_each_node_state(node, N_POSSIBLE) { + struct mlx5_frag_buf_node_pools *node_pools; + + node_pools = dev->priv.frag_buf_node_pools[node]; + if (!node_pools) + continue; + + for (int i = 0; i < MLX5_FRAG_BUF_POOLS_NUM; i++) { + struct mlx5_dma_pool *pool = node_pools->pools[i]; + + if (!pool) + continue; + + mlx5_dma_pool_debugfs_stats_print(file, pool); } - size -= frag_sz; } return 0; +} +DEFINE_SHOW_ATTRIBUTE(mlx5_frag_buf_dma_pools_debugfs); -err_free_buf: - while (i--) - dma_free_coherent(mlx5_core_dma_dev(dev), PAGE_SIZE, buf->frags[i].buf, - buf->frags[i].map); - kfree(buf->frags); -err_out: - return -ENOMEM; +void mlx5_frag_buf_pools_cleanup(struct mlx5_core_dev *dev) +{ + struct mlx5_priv *priv = &dev->priv; + int node; + + debugfs_remove(priv->dbg.frag_buf_dma_pools_debugfs); + priv->dbg.frag_buf_dma_pools_debugfs = NULL; + + for_each_node_state(node, N_POSSIBLE) { + struct mlx5_frag_buf_node_pools *node_pools; + + node_pools = priv->frag_buf_node_pools[node]; + if (!node_pools) + continue; + mlx5_frag_buf_node_pools_destroy(node_pools); + } + + kfree(priv->frag_buf_node_pools); + priv->frag_buf_node_pools = NULL; +} + +int mlx5_frag_buf_pools_init(struct mlx5_core_dev *dev) +{ + struct mlx5_priv *priv = &dev->priv; + int node; + + priv->frag_buf_node_pools = kzalloc_objs(*priv->frag_buf_node_pools, + nr_node_ids); + if (!priv->frag_buf_node_pools) + return -ENOMEM; + + for_each_node_state(node, N_POSSIBLE) { + struct mlx5_frag_buf_node_pools *node_pools; + + node_pools = mlx5_frag_buf_node_pools_create(dev, node); + if (!node_pools) { + mlx5_frag_buf_pools_cleanup(dev); + return -ENOMEM; + } + priv->frag_buf_node_pools[node] = node_pools; + } + + priv->dbg.frag_buf_dma_pools_debugfs = + debugfs_create_file("frag_buf_dma_pools", 0444, + priv->dbg.dbg_root, dev, + &mlx5_frag_buf_dma_pools_debugfs_fops); + + return 0; +} + +int mlx5_frag_buf_alloc_node(struct mlx5_core_dev *dev, int size, + struct mlx5_frag_buf *buf, int node) +{ + struct mlx5_dma_pool *pool; + int pool_idx; + + node = node == NUMA_NO_NODE ? numa_mem_id() : node; + + buf->size = size; + buf->npages = DIV_ROUND_UP(size, PAGE_SIZE); + buf->page_shift = clamp_t(int, order_base_2(size), + MLX5_FRAG_BUF_POOL_MIN_BLOCK_SHIFT, + PAGE_SHIFT); + buf->frags = kcalloc_node(buf->npages, sizeof(*buf->frags), + GFP_KERNEL, node); + if (!buf->frags) + return -ENOMEM; + + pool_idx = buf->page_shift - MLX5_FRAG_BUF_POOL_MIN_BLOCK_SHIFT; + pool = dev->priv.frag_buf_node_pools[node]->pools[pool_idx]; + for (int i = 0; i < buf->npages; i++) { + struct mlx5_buf_list *frag = &buf->frags[i]; + struct mlx5_dma_pool_page *page; + unsigned long idx; + + page = mlx5_dma_pool_alloc(pool, &idx); + if (!page) { + mlx5_frag_buf_free(dev, buf); + return -ENOMEM; + } + frag->buf = (u8 *)page->buf + (idx << pool->block_shift); + frag->map = page->dma + (idx << pool->block_shift); + frag->frag_page = page; + } + + return 0; } EXPORT_SYMBOL_GPL(mlx5_frag_buf_alloc_node); void mlx5_frag_buf_free(struct mlx5_core_dev *dev, struct mlx5_frag_buf *buf) { - int size = buf->size; - int i; + for (int i = 0; i < buf->npages; i++) { + struct mlx5_buf_list *frag = &buf->frags[i]; + struct mlx5_dma_pool_page *page; + struct mlx5_dma_pool *pool; + unsigned long idx; - for (i = 0; i < buf->npages; i++) { - int frag_sz = min_t(int, size, PAGE_SIZE); + if (!frag->buf) + continue; - dma_free_coherent(mlx5_core_dma_dev(dev), frag_sz, buf->frags[i].buf, - buf->frags[i].map); - size -= frag_sz; + page = frag->frag_page; + pool = page->pool; + idx = (frag->map - page->dma) >> pool->block_shift; + mlx5_dma_pool_free(pool, page, idx); } kfree(buf->frags); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index e2895972cc82..84583dc5eb1c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -2308,6 +2308,7 @@ int mlx5_cmd_alias_obj_create(struct mlx5_core_dev *dev, attr = MLX5_ADDR_OF(create_alias_obj_in, in, alias_ctx); MLX5_SET(alias_context, attr, vhca_id_to_be_accessed, alias_attr->vhca_id); + MLX5_SET(alias_context, attr, vhca_id_type, alias_attr->vhca_id_type); MLX5_SET(alias_context, attr, object_id_to_be_accessed, alias_attr->obj_id); key = MLX5_ADDR_OF(alias_context, attr, access_key); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c index 8fe263190d38..30be2b631e7c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c @@ -285,10 +285,6 @@ void mlx5_pages_debugfs_init(struct mlx5_core_dev *dev) 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.page_counters[MLX5_VF]); - debugfs_create_u32("fw_pages_ec_vfs", 0400, pages, &dev->priv.page_counters[MLX5_EC_VF]); - debugfs_create_u32("fw_pages_sfs", 0400, pages, &dev->priv.page_counters[MLX5_SF]); - debugfs_create_u32("fw_pages_host_pf", 0400, pages, &dev->priv.page_counters[MLX5_HOST_PF]); 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, @@ -300,6 +296,44 @@ void mlx5_pages_debugfs_cleanup(struct mlx5_core_dev *dev) debugfs_remove_recursive(dev->priv.dbg.pages_debugfs); } +void mlx5_pages_by_func_type_debugfs_init(struct mlx5_core_dev *dev) +{ + struct dentry *pages = dev->priv.dbg.pages_debugfs; + + if (!pages) + return; + + if (!dev->priv.eswitch && + MLX5_CAP_GEN(dev, icm_mng_function_id_mode) == + MLX5_ID_MODE_FUNCTION_VHCA_ID) + return; + + debugfs_create_u32("fw_pages_vfs", 0400, pages, + &dev->priv.page_counters[MLX5_VF]); + debugfs_create_u32("fw_pages_ec_vfs", 0400, pages, + &dev->priv.page_counters[MLX5_EC_VF]); + debugfs_create_u32("fw_pages_sfs", 0400, pages, + &dev->priv.page_counters[MLX5_SF]); + debugfs_create_u32("fw_pages_host_pf", 0400, pages, + &dev->priv.page_counters[MLX5_HOST_PF]); + debugfs_create_u32("fw_pages_spfs", 0400, pages, + &dev->priv.page_counters[MLX5_SPF]); +} + +void mlx5_pages_by_func_type_debugfs_cleanup(struct mlx5_core_dev *dev) +{ + struct dentry *pages = dev->priv.dbg.pages_debugfs; + + if (!pages) + return; + + debugfs_lookup_and_remove("fw_pages_vfs", pages); + debugfs_lookup_and_remove("fw_pages_ec_vfs", pages); + debugfs_lookup_and_remove("fw_pages_sfs", pages); + debugfs_lookup_and_remove("fw_pages_host_pf", pages); + debugfs_lookup_and_remove("fw_pages_spfs", pages); +} + static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp, int index, int *is_str) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 73cf0321bb86..c31e05529fc4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -459,11 +459,11 @@ void mlx5_devlink_free(struct devlink *devlink) } static int mlx5_devlink_enable_roce_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - bool new_state = val.vbool; + bool new_state = val->vbool; if (new_state && !MLX5_CAP_GEN(dev, roce) && !(MLX5_CAP_GEN(dev, roce_rw_supported) && MLX5_CAP_GEN_MAX(dev, roce))) { @@ -480,10 +480,10 @@ static int mlx5_devlink_enable_roce_validate(struct devlink *devlink, u32 id, #ifdef CONFIG_MLX5_ESWITCH static int mlx5_devlink_large_group_num_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - int group_num = val.vu32; + int group_num = val->vu32; if (group_num < 1 || group_num > 1024) { NL_SET_ERR_MSG_MOD(extack, @@ -496,27 +496,27 @@ static int mlx5_devlink_large_group_num_validate(struct devlink *devlink, u32 id #endif static int mlx5_devlink_eq_depth_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - return (val.vu32 >= 64 && val.vu32 <= 4096) ? 0 : -EINVAL; + return (val->vu32 >= 64 && val->vu32 <= 4096) ? 0 : -EINVAL; } static int mlx5_devlink_hairpin_num_queues_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - return val.vu32 ? 0 : -EINVAL; + return val->vu32 ? 0 : -EINVAL; } static int mlx5_devlink_hairpin_queue_size_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - u32 val32 = val.vu32; + u32 val32 = val->vu32; if (!is_power_of_2(val32)) { NL_SET_ERR_MSG_MOD(extack, "Value is not power of two"); @@ -534,11 +534,11 @@ mlx5_devlink_hairpin_queue_size_validate(struct devlink *devlink, u32 id, } static int mlx5_devlink_num_doorbells_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *mdev = devlink_priv(devlink); - u32 val32 = val.vu32; + u32 val32 = val->vu32; u32 max_num_channels; max_num_channels = mlx5e_get_max_num_channels(mdev); @@ -567,13 +567,13 @@ static void mlx5_devlink_hairpin_params_init_values(struct devlink *devlink) value.vu32 = link_speed64; devl_param_driverinit_value_set( - devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, value); + devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_NUM_QUEUES, &value); value.vu32 = BIT(min_t(u32, 16 - MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(dev), MLX5_CAP_GEN(dev, log_max_hairpin_num_packets))); devl_param_driverinit_value_set( - devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, value); + devlink, MLX5_DEVLINK_PARAM_ID_HAIRPIN_QUEUE_SIZE, &value); } static const struct devlink_param mlx5_devlink_params[] = { @@ -600,24 +600,24 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink) value.vbool = MLX5_CAP_GEN(dev, roce) && !mlx5_dev_is_lightweight(dev); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE, - value); + &value); #ifdef CONFIG_MLX5_ESWITCH value.vu32 = ESW_OFFLOADS_DEFAULT_NUM_GROUPS; devl_param_driverinit_value_set(devlink, MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM, - value); + &value); #endif value.vu32 = MLX5_COMP_EQ_SIZE; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE, - value); + &value); value.vu32 = MLX5_NUM_ASYNC_EQE; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE, - value); + &value); } static const struct devlink_param mlx5_devlink_eth_params[] = { @@ -653,14 +653,14 @@ static int mlx5_devlink_eth_params_register(struct devlink *devlink) value.vbool = !mlx5_dev_is_lightweight(dev); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH, - value); + &value); mlx5_devlink_hairpin_params_init_values(devlink); value.vu32 = MLX5_DEFAULT_NUM_DOORBELLS; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_NUM_DOORBELLS, - value); + &value); return 0; } @@ -681,12 +681,12 @@ static void mlx5_devlink_eth_params_unregister(struct devlink *devlink) static int mlx5_devlink_pcie_cong_thresh_validate(struct devlink *devl, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - if (val.vu16 > MLX5_PCIE_CONG_THRESH_MAX) { + if (val->vu16 > MLX5_PCIE_CONG_THRESH_MAX) { NL_SET_ERR_MSG_FMT_MOD(extack, "Value %u > max supported (%u)", - val.vu16, MLX5_PCIE_CONG_THRESH_MAX); + val->vu16, MLX5_PCIE_CONG_THRESH_MAX); return -EINVAL; } @@ -711,19 +711,19 @@ static void mlx5_devlink_pcie_cong_init_values(struct devlink *devlink) value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_LOW; id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW; - devl_param_driverinit_value_set(devlink, id, value); + devl_param_driverinit_value_set(devlink, id, &value); value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_HIGH; id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH; - devl_param_driverinit_value_set(devlink, id, value); + devl_param_driverinit_value_set(devlink, id, &value); value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_LOW; id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW; - devl_param_driverinit_value_set(devlink, id, value); + devl_param_driverinit_value_set(devlink, id, &value); value.vu16 = MLX5_PCIE_CONG_THRESH_DEF_HIGH; id = MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH; - devl_param_driverinit_value_set(devlink, id, value); + devl_param_driverinit_value_set(devlink, id, &value); } static const struct devlink_param mlx5_devlink_pcie_cong_params[] = { @@ -775,11 +775,11 @@ static void mlx5_devlink_pcie_cong_params_unregister(struct devlink *devlink) } static int mlx5_devlink_enable_rdma_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - bool new_state = val.vbool; + bool new_state = val->vbool; if (new_state && !mlx5_rdma_supported(dev)) return -EOPNOTSUPP; @@ -808,7 +808,7 @@ static int mlx5_devlink_rdma_params_register(struct devlink *devlink) value.vbool = !mlx5_dev_is_lightweight(dev); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA, - value); + &value); return 0; } @@ -843,7 +843,7 @@ static int mlx5_devlink_vnet_params_register(struct devlink *devlink) value.vbool = !mlx5_dev_is_lightweight(dev); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET, - value); + &value); return 0; } @@ -890,22 +890,22 @@ static void mlx5_devlink_auxdev_params_unregister(struct devlink *devlink) } static int mlx5_devlink_max_uc_list_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - if (val.vu32 == 0) { + if (val->vu32 == 0) { NL_SET_ERR_MSG_MOD(extack, "max_macs value must be greater than 0"); return -EINVAL; } - if (!is_power_of_2(val.vu32)) { + if (!is_power_of_2(val->vu32)) { NL_SET_ERR_MSG_MOD(extack, "Only power of 2 values are supported for max_macs"); return -EINVAL; } - if (ilog2(val.vu32) > + if (ilog2(val->vu32) > MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) { NL_SET_ERR_MSG_MOD(extack, "max_macs value is out of the supported range"); return -EINVAL; @@ -936,7 +936,7 @@ static int mlx5_devlink_max_uc_list_params_register(struct devlink *devlink) value.vu32 = 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list); devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_MAX_MACS, - value); + &value); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c index bce72e8d1bc3..7c69d9029bfa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c @@ -300,7 +300,8 @@ static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin, static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv, const struct dpll_device *dpll, void *dpll_priv, - s64 *ffo, struct netlink_ext_ack *extack) + struct dpll_ffo_param *ffo, + struct netlink_ext_ack *extack) { struct mlx5_dpll_synce_status synce_status; struct mlx5_dpll *mdpll = pin_priv; @@ -309,10 +310,11 @@ static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv, err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status); if (err) return err; - return mlx5_dpll_pin_ffo_get(&synce_status, ffo); + return mlx5_dpll_pin_ffo_get(&synce_status, &ffo->ffo); } static const struct dpll_pin_ops mlx5_dpll_pins_ops = { + .supported_ffo = BIT(DPLL_FFO_PORT_RXTX_RATE), .direction_get = mlx5_dpll_pin_direction_get, .state_on_dpll_get = mlx5_dpll_state_on_dpll_get, .state_on_dpll_set = mlx5_dpll_state_on_dpll_set, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c index 15cb27aea2c9..9839f1a58640 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c @@ -18,25 +18,35 @@ static bool mlx5_ecpf_esw_admins_host_pf(const struct mlx5_core_dev *dev) return mlx5_core_is_ecpf_esw_manager(dev); } -int mlx5_cmd_host_pf_enable_hca(struct mlx5_core_dev *dev) +int mlx5_cmd_pf_enable_hca(struct mlx5_core_dev *dev, u16 vport_num) { u32 out[MLX5_ST_SZ_DW(enable_hca_out)] = {}; u32 in[MLX5_ST_SZ_DW(enable_hca_in)] = {}; + u16 vhca_id; MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA); - MLX5_SET(enable_hca_in, in, function_id, 0); - MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0); - return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); + if (mlx5_vport_use_vhca_id_as_func_id(dev, vport_num, &vhca_id)) { + MLX5_SET(enable_hca_in, in, function_id, vhca_id); + MLX5_SET(enable_hca_in, in, function_id_type, 1); + } else { + MLX5_SET(enable_hca_in, in, function_id, vport_num); + } + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } -int mlx5_cmd_host_pf_disable_hca(struct mlx5_core_dev *dev) +int mlx5_cmd_pf_disable_hca(struct mlx5_core_dev *dev, u16 vport_num) { u32 out[MLX5_ST_SZ_DW(disable_hca_out)] = {}; u32 in[MLX5_ST_SZ_DW(disable_hca_in)] = {}; + u16 vhca_id; MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA); - MLX5_SET(disable_hca_in, in, function_id, 0); - MLX5_SET(disable_hca_in, in, embedded_cpu_function, 0); + if (mlx5_vport_use_vhca_id_as_func_id(dev, vport_num, &vhca_id)) { + MLX5_SET(disable_hca_in, in, function_id, vhca_id); + MLX5_SET(disable_hca_in, in, function_id_type, 1); + } else { + MLX5_SET(disable_hca_in, in, function_id, vport_num); + } return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } @@ -92,6 +102,11 @@ void mlx5_ec_cleanup(struct mlx5_core_dev *dev) if (err) mlx5_core_warn(dev, "Timeout reclaiming external host PF pages err(%d)\n", err); + err = mlx5_wait_for_pages(dev, &dev->priv.page_counters[MLX5_SPF]); + if (err) + mlx5_core_warn(dev, "Timeout reclaiming SPF pages err(%d)\n", + err); + err = mlx5_wait_for_pages(dev, &dev->priv.page_counters[MLX5_VF]); if (err) mlx5_core_warn(dev, "Timeout reclaiming external host VFs pages err(%d)\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.h b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.h index 40b6ad76dca6..d9f9a53b019b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.h @@ -17,8 +17,8 @@ bool mlx5_read_embedded_cpu(struct mlx5_core_dev *dev); int mlx5_ec_init(struct mlx5_core_dev *dev); void mlx5_ec_cleanup(struct mlx5_core_dev *dev); -int mlx5_cmd_host_pf_enable_hca(struct mlx5_core_dev *dev); -int mlx5_cmd_host_pf_disable_hca(struct mlx5_core_dev *dev); +int mlx5_cmd_pf_enable_hca(struct mlx5_core_dev *dev, u16 vport_num); +int mlx5_cmd_pf_disable_hca(struct mlx5_core_dev *dev, u16 vport_num); #else /* CONFIG_MLX5_ESWITCH */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c b/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c index 2eb666a46f39..f4f2ecfc6719 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c @@ -252,28 +252,37 @@ static int mlx5e_pcie_cong_get_thresh_config(struct mlx5_core_dev *dev, struct mlx5e_pcie_cong_thresh *config) { - u32 ids[4] = { - MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW, - MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH, - MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW, - MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH, - }; struct devlink *devlink = priv_to_devlink(dev); - union devlink_param_value val[4]; + union devlink_param_value val; + int err; - for (int i = 0; i < 4; i++) { - u32 id = ids[i]; - int err; + err = devl_param_driverinit_value_get(devlink, + MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW, + &val); + if (err) + return err; + config->inbound_low = val.vu16; - err = devl_param_driverinit_value_get(devlink, id, &val[i]); - if (err) - return err; - } + err = devl_param_driverinit_value_get(devlink, + MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH, + &val); + if (err) + return err; + config->inbound_high = val.vu16; - config->inbound_low = val[0].vu16; - config->inbound_high = val[1].vu16; - config->outbound_low = val[2].vu16; - config->outbound_high = val[3].vu16; + err = devl_param_driverinit_value_get(devlink, + MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW, + &val); + if (err) + return err; + config->outbound_low = val.vu16; + + err = devl_param_driverinit_value_get(devlink, + MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH, + &val); + if (err) + return err; + config->outbound_high = val.vu16; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.c index 8d9a3b5ec973..8511363f7bec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.c @@ -8,13 +8,28 @@ static bool verify_num_vhca_ids(struct mlx5_core_dev *mdev, u32 *vhca_ids, unsigned int size) { unsigned int max_num_vhca_id = MLX5_CAP_GEN_2(mdev, max_rqt_vhca_id); - int i; + unsigned int unique_count = 0; + int i, j; - /* Verify that all vhca_ids are in range [0, max_num_vhca_ids - 1] */ - for (i = 0; i < size; i++) - if (vhca_ids[i] >= max_num_vhca_id) - return false; - return true; + /* Count unique vhca_ids */ + for (i = 0; i < size; i++) { + bool is_unique = true; + + /* Check if vhca_ids[i] was already seen */ + for (j = 0; j < i; j++) { + if (vhca_ids[j] == vhca_ids[i]) { + is_unique = false; + break; + } + } + if (is_unique) + unique_count++; + } + + /* Verify that number of unique vhca_ids doesn't exceed + * max_num_vhca_id + */ + return unique_count <= max_num_vhca_id; } static bool rqt_verify_vhca_ids(struct mlx5_core_dev *mdev, u32 *vhca_ids, @@ -168,8 +183,6 @@ int mlx5e_rqt_init_indir(struct mlx5e_rqt *rqt, struct mlx5_core_dev *mdev, return err; } -#define MLX5E_UNIFORM_SPREAD_RQT_FACTOR 2 - u32 mlx5e_rqt_size(struct mlx5_core_dev *mdev, unsigned int num_channels) { u32 rqt_size = max_t(u32, MLX5E_INDIR_MIN_RQT_SIZE, @@ -179,13 +192,6 @@ u32 mlx5e_rqt_size(struct mlx5_core_dev *mdev, unsigned int num_channels) return min_t(u32, rqt_size, max_cap_rqt_size); } -#define MLX5E_MAX_RQT_SIZE_ALLOWED_WITH_XOR8_HASH 256 - -unsigned int mlx5e_rqt_max_num_channels_allowed_for_xor8(void) -{ - return MLX5E_MAX_RQT_SIZE_ALLOWED_WITH_XOR8_HASH / MLX5E_UNIFORM_SPREAD_RQT_FACTOR; -} - void mlx5e_rqt_destroy(struct mlx5e_rqt *rqt) { mlx5_core_destroy_rqt(rqt->mdev, rqt->rqtn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.h b/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.h index 2f9e04a8418f..c6d0a92b132c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rqt.h @@ -7,6 +7,7 @@ #include #define MLX5E_INDIR_MIN_RQT_SIZE (BIT(8)) +#define MLX5E_UNIFORM_SPREAD_RQT_FACTOR 4 struct mlx5_core_dev; @@ -38,7 +39,6 @@ static inline u32 mlx5e_rqt_get_rqtn(struct mlx5e_rqt *rqt) } u32 mlx5e_rqt_size(struct mlx5_core_dev *mdev, unsigned int num_channels); -unsigned int mlx5e_rqt_max_num_channels_allowed_for_xor8(void); int mlx5e_rqt_redirect_direct(struct mlx5e_rqt *rqt, u32 rqn, u32 *vhca_id); int mlx5e_rqt_redirect_indir(struct mlx5e_rqt *rqt, u32 *rqns, u32 *vhca_ids, unsigned int num_rqns, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c index a2ec67a122d9..de435df7ca50 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. +#include #include "rss.h" #define mlx5e_rss_warn(__dev, format, ...) \ @@ -85,9 +86,33 @@ bool mlx5e_rss_get_inner_ft_support(struct mlx5e_rss *rss) return rss->params.inner_ft_support; } -void mlx5e_rss_params_indir_modify_actual_size(struct mlx5e_rss *rss, u32 num_channels) +u32 *mlx5e_rss_get_indir_table(struct mlx5e_rss *rss) { - rss->indir.actual_table_size = mlx5e_rqt_size(rss->mdev, num_channels); + return rss->indir.table; +} + +void mlx5e_rss_set_indir_actual_size(struct mlx5e_rss *rss, u32 size) +{ + rss->indir.actual_table_size = size; +} + +/* Handles non-default contexts, replicate existing pattern into new entries, + * matching what ethtool_rxfh_ctxs_resize() does. + */ +void mlx5e_rss_ctx_resize(struct mlx5e_rss *rss, u32 new_size) +{ + u32 old_size = rss->indir.actual_table_size; + u32 i; + + for (i = old_size; i < new_size; i++) + rss->indir.table[i] = rss->indir.table[i % old_size]; +} + +void mlx5e_rss_indir_resize(struct mlx5e_rss *rss, struct net_device *netdev, + u32 new_size) +{ + ethtool_rxfh_indir_resize(netdev, rss->indir.table, + rss->indir.actual_table_size, new_size); } int mlx5e_rss_params_indir_init(struct mlx5e_rss_params_indir *indir, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h index 17664757a561..1bb0434612a4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h @@ -34,7 +34,9 @@ struct mlx5e_rss; int mlx5e_rss_params_indir_init(struct mlx5e_rss_params_indir *indir, u32 actual_table_size, u32 max_table_size); void mlx5e_rss_params_indir_cleanup(struct mlx5e_rss_params_indir *indir); -void mlx5e_rss_params_indir_modify_actual_size(struct mlx5e_rss *rss, u32 num_channels); +void mlx5e_rss_ctx_resize(struct mlx5e_rss *rss, u32 new_size); +void mlx5e_rss_indir_resize(struct mlx5e_rss *rss, struct net_device *netdev, + u32 new_size); struct mlx5e_rss * mlx5e_rss_init(struct mlx5_core_dev *mdev, const struct mlx5e_rss_params *params, @@ -46,6 +48,8 @@ void mlx5e_rss_refcnt_dec(struct mlx5e_rss *rss); unsigned int mlx5e_rss_refcnt_read(struct mlx5e_rss *rss); bool mlx5e_rss_get_inner_ft_support(struct mlx5e_rss *rss); +u32 *mlx5e_rss_get_indir_table(struct mlx5e_rss *rss); +void mlx5e_rss_set_indir_actual_size(struct mlx5e_rss *rss, u32 size); u32 mlx5e_rss_get_tirn(struct mlx5e_rss *rss, enum mlx5_traffic_types tt, bool inner); bool mlx5e_rss_valid_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt, bool inner); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c index 92974b11ec75..e940635f5dcb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c @@ -40,13 +40,38 @@ static u32 *get_vhca_ids(struct mlx5e_rx_res *res, int offset) return multi_vhca ? res->rss_vhca_ids + offset : NULL; } -void mlx5e_rx_res_rss_update_num_channels(struct mlx5e_rx_res *res, u32 nch) +/* Updates the indirection table SW shadow, does not update the HW resources yet + */ +void mlx5e_rx_res_rss_update_num_channels(struct mlx5e_rx_res *res, u32 nch, + struct net_device *netdev) { + u32 new_size = mlx5e_rqt_size(res->mdev, nch); int i; - for (i = 0; i < MLX5E_MAX_NUM_RSS; i++) { - if (res->rss[i]) - mlx5e_rss_params_indir_modify_actual_size(res->rss[i], nch); + WARN_ON_ONCE(res->rss_active); + + /* Default context: fold/unfold user-configured table, then update size + * and reset to uniform when unconfigured. + */ + mlx5e_rss_indir_resize(res->rss[0], netdev, new_size); + + /* mlx5e_rss_indir_resize() is a no-op when the table is not + * user-configured. actual_table_size is updated after the resize + * because ethtool_rxfh_indir_resize() uses it as the old size to + * replicate the pattern; updating it first would make the grow a no-op. + * It must be updated before mlx5e_rss_set_indir_uniform() so that + * the uniform fill covers all new entries, not just the old ones. + */ + mlx5e_rss_set_indir_actual_size(res->rss[0], new_size); + if (!netif_is_rxfh_configured(netdev)) + mlx5e_rss_set_indir_uniform(res->rss[0], nch); + + /* Non-default contexts */ + for (i = 1; i < MLX5E_MAX_NUM_RSS; i++) { + if (res->rss[i]) { + mlx5e_rss_ctx_resize(res->rss[i], new_size); + mlx5e_rss_set_indir_actual_size(res->rss[i], new_size); + } } } @@ -209,13 +234,6 @@ static void mlx5e_rx_res_rss_disable(struct mlx5e_rx_res *res) } } -/* Updates the indirection table SW shadow, does not update the HW resources yet */ -void mlx5e_rx_res_rss_set_indir_uniform(struct mlx5e_rx_res *res, unsigned int nch) -{ - WARN_ON_ONCE(res->rss_active); - mlx5e_rss_set_indir_uniform(res->rss[0], nch); -} - void mlx5e_rx_res_rss_get_rxfh(struct mlx5e_rx_res *res, u32 rss_idx, u32 *indir, u8 *key, u8 *hfunc, bool *symmetric) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h index 675780120a20..8fff18d64978 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h @@ -48,7 +48,6 @@ void mlx5e_rx_res_xsk_update(struct mlx5e_rx_res *res, struct mlx5e_channels *ch unsigned int ix, bool xsk); /* Configuration API */ -void mlx5e_rx_res_rss_set_indir_uniform(struct mlx5e_rx_res *res, unsigned int nch); void mlx5e_rx_res_rss_get_rxfh(struct mlx5e_rx_res *res, u32 rss_idx, u32 *indir, u8 *key, u8 *hfunc, bool *symmetric); @@ -68,7 +67,8 @@ int mlx5e_rx_res_rss_destroy(struct mlx5e_rx_res *res, u32 rss_idx); int mlx5e_rx_res_rss_cnt(struct mlx5e_rx_res *res); int mlx5e_rx_res_rss_index(struct mlx5e_rx_res *res, struct mlx5e_rss *rss); struct mlx5e_rss *mlx5e_rx_res_rss_get(struct mlx5e_rx_res *res, u32 rss_idx); -void mlx5e_rx_res_rss_update_num_channels(struct mlx5e_rx_res *res, u32 nch); +void mlx5e_rx_res_rss_update_num_channels(struct mlx5e_rx_res *res, u32 nch, + struct net_device *netdev); /* Workaround for hairpin */ struct mlx5e_rss_params_hash mlx5e_rx_res_get_current_hash(struct mlx5e_rx_res *res); 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 efb34de4cb7a..28cab4bf525c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h @@ -97,10 +97,17 @@ struct mlx5e_tc_flow { struct mlx5e_hairpin_entry *hpe; /* attached hairpin instance */ struct list_head hairpin; /* flows sharing the same hairpin */ struct list_head peer[MLX5_MAX_PORTS]; /* flows with peer flow */ + DECLARE_BITMAP(peer_used, MLX5_MAX_PORTS); /* tracks populated peer + * slots + */ struct list_head unready; /* flows not ready to be offloaded (e.g * due to missing route) */ struct list_head peer_flows; /* flows on peer */ + int peer_index; /* peer-flow index pinned at add time, used at del + * time so removal is independent of LAG state + * changes between add and del. + */ struct net_device *orig_dev; /* netdev adding flow first */ int tmp_entry_index; struct list_head tmp_list; /* temporary flow list used by neigh update */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c index ac8168ebb38c..bca45679e201 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c @@ -744,17 +744,14 @@ void mlx5e_ktls_del_rx(struct net_device *netdev, struct tls_context *tls_ctx) mlx5e_ktls_priv_rx_put(priv_rx); } -bool mlx5e_ktls_rx_handle_resync_list(struct mlx5e_channel *c, int budget) +bool mlx5e_ktls_rx_handle_resync_list(struct mlx5e_icosq *sq, int budget) { struct mlx5e_ktls_offload_context_rx *priv_rx, *tmp; struct mlx5e_ktls_resync_resp *ktls_resync; struct mlx5_wqe_ctrl_seg *db_cseg; - struct mlx5e_icosq *sq; LIST_HEAD(local_list); int i, j; - sq = c->async_icosq; - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) return false; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h index 4022c7e78a2e..93bd383a23d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h @@ -45,13 +45,13 @@ mlx5e_ktls_tx_try_handle_resync_dump_comp(struct mlx5e_txqsq *sq, return false; } -bool mlx5e_ktls_rx_handle_resync_list(struct mlx5e_channel *c, int budget); +bool mlx5e_ktls_rx_handle_resync_list(struct mlx5e_icosq *sq, int budget); static inline bool -mlx5e_ktls_rx_pending_resync_list(struct mlx5e_channel *c, int budget) +mlx5e_ktls_rx_pending_resync_list(struct mlx5e_icosq *sq, int budget) { - return budget && test_bit(MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC, - &c->async_icosq->state); + return test_bit(MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC, &sq->state) && + budget; } static inline void @@ -70,13 +70,13 @@ mlx5e_ktls_tx_try_handle_resync_dump_comp(struct mlx5e_txqsq *sq, } static inline bool -mlx5e_ktls_rx_handle_resync_list(struct mlx5e_channel *c, int budget) +mlx5e_ktls_rx_handle_resync_list(struct mlx5e_icosq *sq, int budget) { return false; } static inline bool -mlx5e_ktls_rx_pending_resync_list(struct mlx5e_channel *c, int budget) +mlx5e_ktls_rx_pending_resync_list(struct mlx5e_icosq *sq, int budget) { return false; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 99a0034b9b20..2f5b626ba33f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -45,6 +45,10 @@ #define LANES_UNKNOWN 0 +#define MLX5E_MAX_INDIR_RQT_SIZE \ + roundup_pow_of_two(MLX5E_MAX_NUM_CHANNELS * \ + MLX5E_UNIFORM_SPREAD_RQT_FACTOR) + void mlx5e_ethtool_get_drvinfo(struct mlx5e_priv *priv, struct ethtool_drvinfo *drvinfo) { @@ -495,11 +499,16 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, { struct mlx5e_params *cur_params = &priv->channels.params; unsigned int count = ch->combined_count; + int new_rqt_size, cur_rqt_size; struct mlx5e_params new_params; + struct mlx5e_rss *rss0; bool arfs_enabled; + bool has_rss_ctxs; bool opened; int err = 0; + ASSERT_RTNL(); + if (!count) { netdev_info(priv->netdev, "%s: combined_count=0 not supported\n", __func__); @@ -509,34 +518,37 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, if (cur_params->num_channels == count) return 0; - mutex_lock(&priv->state_lock); - - if (mlx5e_rx_res_get_current_hash(priv->rx_res).hfunc == ETH_RSS_HASH_XOR) { - unsigned int xor8_max_channels = mlx5e_rqt_max_num_channels_allowed_for_xor8(); - - if (count > xor8_max_channels) { - err = -EINVAL; - netdev_err(priv->netdev, "%s: Requested number of channels (%d) exceeds the maximum allowed by the XOR8 RSS hfunc (%d)\n", - __func__, count, xor8_max_channels); - goto out; - } + new_rqt_size = mlx5e_rqt_size(priv->mdev, count); + /* Validate that all non-default RSS contexts can be resized before + * committing to the channel count change. + * ethtool_rxfh_ctxs_can_resize() acquires rss_lock internally and + * cannot be called under state_lock (rss_lock -> state_lock ordering). + */ + has_rss_ctxs = priv->rx_res && mlx5e_rx_res_rss_cnt(priv->rx_res) > 1; + if (has_rss_ctxs) { + err = ethtool_rxfh_ctxs_can_resize(priv->netdev, new_rqt_size); + if (err) + return err; } - /* If RXFH is configured, changing the channels number is allowed only if - * it does not require resizing the RSS table. This is because the previous - * configuration may no longer be compatible with the new RSS table. - */ - if (netif_is_rxfh_configured(priv->netdev)) { - int cur_rqt_size = mlx5e_rqt_size(priv->mdev, cur_params->num_channels); - int new_rqt_size = mlx5e_rqt_size(priv->mdev, count); + mutex_lock(&priv->state_lock); - if (new_rqt_size != cur_rqt_size) { - err = -EINVAL; - netdev_err(priv->netdev, - "%s: RXFH is configured, block changing channels number that affects RSS table size (new: %d, current: %d)\n", - __func__, new_rqt_size, cur_rqt_size); - goto out; - } + if (!priv->rx_res) { + err = -EINVAL; + goto out; + } + + cur_rqt_size = mlx5e_rqt_size(priv->mdev, cur_params->num_channels); + rss0 = mlx5e_rx_res_rss_get(priv->rx_res, 0); + + if (!ethtool_rxfh_indir_can_resize(priv->netdev, + mlx5e_rss_get_indir_table(rss0), + cur_rqt_size, new_rqt_size)) { + netdev_err(priv->netdev, + "%s: cannot resize RSS table (%u -> %u); reset indirection table to allow this change\n", + __func__, cur_rqt_size, new_rqt_size); + err = -EINVAL; + goto out; } /* Don't allow changing the number of channels if HTB offload is active, @@ -584,6 +596,14 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, out: mutex_unlock(&priv->state_lock); + /* After a successful channel count change that altered the RQT size, + * fold or unfold the indirection tables of all non-default RSS + * contexts. Must run after state_lock is released because + * ethtool_rxfh_ctxs_resize() acquires rss_lock internally. + */ + if (!err && cur_rqt_size != new_rqt_size && has_rss_ctxs) + ethtool_rxfh_ctxs_resize(priv->netdev, new_rqt_size); + return err; } @@ -1501,29 +1521,6 @@ static int mlx5e_get_rxfh(struct net_device *netdev, struct ethtool_rxfh_param * return 0; } -static int mlx5e_rxfh_hfunc_check(struct mlx5e_priv *priv, - const struct ethtool_rxfh_param *rxfh, - struct netlink_ext_ack *extack) -{ - unsigned int count; - - count = priv->channels.params.num_channels; - - if (rxfh->hfunc == ETH_RSS_HASH_XOR) { - unsigned int xor8_max_channels = mlx5e_rqt_max_num_channels_allowed_for_xor8(); - - if (count > xor8_max_channels) { - NL_SET_ERR_MSG_FMT_MOD( - extack, - "Number of channels (%u) exceeds the max for XOR8 RSS (%u)", - count, xor8_max_channels); - return -EINVAL; - } - } - - return 0; -} - static int mlx5e_set_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh, struct netlink_ext_ack *extack) @@ -1535,16 +1532,11 @@ static int mlx5e_set_rxfh(struct net_device *dev, mutex_lock(&priv->state_lock); - err = mlx5e_rxfh_hfunc_check(priv, rxfh, extack); - if (err) - goto unlock; - err = mlx5e_rx_res_rss_set_rxfh(priv->rx_res, rxfh->rss_context, rxfh->indir, rxfh->key, hfunc == ETH_RSS_HASH_NO_CHANGE ? NULL : &hfunc, rxfh->input_xfrm == RXH_XFRM_NO_CHANGE ? NULL : &symmetric); -unlock: mutex_unlock(&priv->state_lock); return err; } @@ -1561,10 +1553,6 @@ static int mlx5e_create_rxfh_context(struct net_device *dev, mutex_lock(&priv->state_lock); - err = mlx5e_rxfh_hfunc_check(priv, rxfh, extack); - if (err) - goto unlock; - err = mlx5e_rx_res_rss_init(priv->rx_res, rxfh->rss_context, priv->channels.params.num_channels); if (err) @@ -1604,16 +1592,11 @@ static int mlx5e_modify_rxfh_context(struct net_device *dev, mutex_lock(&priv->state_lock); - err = mlx5e_rxfh_hfunc_check(priv, rxfh, extack); - if (err) - goto unlock; - err = mlx5e_rx_res_rss_set_rxfh(priv->rx_res, rxfh->rss_context, rxfh->indir, rxfh->key, hfunc == ETH_RSS_HASH_NO_CHANGE ? NULL : &hfunc, rxfh->input_xfrm == RXH_XFRM_NO_CHANGE ? NULL : &symmetric); -unlock: mutex_unlock(&priv->state_lock); return err; } @@ -2736,6 +2719,9 @@ const struct ethtool_ops mlx5e_ethtool_ops = { .rxfh_per_ctx_fields = true, .rxfh_per_ctx_key = true, .rxfh_max_num_contexts = MLX5E_MAX_NUM_RSS, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM | + ETHTOOL_OP_NEEDS_RTNL_SPFLAGS, .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USE_ADAPTIVE | @@ -2743,6 +2729,7 @@ const struct ethtool_ops mlx5e_ethtool_ops = { .supported_input_xfrm = RXH_XFRM_SYM_OR_XOR, .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | ETHTOOL_RING_USE_HDS_THRS, + .rxfh_indir_space = MLX5E_MAX_INDIR_RQT_SIZE, .get_drvinfo = mlx5e_get_drvinfo, .get_link = ethtool_op_get_link, .get_link_ext_state = mlx5e_get_link_ext_state, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 8f2b3abe0092..775f0c6e55c9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3296,12 +3296,9 @@ static int mlx5e_num_channels_changed(struct mlx5e_priv *priv) } /* This function may be called on attach, before priv->rx_res is created. */ - if (priv->rx_res) { - mlx5e_rx_res_rss_update_num_channels(priv->rx_res, count); - - if (!netif_is_rxfh_configured(priv->netdev)) - mlx5e_rx_res_rss_set_indir_uniform(priv->rx_res, count); - } + if (priv->rx_res) + mlx5e_rx_res_rss_update_num_channels(priv->rx_res, count, + netdev); return 0; } @@ -4145,13 +4142,15 @@ static void mlx5e_nic_set_rx_mode(struct mlx5e_priv *priv) queue_work(priv->wq, &priv->set_rx_mode_work); } -static void mlx5e_set_rx_mode(struct net_device *dev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int mlx5e_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_priv *priv = netdev_priv(dev); mlx5e_fs_set_rx_mode_work(priv->fs, dev, uc, mc); + + return 0; } static int mlx5e_set_mac(struct net_device *netdev, void *addr) @@ -5500,6 +5499,11 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i, stats->bytes = rq_stats->bytes + xskrq_stats->bytes; stats->alloc_fail = rq_stats->buff_alloc_err + xskrq_stats->buff_alloc_err; + + stats->hw_gro_packets = rq_stats->gro_skbs + xskrq_stats->gro_skbs; + stats->hw_gro_wire_packets = + rq_stats->gro_packets + xskrq_stats->gro_packets; + stats->hw_gro_wire_bytes = rq_stats->gro_bytes + xskrq_stats->gro_bytes; } static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i, @@ -5518,6 +5522,15 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i, sq_stats = priv->txq2sq_stats[i]; stats->packets = sq_stats->packets; stats->bytes = sq_stats->bytes; + + stats->hw_gso_packets = + sq_stats->tso_packets + sq_stats->tso_inner_packets; + stats->hw_gso_bytes = sq_stats->tso_bytes + sq_stats->tso_inner_bytes; + + stats->csum_none = sq_stats->csum_none; + + stats->stop = sq_stats->stopped; + stats->wake = sq_stats->wake; } static void mlx5e_get_base_stats(struct net_device *dev, @@ -5532,6 +5545,9 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->packets = 0; rx->bytes = 0; rx->alloc_fail = 0; + rx->hw_gro_packets = 0; + rx->hw_gro_wire_packets = 0; + rx->hw_gro_wire_bytes = 0; for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) { struct netdev_queue_stats_rx rx_i = {0}; @@ -5541,6 +5557,9 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->packets += rx_i.packets; rx->bytes += rx_i.bytes; rx->alloc_fail += rx_i.alloc_fail; + rx->hw_gro_packets += rx_i.hw_gro_packets; + rx->hw_gro_wire_packets += rx_i.hw_gro_wire_packets; + rx->hw_gro_wire_bytes += rx_i.hw_gro_wire_bytes; } /* always report PTP RX stats from base as there is no @@ -5552,11 +5571,19 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->packets += rq_stats->packets; rx->bytes += rq_stats->bytes; + rx->hw_gro_packets += rq_stats->gro_skbs; + rx->hw_gro_wire_packets += rq_stats->gro_packets; + rx->hw_gro_wire_bytes += rq_stats->gro_bytes; } } tx->packets = 0; tx->bytes = 0; + tx->hw_gso_packets = 0; + tx->hw_gso_bytes = 0; + tx->csum_none = 0; + tx->stop = 0; + tx->wake = 0; for (i = 0; i < priv->stats_nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; @@ -5583,6 +5610,13 @@ static void mlx5e_get_base_stats(struct net_device *dev, tx->packets += sq_stats->packets; tx->bytes += sq_stats->bytes; + tx->hw_gso_packets += sq_stats->tso_packets + + sq_stats->tso_inner_packets; + tx->hw_gso_bytes += sq_stats->tso_bytes + + sq_stats->tso_inner_bytes; + tx->csum_none += sq_stats->csum_none; + tx->stop += sq_stats->stopped; + tx->wake += sq_stats->wake; } } @@ -5601,6 +5635,13 @@ static void mlx5e_get_base_stats(struct net_device *dev, tx->packets += sq_stats->packets; tx->bytes += sq_stats->bytes; + tx->hw_gso_packets += sq_stats->tso_packets + + sq_stats->tso_inner_packets; + tx->hw_gso_bytes += sq_stats->tso_bytes + + sq_stats->tso_inner_bytes; + tx->csum_none += sq_stats->csum_none; + tx->stop += sq_stats->stopped; + tx->wake += sq_stats->wake; } } } @@ -5924,7 +5965,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netdev->priv_flags |= IFF_UNICAST_FLT; - netdev->netmem_tx = true; + netdev->netmem_tx = NETMEM_TX_DMA; netif_set_tso_max_size(netdev, GSO_MAX_SIZE); mlx5e_set_xdp_feature(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index ba6c0f38cc73..1a8a19f980d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -418,6 +418,8 @@ static const struct ethtool_ops mlx5e_rep_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USE_ADAPTIVE, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM, .get_drvinfo = mlx5e_rep_get_drvinfo, .get_link = ethtool_op_get_link, .get_strings = mlx5e_rep_get_strings, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 5b60aa47c75b..6fbc0441c4b8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -1912,7 +1912,6 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w u32 page_idx) { struct mlx5e_frag_page *frag_page = &wi->alloc_units.frag_pages[page_idx]; - u16 headlen = min_t(u16, MLX5E_RX_MAX_HEAD, cqe_bcnt); struct mlx5e_frag_page *head_page = frag_page; struct mlx5e_frag_page *linear_page = NULL; struct mlx5e_xdp_buff *mxbuf = &rq->mxbuf; @@ -1923,11 +1922,12 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w unsigned int truesize = 0; u32 pg_consumed_bytes; struct bpf_prog *prog; + void *va, *head_addr; struct sk_buff *skb; u32 linear_frame_sz; u16 linear_data_len; u16 linear_hr; - void *va; + u16 headlen; if (unlikely(cqe_bcnt > rq->hw_mtu)) { u8 lro_num_seg = get_cqe_lro_num_seg(cqe); @@ -1940,9 +1940,11 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w prog = rcu_dereference(rq->xdp_prog); + head_addr = netmem_address(head_page->netmem) + head_offset; + if (prog) { /* area for bpf_xdp_[store|load]_bytes */ - net_prefetchw(netmem_address(frag_page->netmem) + frag_offset); + net_prefetchw(head_addr); va = mlx5e_mpwqe_get_linear_page_frag(rq); if (!va) { @@ -1956,6 +1958,8 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w linear_frame_sz = MLX5_SKB_FRAG_SZ(linear_hr + MLX5E_RX_MAX_HEAD); linear_page = &rq->mpwqe.linear_info->frag_page; } else { + dma_addr_t addr; + skb = napi_alloc_skb(rq->cq.napi, ALIGN(MLX5E_RX_MAX_HEAD, sizeof(long))); if (unlikely(!skb)) { @@ -1967,6 +1971,14 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w net_prefetchw(va); /* xdp_frame data area */ net_prefetchw(skb->data); + headlen = min(MLX5E_RX_MAX_HEAD, cqe_bcnt); + addr = page_pool_get_dma_addr_netmem(head_page->netmem); + dma_sync_single_for_cpu(rq->pdev, addr + head_offset, + ALIGN(headlen, sizeof(long)), + rq->buff.map_dir); + + headlen = eth_get_headlen(rq->netdev, head_addr, headlen); + frag_offset += headlen; byte_cnt -= headlen; linear_hr = skb_headroom(skb); @@ -2051,13 +2063,11 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w pagep->frags++; while (++pagep < frag_page); - headlen = min_t(u16, MLX5E_RX_MAX_HEAD - len, - skb->data_len); - __pskb_pull_tail(skb, headlen); + if (len < ETH_HLEN) + __pskb_pull_tail(skb, min(ETH_HLEN - len, + skb->data_len)); } } else { - dma_addr_t addr; - if (xdp_buff_has_frags(&mxbuf->xdp)) { struct mlx5e_frag_page *pagep; @@ -2071,10 +2081,11 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w pagep->frags++; while (++pagep < frag_page); } + /* copy header */ - addr = page_pool_get_dma_addr_netmem(head_page->netmem); - mlx5e_copy_skb_header(rq, skb, head_page->netmem, addr, - head_offset, head_offset, headlen); + skb_copy_to_linear_data(skb, head_addr, + ALIGN(headlen, sizeof(long))); + /* skb linear part was allocated with headlen and aligned to long */ skb->tail += headlen; skb->len += headlen; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 1a3ecf073913..7f33261ba655 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -496,8 +496,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) struct page_pool *pool = c->rq.page_pool; struct page_pool_stats stats = { 0 }; - if (!page_pool_get_stats(pool, &stats)) - return; + page_pool_get_stats(pool, &stats); rq_stats->pp_alloc_fast = stats.alloc_stats.fast; rq_stats->pp_alloc_slow = stats.alloc_stats.slow; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index a9001d1c902f..910492eb51f2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -71,6 +71,7 @@ #include #include "lag/lag.h" #include "lag/mp.h" +#include "lib/sd.h" #define MLX5E_TC_TABLE_NUM_GROUPS 4 #define MLX5E_TC_TABLE_MAX_GROUP_SIZE BIT(18) @@ -2128,10 +2129,11 @@ static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow, mutex_lock(&esw->offloads.peer_mutex); list_del(&flow->peer[peer_index]); + clear_bit(peer_index, flow->peer_used); mutex_unlock(&esw->offloads.peer_mutex); list_for_each_entry_safe(peer_flow, tmp, &flow->peer_flows, peer_flows) { - if (peer_index != mlx5_lag_get_dev_seq(peer_flow->priv->mdev)) + if (peer_index != peer_flow->peer_index) continue; list_del(&peer_flow->peer_flows); @@ -2147,16 +2149,10 @@ static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow, static void mlx5e_tc_del_fdb_peers_flow(struct mlx5e_tc_flow *flow) { - struct mlx5_devcom_comp_dev *devcom; - struct mlx5_devcom_comp_dev *pos; - struct mlx5_eswitch *peer_esw; int i; - devcom = flow->priv->mdev->priv.eswitch->devcom; - mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) { - i = mlx5_lag_get_dev_seq(peer_esw->dev); + for_each_set_bit(i, flow->peer_used, MLX5_MAX_PORTS) mlx5e_tc_del_fdb_peer_flow(flow, i); - } } static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, @@ -4201,9 +4197,26 @@ static bool is_lag_dev(struct mlx5e_priv *priv, same_hw_reps(priv, peer_netdev)); } +static bool is_sd_eligible(struct mlx5e_priv *priv, + struct net_device *peer_netdev) +{ + struct mlx5e_priv *peer_priv; + + peer_priv = netdev_priv(peer_netdev); + return same_hw_reps(priv, peer_netdev) && + mlx5_lag_is_sd(priv->mdev) && + (mlx5_sd_get_primary(priv->mdev) == + mlx5_sd_get_primary(peer_priv->mdev)); +} + static bool is_multiport_eligible(struct mlx5e_priv *priv, struct net_device *out_dev) { - return same_hw_reps(priv, out_dev) && mlx5_lag_is_mpesw(priv->mdev); + struct mlx5_core_dev *primary = mlx5_sd_get_primary(priv->mdev); + + if (!primary) + return false; + + return same_hw_reps(priv, out_dev) && mlx5_lag_is_mpesw(primary); } bool mlx5e_is_valid_eswitch_fwd_dev(struct mlx5e_priv *priv, @@ -4212,6 +4225,9 @@ bool mlx5e_is_valid_eswitch_fwd_dev(struct mlx5e_priv *priv, if (is_merged_eswitch_vfs(priv, out_dev)) return true; + if (is_sd_eligible(priv, out_dev)) + return true; + if (is_multiport_eligible(priv, out_dev)) return true; @@ -4356,7 +4372,7 @@ static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv, return &tc->ht; } -static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow) +static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow, bool *is_sd) { struct mlx5_esw_flow_attr *esw_attr = flow->attr->esw_attr; struct mlx5_flow_attr *attr = flow->attr; @@ -4377,6 +4393,13 @@ static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow) if (mlx5_lag_is_mpesw(esw_attr->in_mdev)) return true; + if (mlx5_lag_is_sd(esw_attr->in_mdev) && + !mlx5_sd_is_primary(esw_attr->in_mdev)) { + if (!mlx5_lag_is_mpesw(mlx5_sd_get_primary(esw_attr->in_mdev))) + *is_sd = true; + return true; + } + return false; } @@ -4614,10 +4637,12 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f, goto out; } + peer_flow->peer_index = i; list_add_tail(&peer_flow->peer_flows, &flow->peer_flows); flow_flag_set(flow, DUP); mutex_lock(&esw->offloads.peer_mutex); list_add_tail(&flow->peer[i], &esw->offloads.peer_flows[i]); + set_bit(i, flow->peer_used); mutex_unlock(&esw->offloads.peer_mutex); out: @@ -4632,19 +4657,26 @@ mlx5e_add_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow **__flow) { struct mlx5_devcom_comp_dev *devcom = priv->mdev->priv.eswitch->devcom, *pos; + struct netlink_ext_ack *extack = f->common.extack; struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *in_rep = rpriv->rep; struct mlx5_core_dev *in_mdev = priv->mdev; struct mlx5_eswitch *peer_esw; struct mlx5e_tc_flow *flow; + bool is_sd = false; int err; + if (mlx5_lag_is_sd(in_mdev) && !mlx5_lag_is_active(in_mdev)) { + NL_SET_ERR_MSG_MOD(extack, "SD shared FDB not yet active"); + return -EOPNOTSUPP; + } + flow = __mlx5e_add_fdb_flow(priv, f, flow_flags, filter_dev, in_rep, in_mdev); if (IS_ERR(flow)) return PTR_ERR(flow); - if (!is_peer_flow_needed(flow)) { + if (!is_peer_flow_needed(flow, &is_sd)) { *__flow = flow; return 0; } @@ -4655,6 +4687,15 @@ mlx5e_add_fdb_flow(struct mlx5e_priv *priv, } mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) { + if (is_sd) { + /* SD shared FDB: only the matching SD primary. */ + if (mlx5_sd_get_primary(in_mdev) != + mlx5_sd_get_primary(peer_esw->dev)) + continue; + } else { + if (!mlx5_sd_is_primary(peer_esw->dev)) + continue; + } err = mlx5e_tc_add_fdb_peer_flow(f, flow, flow_flags, peer_esw); if (err) goto peer_clean; @@ -5394,8 +5435,6 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv) { const size_t sz_enc_opts = sizeof(struct tunnel_match_enc_opts); u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES]; - struct mlx5_devcom_match_attr attr = {}; - struct netdev_phys_item_id ppid; struct mlx5e_rep_priv *rpriv; struct mapping_ctx *mapping; struct mlx5_eswitch *esw; @@ -5456,14 +5495,6 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv) goto err_action_counter; } - err = netif_get_port_parent_id(priv->netdev, &ppid, false); - if (!err) { - memcpy(&attr.key.buf, &ppid.id, ppid.id_len); - attr.flags = MLX5_DEVCOM_MATCH_FLAGS_NS; - attr.net = mlx5_core_net(esw->dev); - mlx5_esw_offloads_devcom_init(esw, &attr); - } - return 0; err_action_counter: @@ -5484,16 +5515,6 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv) void mlx5e_tc_esw_cleanup(struct mlx5_rep_uplink_priv *uplink_priv) { - struct mlx5e_rep_priv *rpriv; - struct mlx5_eswitch *esw; - struct mlx5e_priv *priv; - - rpriv = container_of(uplink_priv, struct mlx5e_rep_priv, uplink_priv); - priv = netdev_priv(rpriv->netdev); - esw = priv->mdev->priv.eswitch; - - mlx5_esw_offloads_devcom_cleanup(esw); - 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_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 9f0272649fa1..0b5e600e4a6a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -164,14 +164,14 @@ mlx5e_tx_get_gso_ihs(struct mlx5e_txqsq *sq, struct sk_buff *skb) else ihs = skb_inner_tcp_all_headers(skb); stats->tso_inner_packets++; - stats->tso_inner_bytes += skb->len - ihs; + stats->tso_inner_bytes += skb->len; } else { if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) ihs = skb_transport_offset(skb) + sizeof(struct udphdr); else ihs = skb_tcp_all_headers(skb); stats->tso_packets++; - stats->tso_bytes += skb->len - ihs; + stats->tso_bytes += skb->len; } return ihs; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index e90c6c6df835..185105606469 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -166,14 +166,13 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) if (unlikely(!budget)) goto out; - if (c->xdpsq) - busy |= mlx5e_poll_xdpsq_cq(&c->xdpsq->cq); - - if (c->xdp) + if (c->xdp) { + if (c->xdpsq) + busy |= mlx5e_poll_xdpsq_cq(&c->xdpsq->cq); busy |= mlx5e_poll_xdpsq_cq(&c->rq_xdpsq.cq); - - if (xsk_open) - work_done = mlx5e_poll_rx_cq(&xskrq->cq, budget); + if (xsk_open) + work_done += mlx5e_poll_rx_cq(&xskrq->cq, budget); + } if (likely(budget - work_done)) work_done += mlx5e_poll_rx_cq(&rq->cq, budget - work_done); @@ -190,20 +189,22 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) &aicosq->state); /* Keep after async ICOSQ CQ poll */ - if (unlikely(mlx5e_ktls_rx_pending_resync_list(c, budget))) - busy |= mlx5e_ktls_rx_handle_resync_list(c, budget); + if (unlikely(mlx5e_ktls_rx_pending_resync_list(aicosq, budget))) + busy |= mlx5e_ktls_rx_handle_resync_list(aicosq, + budget); + + if (xsk_open) { + busy |= mlx5e_poll_xdpsq_cq(&xsksq->cq); + busy_xsk |= mlx5e_napi_xsk_post(xsksq, xskrq); + + busy |= busy_xsk; + } } busy |= INDIRECT_CALL_2(rq->post_wqes, mlx5e_post_rx_mpwqes, mlx5e_post_rx_wqes, rq); - if (xsk_open) { - busy |= mlx5e_poll_xdpsq_cq(&xsksq->cq); - busy_xsk |= mlx5e_napi_xsk_post(xsksq, xskrq); - } - - busy |= busy_xsk; if (busy) { if (likely(mlx5e_channel_no_affinity_change(c))) { @@ -247,9 +248,9 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) mlx5e_cq_arm(&xsksq->cq); mlx5e_cq_arm(&xskrq->cq); } + if (c->xdpsq) + mlx5e_cq_arm(&c->xdpsq->cq); } - if (c->xdpsq) - mlx5e_cq_arm(&c->xdpsq->cq); if (unlikely(aff_change && busy_xsk)) { mlx5e_trigger_napi_async_icosq(c); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 22a637111aa2..d11ec263d53c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -886,8 +886,7 @@ static int comp_irq_request_sf(struct mlx5_core_dev *dev, u16 vecidx) return -ENOMEM; af_desc->is_managed = false; - cpumask_copy(&af_desc->mask, cpu_online_mask); - cpumask_andnot(&af_desc->mask, &af_desc->mask, &table->used_cpus); + cpumask_andnot(&af_desc->mask, cpu_online_mask, &table->used_cpus); irq = mlx5_irq_affinity_request(dev, pool, af_desc); if (IS_ERR(irq)) { kvfree(af_desc); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c index 250af09b5af2..ca249b50f830 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c @@ -23,7 +23,7 @@ int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev, u16 vport, return mlx5_cmd_exec_in(dev, modify_vport_state, in); } -static void mlx5_esw_destroy_esw_vport(struct mlx5_core_dev *dev, u16 vport) +void mlx5_esw_destroy_esw_vport(struct mlx5_core_dev *dev, u16 vport) { u32 in[MLX5_ST_SZ_DW(destroy_esw_vport_in)] = {}; @@ -34,8 +34,8 @@ static void mlx5_esw_destroy_esw_vport(struct mlx5_core_dev *dev, u16 vport) mlx5_cmd_exec_in(dev, destroy_esw_vport, in); } -static int mlx5_esw_create_esw_vport(struct mlx5_core_dev *dev, u16 vhca_id, - u16 *vport_num) +int mlx5_esw_create_esw_vport(struct mlx5_core_dev *dev, u16 vhca_id, + u16 *vport_num) { u32 out[MLX5_ST_SZ_DW(create_esw_vport_out)] = {}; u32 in[MLX5_ST_SZ_DW(create_esw_vport_in)] = {}; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c index e1d11326af1b..6e50311faa27 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c @@ -13,9 +13,11 @@ mlx5_esw_get_port_parent_id(struct mlx5_core_dev *dev, struct netdev_phys_item_i static bool mlx5_esw_devlink_port_supported(struct mlx5_eswitch *esw, u16 vport_num) { - return (mlx5_core_is_ecpf(esw->dev) && vport_num == MLX5_VPORT_PF) || + return (mlx5_core_is_ecpf(esw->dev) && + vport_num == MLX5_VPORT_HOST_PF) || mlx5_eswitch_is_vf_vport(esw, vport_num) || - mlx5_core_is_ec_vf_vport(esw->dev, vport_num); + mlx5_core_is_ec_vf_vport(esw->dev, vport_num) || + mlx5_esw_is_spf_vport(esw, vport_num); } static void mlx5_esw_offloads_pf_vf_devlink_port_attrs_set(struct mlx5_eswitch *esw, @@ -33,9 +35,11 @@ static void mlx5_esw_offloads_pf_vf_devlink_port_attrs_set(struct mlx5_eswitch * pfnum = PCI_FUNC(dev->pdev->devfn); external = mlx5_core_is_ecpf_esw_manager(dev); if (external) - controller_num = dev->priv.eswitch->offloads.host_number + 1; + controller_num = mlx5_esw_get_hpf_host_number(dev) + 1; - if (vport_num == MLX5_VPORT_PF) { + if (vport_num == MLX5_VPORT_HOST_PF) { + if (external) + pfnum = mlx5_esw_get_hpf_pf_num(dev); memcpy(dl_port->attrs.switch_id.id, ppid.id, ppid.id_len); dl_port->attrs.switch_id.id_len = ppid.id_len; devlink_port_attrs_pci_pf_set(dl_port, controller_num, pfnum, external); @@ -48,6 +52,8 @@ static void mlx5_esw_offloads_pf_vf_devlink_port_attrs_set(struct mlx5_eswitch * if (vport->adjacent) { func_id = vport->adj_info.function_id; pfnum = vport->adj_info.parent_pci_devfn; + } else if (external) { + pfnum = mlx5_esw_get_hpf_pf_num(dev); } devlink_port_attrs_pci_vf_set(dl_port, controller_num, pfnum, @@ -59,6 +65,16 @@ static void mlx5_esw_offloads_pf_vf_devlink_port_attrs_set(struct mlx5_eswitch * dl_port->attrs.switch_id.id_len = ppid.id_len; devlink_port_attrs_pci_vf_set(dl_port, 0, pfnum, vport_num - base_vport, false); + } else if (mlx5_esw_is_spf_vport(esw, vport_num)) { + int spf_idx = mlx5_esw_spf_vport_to_idx(esw, vport_num); + + controller_num = esw->esw_funcs.spfs[spf_idx].host_number + 1; + pfnum = esw->esw_funcs.spfs[spf_idx].pf_num; + + memcpy(dl_port->attrs.switch_id.id, ppid.id, ppid.id_len); + dl_port->attrs.switch_id.id_len = ppid.id_len; + devlink_port_attrs_pci_pf_set(dl_port, controller_num, pfnum, + true); } } @@ -120,7 +136,7 @@ static void mlx5_esw_offloads_sf_devlink_port_attrs_set(struct mlx5_eswitch *esw struct netdev_phys_item_id ppid = {}; u16 pfnum; - pfnum = PCI_FUNC(dev->pdev->devfn); + pfnum = mlx5_esw_sf_controller_to_pfnum(dev, controller); mlx5_esw_get_port_parent_id(dev, &ppid); memcpy(dl_port->attrs.switch_id.id, &ppid.id[0], ppid.id_len); dl_port->attrs.switch_id.id_len = ppid.id_len; @@ -160,14 +176,28 @@ static const struct devlink_port_ops mlx5_esw_dl_sf_port_ops = { }; static int mlx5_esw_devlink_port_res_register(struct mlx5_eswitch *esw, - struct devlink_port *dl_port) + struct devlink_port *dl_port, + u16 vport_num) { struct devlink_resource_size_params size_params; struct mlx5_core_dev *dev = esw->dev; u16 max_sfs, sf_base_id; int err; - err = mlx5_esw_sf_max_hpf_functions(dev, &max_sfs, &sf_base_id); + if (vport_num != MLX5_VPORT_HOST_PF && + !mlx5_esw_is_spf_vport(esw, vport_num)) + return 0; + + if (vport_num == MLX5_VPORT_HOST_PF) { + err = mlx5_esw_sf_max_hpf_functions(dev, &max_sfs, + &sf_base_id); + } else { + int spf_idx = mlx5_esw_spf_vport_to_idx(esw, vport_num); + + err = mlx5_esw_sf_max_spf_functions(dev, spf_idx, &max_sfs, + &sf_base_id); + } + if (err) return err; @@ -216,14 +246,11 @@ int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx if (err) goto rate_err; - if (vport_num == MLX5_VPORT_PF) { - err = mlx5_esw_devlink_port_res_register(esw, - &dl_port->dl_port); - if (err) - mlx5_core_dbg(dev, - "Failed to register port resources: %d\n", - err); - } + err = mlx5_esw_devlink_port_res_register(esw, &dl_port->dl_port, + vport_num); + if (err) + mlx5_core_dbg(dev, "Failed to register port resources: %d\n", + err); return 0; @@ -252,5 +279,10 @@ struct devlink_port *mlx5_esw_offloads_devlink_port(struct mlx5_eswitch *esw, u1 struct mlx5_vport *vport; vport = mlx5_eswitch_get_vport(esw, vport_num); - return IS_ERR(vport) ? ERR_CAST(vport) : &vport->dl_port->dl_port; + if (IS_ERR(vport)) + return ERR_CAST(vport); + if (!vport->dl_port) + return ERR_PTR(-ENODEV); + + return &vport->dl_port->dl_port; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec.c index da10e04777cf..2b5765ab60d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec.c @@ -12,7 +12,7 @@ static int esw_ipsec_vf_query_generic(struct mlx5_core_dev *dev, u16 vport_num, void *hca_cap, *query_cap; int err; - if (!MLX5_CAP_GEN(dev, vhca_resource_manager)) + if (!MLX5_CAP_GEN(dev, vport_group_manager)) return -EOPNOTSUPP; if (!mlx5_esw_ipsec_vf_offload_supported(dev)) { @@ -81,38 +81,25 @@ int mlx5_esw_ipsec_vf_offload_get(struct mlx5_core_dev *dev, struct mlx5_vport * static int esw_ipsec_vf_set_generic(struct mlx5_core_dev *dev, u16 vport_num, bool ipsec_ofld) { int query_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); - int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in); - void *hca_cap, *query_cap, *cap; + void *query_cap, *hca_caps; int ret; if (!MLX5_CAP_GEN(dev, vhca_resource_manager)) return -EOPNOTSUPP; query_cap = kvzalloc(query_sz, GFP_KERNEL); - hca_cap = kvzalloc(set_sz, GFP_KERNEL); - if (!hca_cap || !query_cap) { - ret = -ENOMEM; - goto free; - } + if (!query_cap) + return -ENOMEM; ret = mlx5_vport_get_other_func_general_cap(dev, vport_num, query_cap); if (ret) goto free; - cap = MLX5_ADDR_OF(set_hca_cap_in, hca_cap, capability); - memcpy(cap, MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability), - MLX5_UN_SZ_BYTES(hca_cap_union)); - MLX5_SET(cmd_hca_cap, cap, ipsec_offload, ipsec_ofld); + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability); + MLX5_SET(cmd_hca_cap, hca_caps, ipsec_offload, ipsec_ofld); - MLX5_SET(set_hca_cap_in, hca_cap, opcode, MLX5_CMD_OP_SET_HCA_CAP); - MLX5_SET(set_hca_cap_in, hca_cap, other_function, 1); - MLX5_SET(set_hca_cap_in, hca_cap, function_id, vport_num); - - MLX5_SET(set_hca_cap_in, hca_cap, op_mod, - MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1); - ret = mlx5_cmd_exec_in(dev, set_hca_cap, hca_cap); + ret = mlx5_vport_set_other_func_general_cap(dev, hca_caps, vport_num); free: - kvfree(hca_cap); kvfree(query_cap); return ret; } @@ -121,49 +108,37 @@ static int esw_ipsec_vf_set_bytype(struct mlx5_core_dev *dev, struct mlx5_vport bool enable, enum esw_vport_ipsec_offload type) { int query_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); - int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in); - void *hca_cap, *query_cap, *cap; + void *query_cap, *hca_caps; int ret; if (!MLX5_CAP_GEN(dev, vhca_resource_manager)) return -EOPNOTSUPP; query_cap = kvzalloc(query_sz, GFP_KERNEL); - hca_cap = kvzalloc(set_sz, GFP_KERNEL); - if (!hca_cap || !query_cap) { - ret = -ENOMEM; - goto free; - } + if (!query_cap) + return -ENOMEM; ret = mlx5_vport_get_other_func_cap(dev, vport->vport, query_cap, MLX5_CAP_IPSEC); if (ret) goto free; - cap = MLX5_ADDR_OF(set_hca_cap_in, hca_cap, capability); - memcpy(cap, MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability), - MLX5_UN_SZ_BYTES(hca_cap_union)); + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability); switch (type) { case MLX5_ESW_VPORT_IPSEC_CRYPTO_OFFLOAD: - MLX5_SET(ipsec_cap, cap, ipsec_crypto_offload, enable); + MLX5_SET(ipsec_cap, hca_caps, ipsec_crypto_offload, enable); break; case MLX5_ESW_VPORT_IPSEC_PACKET_OFFLOAD: - MLX5_SET(ipsec_cap, cap, ipsec_full_offload, enable); + MLX5_SET(ipsec_cap, hca_caps, ipsec_full_offload, enable); break; default: ret = -EOPNOTSUPP; goto free; } - MLX5_SET(set_hca_cap_in, hca_cap, opcode, MLX5_CMD_OP_SET_HCA_CAP); - MLX5_SET(set_hca_cap_in, hca_cap, other_function, 1); - MLX5_SET(set_hca_cap_in, hca_cap, function_id, vport->vport); - - MLX5_SET(set_hca_cap_in, hca_cap, op_mod, - MLX5_SET_HCA_CAP_OP_MOD_IPSEC << 1); - ret = mlx5_cmd_exec_in(dev, set_hca_cap, hca_cap); + ret = mlx5_vport_set_other_func_cap(dev, hca_caps, vport->vport, + MLX5_SET_HCA_CAP_OP_MOD_IPSEC); free: - kvfree(hca_cap); kvfree(query_cap); return ret; } @@ -171,34 +146,24 @@ static int esw_ipsec_vf_set_bytype(struct mlx5_core_dev *dev, struct mlx5_vport static int esw_ipsec_vf_crypto_aux_caps_set(struct mlx5_core_dev *dev, u16 vport_num, bool enable) { int query_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); - int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in); - struct mlx5_eswitch *esw = dev->priv.eswitch; - void *hca_cap, *query_cap, *cap; + void *query_cap, *hca_caps; int ret; query_cap = kvzalloc(query_sz, GFP_KERNEL); - hca_cap = kvzalloc(set_sz, GFP_KERNEL); - if (!hca_cap || !query_cap) { - ret = -ENOMEM; - goto free; - } + if (!query_cap) + return -ENOMEM; ret = mlx5_vport_get_other_func_cap(dev, vport_num, query_cap, MLX5_CAP_ETHERNET_OFFLOADS); if (ret) goto free; - cap = MLX5_ADDR_OF(set_hca_cap_in, hca_cap, capability); - memcpy(cap, MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability), - MLX5_UN_SZ_BYTES(hca_cap_union)); - MLX5_SET(per_protocol_networking_offload_caps, cap, insert_trailer, enable); - MLX5_SET(set_hca_cap_in, hca_cap, opcode, MLX5_CMD_OP_SET_HCA_CAP); - MLX5_SET(set_hca_cap_in, hca_cap, other_function, 1); - MLX5_SET(set_hca_cap_in, hca_cap, function_id, vport_num); - MLX5_SET(set_hca_cap_in, hca_cap, op_mod, - MLX5_SET_HCA_CAP_OP_MOD_ETHERNET_OFFLOADS << 1); - ret = mlx5_cmd_exec_in(esw->dev, set_hca_cap, hca_cap); + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability); + MLX5_SET(per_protocol_networking_offload_caps, hca_caps, + insert_trailer, enable); + + ret = mlx5_vport_set_other_func_cap(dev, hca_caps, vport_num, + MLX5_SET_HCA_CAP_OP_MOD_ETHERNET_OFFLOADS); free: - kvfree(hca_cap); kvfree(query_cap); return ret; } @@ -209,7 +174,7 @@ static int esw_ipsec_vf_offload_set_bytype(struct mlx5_eswitch *esw, struct mlx5 struct mlx5_core_dev *dev = esw->dev; int err; - if (vport->vport == MLX5_VPORT_PF) + if (!mlx5_eswitch_is_vf_vport(esw, vport->vport)) return -EOPNOTSUPP; if (type == MLX5_ESW_VPORT_IPSEC_CRYPTO_OFFLOAD) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 236f89a6483a..a0e2ca87b8d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -799,7 +799,7 @@ static int mlx5_esw_vport_caps_get(struct mlx5_eswitch *esw, struct mlx5_vport * void *hca_caps; int err; - if (!MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) + if (!MLX5_CAP_GEN(esw->dev, vport_group_manager)) return 0; query_ctx = kzalloc(query_out_sz, GFP_KERNEL); @@ -845,6 +845,40 @@ bool mlx5_esw_vport_vhca_id(struct mlx5_eswitch *esw, u16 vportn, u16 *vhca_id) return true; } +static enum mlx5_func_type +esw_vport_to_func_type(struct mlx5_eswitch *esw, struct mlx5_vport *vport) +{ + u16 vport_num = vport->vport; + + if (vport_num == MLX5_VPORT_HOST_PF) + return MLX5_HOST_PF; + if (xa_get_mark(&esw->vports, vport_num, MLX5_ESW_VPT_SF)) + return MLX5_SF; + if (xa_get_mark(&esw->vports, vport_num, MLX5_ESW_VPT_VF)) + return MLX5_VF; + if (mlx5_esw_is_spf_vport(esw, vport_num)) + return MLX5_SPF; + return MLX5_EC_VF; +} + +u16 mlx5_esw_vhca_id_to_func_type(struct mlx5_core_dev *dev, u16 vhca_id) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + void *entry; + + if (vhca_id == MLX5_CAP_GEN(dev, vhca_id)) + return MLX5_SELF; + + if (!esw) + return MLX5_FUNC_TYPE_NONE; + + entry = xa_load(&esw->vhca_type_map, vhca_id); + if (entry) + return xa_to_value(entry); + + return MLX5_FUNC_TYPE_NONE; +} + static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { bool vst_mode_steering = esw_vst_mode_is_steering(esw); @@ -937,7 +971,7 @@ int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, /* Sync with current vport context */ vport->enabled_events = enabled_events; vport->enabled = true; - if (vport->vport != MLX5_VPORT_PF && + if (mlx5_eswitch_is_vf_vport(esw, vport_num) && (vport->info.ipsec_crypto_enabled || vport->info.ipsec_packet_enabled)) esw->enabled_ipsec_vf_count++; @@ -949,10 +983,15 @@ int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, vport->info.trusted = true; if (!mlx5_esw_is_manager_vport(esw, vport_num) && - MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) { + MLX5_CAP_GEN(esw->dev, vport_group_manager)) { ret = mlx5_esw_vport_vhca_id_map(esw, vport); if (ret) goto err_vhca_mapping; + ret = xa_insert(&esw->vhca_type_map, vport->vhca_id, + xa_mk_value(esw_vport_to_func_type(esw, vport)), + GFP_KERNEL); + if (ret) + goto err_type_map; } esw_vport_change_handle_locked(vport); @@ -966,6 +1005,8 @@ int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, mutex_unlock(&esw->state_lock); return ret; +err_type_map: + mlx5_esw_vport_vhca_id_unmap(esw, vport); err_vhca_mapping: esw_vport_cleanup(esw, vport); mutex_unlock(&esw->state_lock); @@ -990,10 +1031,12 @@ void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) arm_vport_context_events_cmd(esw->dev, vport_num, 0); if (!mlx5_esw_is_manager_vport(esw, vport_num) && - MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) + MLX5_CAP_GEN(esw->dev, vport_group_manager)) { + xa_erase(&esw->vhca_type_map, vport->vhca_id); mlx5_esw_vport_vhca_id_unmap(esw, vport); + } - if (vport->vport != MLX5_VPORT_PF && + if (mlx5_eswitch_is_vf_vport(esw, vport_num) && (vport->info.ipsec_crypto_enabled || vport->info.ipsec_packet_enabled)) esw->enabled_ipsec_vf_count--; @@ -1036,11 +1079,28 @@ static int eswitch_vport_event(struct notifier_block *nb, */ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) { - int outlen = MLX5_ST_SZ_BYTES(query_esw_functions_out); + bool net_func_v1 = MLX5_CAP_GEN(dev, query_host_net_function_v1); u32 in[MLX5_ST_SZ_DW(query_esw_functions_in)] = {}; + int alloc_entries; + int outlen; u32 *out; int err; + if (net_func_v1) { + alloc_entries = MLX5_CAP_GEN(dev, + query_host_net_function_num_max); + alloc_entries = max(alloc_entries, 1); + MLX5_SET(query_esw_functions_in, in, op_mod, + MLX5_QUERY_ESW_FUNC_OP_MOD_LAYOUT_V1); + outlen = MLX5_BYTE_OFF(query_esw_functions_out, + net_function_params) + + alloc_entries * MLX5_UN_SZ_BYTES(net_function_params); + outlen = max_t(int, outlen, + MLX5_ST_SZ_BYTES(query_esw_functions_out)); + } else { + outlen = MLX5_ST_SZ_BYTES(query_esw_functions_out); + } + out = kvzalloc(outlen, GFP_KERNEL); if (!out) return ERR_PTR(-ENOMEM); @@ -1049,15 +1109,130 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) MLX5_CMD_OP_QUERY_ESW_FUNCTIONS); err = mlx5_cmd_exec(dev, in, sizeof(in), out, outlen); - if (!err) - return out; + if (err) + goto free; + if (net_func_v1) { + int num_entries; + + num_entries = MLX5_GET(query_esw_functions_out, out, + net_function_num); + if (num_entries > alloc_entries) { + mlx5_core_warn(dev, "Got %d entries, max expected %d\n", + num_entries, alloc_entries); + err = -EINVAL; + goto free; + } + } + + return out; + +free: kvfree(out); return ERR_PTR(err); } +static struct mlx5_esw_pf_info +mlx5_esw_host_pf_from_host_params(const void *entry) +{ + return (struct mlx5_esw_pf_info) { + .pf_not_exist = MLX5_GET(host_params_context, entry, + host_pf_not_exist), + .pf_disabled = MLX5_GET(host_params_context, entry, + host_pf_disabled), + .num_of_vfs = MLX5_GET(host_params_context, entry, + host_num_of_vfs), + .total_vfs = MLX5_GET(host_params_context, entry, + host_total_vfs), + .host_number = MLX5_GET(host_params_context, entry, + host_number), + }; +} + +static struct mlx5_esw_pf_info +mlx5_esw_host_pf_from_net_func_params(const u8 *entry, int num_entries) +{ + int i; + + for (i = 0; i < num_entries; i++) { + int pf_type, state; + + pf_type = MLX5_GET(network_function_params, entry, pci_pf_type); + if (pf_type != MLX5_PCI_PF_TYPE_EXTERNAL_HOST_PF) { + entry += MLX5_UN_SZ_BYTES(net_function_params); + continue; + } + + state = MLX5_GET(network_function_params, entry, vhca_state); + + return (struct mlx5_esw_pf_info) { + .pf_disabled = state != MLX5_VHCA_STATE_IN_USE, + .num_of_vfs = MLX5_GET(network_function_params, + entry, pci_num_vfs), + .total_vfs = MLX5_GET(network_function_params, + entry, pci_total_vfs), + .host_number = MLX5_GET(network_function_params, + entry, host_number), + .pf_num = MLX5_GET(network_function_params, entry, + pci_device_function), + }; + } + + /* No external host PF entry found */ + return (struct mlx5_esw_pf_info) { + .pf_not_exist = true, + .pf_disabled = true, + }; +} + +struct mlx5_esw_pf_info +mlx5_esw_get_host_pf_info(struct mlx5_core_dev *dev, const u32 *out) +{ + const void *entry; + + entry = MLX5_ADDR_OF(query_esw_functions_out, out, net_function_params); + + if (MLX5_CAP_GEN(dev, query_host_net_function_v1)) { + int num_entries = MLX5_GET(query_esw_functions_out, out, + net_function_num); + + return mlx5_esw_host_pf_from_net_func_params(entry, + num_entries); + } + + return mlx5_esw_host_pf_from_host_params(entry); +} + +bool mlx5_esw_get_spf_disabled(struct mlx5_core_dev *dev, const u32 *out, + u16 vhca_id) +{ + int num_entries; + const u8 *entry; + int i; + + num_entries = MLX5_GET(query_esw_functions_out, out, net_function_num); + entry = MLX5_ADDR_OF(query_esw_functions_out, out, net_function_params); + + for (i = 0; i < num_entries; i++) { + u16 entry_vhca_id = MLX5_GET(network_function_params, + entry, vhca_id); + + if (entry_vhca_id == vhca_id) { + int state; + + state = MLX5_GET(network_function_params, entry, + vhca_state); + return state != MLX5_VHCA_STATE_IN_USE; + } + entry += MLX5_UN_SZ_BYTES(net_function_params); + } + + return true; +} + static int mlx5_esw_host_functions_enabled_query(struct mlx5_eswitch *esw) { + struct mlx5_esw_pf_info host_pf_info; const u32 *query_host_out; if (!mlx5_core_is_ecpf_esw_manager(esw->dev)) @@ -1067,9 +1242,8 @@ static int mlx5_esw_host_functions_enabled_query(struct mlx5_eswitch *esw) if (IS_ERR(query_host_out)) return PTR_ERR(query_host_out); - esw->esw_funcs.host_funcs_disabled = - MLX5_GET(query_esw_functions_out, query_host_out, - host_params_context.host_pf_not_exist); + host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, query_host_out); + esw->esw_funcs.host_funcs_disabled = host_pf_info.pf_not_exist; kvfree(query_host_out); return 0; @@ -1084,13 +1258,17 @@ static void mlx5_eswitch_event_handler_register(struct mlx5_eswitch *esw) } } +static void mlx5_eswitch_invalidate_wq(struct mlx5_eswitch *esw) +{ + atomic_inc(&esw->generation); + wake_up_all(&esw->work_queue_wait); +} + static void mlx5_eswitch_event_handler_unregister(struct mlx5_eswitch *esw) { if (esw->mode == MLX5_ESWITCH_OFFLOADS && - mlx5_eswitch_is_funcs_handler(esw->dev)) { + mlx5_eswitch_is_funcs_handler(esw->dev)) mlx5_eq_notifier_unregister(esw->dev, &esw->esw_funcs.nb); - atomic_inc(&esw->esw_funcs.generation); - } } static void mlx5_eswitch_clear_vf_vports_info(struct mlx5_eswitch *esw) @@ -1319,7 +1497,7 @@ static int mlx5_eswitch_load_ec_vf_vports(struct mlx5_eswitch *esw, u16 num_ec_v return err; } -int mlx5_esw_host_pf_enable_hca(struct mlx5_core_dev *dev) +int mlx5_esw_pf_enable_hca(struct mlx5_core_dev *dev, u16 vport_num) { struct mlx5_eswitch *esw = dev->priv.eswitch; struct mlx5_vport *vport; @@ -1328,15 +1506,15 @@ int mlx5_esw_host_pf_enable_hca(struct mlx5_core_dev *dev) if (!mlx5_core_is_ecpf(dev) || !mlx5_esw_allowed(esw)) return 0; - vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_PF); + vport = mlx5_eswitch_get_vport(esw, vport_num); if (IS_ERR(vport)) return PTR_ERR(vport); - /* Once vport and representor are ready, take out the external host PF - * out of initializing state. Enabling HCA clears the iser->initializing - * bit and host PF driver loading can progress. + /* Once vport and representor are ready, take the PF out of + * initializing state. Enabling HCA clears the iser->initializing + * bit and PF driver loading can progress. */ - err = mlx5_cmd_host_pf_enable_hca(dev); + err = mlx5_cmd_pf_enable_hca(dev, vport_num); if (err) return err; @@ -1345,7 +1523,7 @@ int mlx5_esw_host_pf_enable_hca(struct mlx5_core_dev *dev) return 0; } -int mlx5_esw_host_pf_disable_hca(struct mlx5_core_dev *dev) +int mlx5_esw_pf_disable_hca(struct mlx5_core_dev *dev, u16 vport_num) { struct mlx5_eswitch *esw = dev->priv.eswitch; struct mlx5_vport *vport; @@ -1354,11 +1532,11 @@ int mlx5_esw_host_pf_disable_hca(struct mlx5_core_dev *dev) if (!mlx5_core_is_ecpf(dev) || !mlx5_esw_allowed(esw)) return 0; - vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_PF); + vport = mlx5_eswitch_get_vport(esw, vport_num); if (IS_ERR(vport)) return PTR_ERR(vport); - err = mlx5_cmd_host_pf_disable_hca(dev); + err = mlx5_cmd_pf_disable_hca(dev, vport_num); if (err) return err; @@ -1367,6 +1545,16 @@ int mlx5_esw_host_pf_disable_hca(struct mlx5_core_dev *dev) return 0; } +int mlx5_esw_host_pf_enable_hca(struct mlx5_core_dev *dev) +{ + return mlx5_esw_pf_enable_hca(dev, MLX5_VPORT_HOST_PF); +} + +int mlx5_esw_host_pf_disable_hca(struct mlx5_core_dev *dev) +{ + return mlx5_esw_pf_disable_hca(dev, MLX5_VPORT_HOST_PF); +} + /* mlx5_eswitch_enable_pf_vf_vports() enables vports of PF, ECPF and VFs * whichever are present on the eswitch. */ @@ -1374,15 +1562,18 @@ int mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, enum mlx5_eswitch_vport_event enabled_events) { + struct mlx5_esw_functions *esw_funcs = &esw->esw_funcs; bool pf_needed; + u16 vport_num; int ret; + int i; pf_needed = mlx5_core_is_ecpf_esw_manager(esw->dev) || esw->mode == MLX5_ESWITCH_LEGACY; /* Enable PF vport */ if (pf_needed && mlx5_esw_host_functions_enabled(esw->dev)) { - ret = mlx5_eswitch_load_pf_vf_vport(esw, MLX5_VPORT_PF, + ret = mlx5_eswitch_load_pf_vf_vport(esw, MLX5_VPORT_HOST_PF, enabled_events); if (ret) return ret; @@ -1405,14 +1596,14 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, /* Enable ECVF vports */ if (mlx5_core_ec_sriov_enabled(esw->dev)) { ret = mlx5_eswitch_load_ec_vf_vports(esw, - esw->esw_funcs.num_ec_vfs, + esw_funcs->num_ec_vfs, enabled_events); if (ret) goto ec_vf_err; } /* Enable VF vports */ - ret = mlx5_eswitch_load_vf_vports(esw, esw->esw_funcs.num_vfs, + ret = mlx5_eswitch_load_vf_vports(esw, esw_funcs->num_vfs, enabled_events); if (ret) goto vf_err; @@ -1422,13 +1613,36 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, if (ret) goto unload_vf_vports; + /* Enable satellite PF vports */ + for (i = 0; i < esw_funcs->num_spfs; i++) { + vport_num = esw_funcs->spfs[i].vport_num; + + ret = mlx5_eswitch_load_pf_vf_vport(esw, vport_num, + enabled_events); + if (ret) + goto spf_err; + + ret = mlx5_esw_pf_enable_hca(esw->dev, vport_num); + if (ret) { + mlx5_eswitch_unload_pf_vf_vport(esw, vport_num); + goto spf_err; + } + } + return 0; +spf_err: + while (i-- > 0) { + vport_num = esw_funcs->spfs[i].vport_num; + mlx5_esw_pf_disable_hca(esw->dev, vport_num); + mlx5_eswitch_unload_pf_vf_vport(esw, vport_num); + } + mlx5_eswitch_unload_adj_vf_vports(esw); unload_vf_vports: - mlx5_eswitch_unload_vf_vports(esw, esw->esw_funcs.num_vfs); + mlx5_eswitch_unload_vf_vports(esw, esw_funcs->num_vfs); vf_err: if (mlx5_core_ec_sriov_enabled(esw->dev)) - mlx5_eswitch_unload_ec_vf_vports(esw, esw->esw_funcs.num_ec_vfs); + mlx5_eswitch_unload_ec_vf_vports(esw, esw_funcs->num_ec_vfs); ec_vf_err: if (mlx5_ecpf_vport_exists(esw->dev)) mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_ECPF); @@ -1437,7 +1651,7 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, mlx5_esw_host_pf_disable_hca(esw->dev); pf_hca_err: if (pf_needed && mlx5_esw_host_functions_enabled(esw->dev)) - mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_PF); + mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_HOST_PF); return ret; } @@ -1446,13 +1660,22 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, */ void mlx5_eswitch_disable_pf_vf_vports(struct mlx5_eswitch *esw) { + struct mlx5_esw_functions *esw_funcs = &esw->esw_funcs; + u16 vport_num; + int i; + + for (i = 0; i < esw_funcs->num_spfs; i++) { + vport_num = esw_funcs->spfs[i].vport_num; + mlx5_esw_pf_disable_hca(esw->dev, vport_num); + mlx5_eswitch_unload_pf_vf_vport(esw, vport_num); + } + mlx5_eswitch_unload_adj_vf_vports(esw); - mlx5_eswitch_unload_vf_vports(esw, esw->esw_funcs.num_vfs); + mlx5_eswitch_unload_vf_vports(esw, esw_funcs->num_vfs); if (mlx5_core_ec_sriov_enabled(esw->dev)) - mlx5_eswitch_unload_ec_vf_vports(esw, - esw->esw_funcs.num_ec_vfs); + mlx5_eswitch_unload_ec_vf_vports(esw, esw_funcs->num_ec_vfs); if (mlx5_ecpf_vport_exists(esw->dev)) { mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_ECPF); @@ -1464,7 +1687,7 @@ void mlx5_eswitch_disable_pf_vf_vports(struct mlx5_eswitch *esw) if ((mlx5_core_is_ecpf_esw_manager(esw->dev) || esw->mode == MLX5_ESWITCH_LEGACY) && mlx5_esw_host_functions_enabled(esw->dev)) - mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_PF); + mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_HOST_PF); } static void mlx5_eswitch_get_devlink_param(struct mlx5_eswitch *esw) @@ -1489,6 +1712,7 @@ static void mlx5_eswitch_get_devlink_param(struct mlx5_eswitch *esw) static void mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, int num_vfs) { + struct mlx5_esw_pf_info host_pf_info; const u32 *out; if (num_vfs < 0) @@ -1503,8 +1727,8 @@ mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, int num_vfs) if (IS_ERR(out)) return; - esw->esw_funcs.num_vfs = MLX5_GET(query_esw_functions_out, out, - host_params_context.host_num_of_vfs); + host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, out); + esw->esw_funcs.num_vfs = host_pf_info.num_of_vfs; if (mlx5_core_ec_sriov_enabled(esw->dev)) esw->esw_funcs.num_ec_vfs = num_vfs; @@ -1715,6 +1939,9 @@ int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs) if (toggle_lag) mlx5_lag_disable_change(esw->dev); + mlx5_eswitch_invalidate_wq(esw); + mlx5_esw_reps_block(esw); + if (!mlx5_esw_is_fdb_created(esw)) { ret = mlx5_eswitch_enable_locked(esw, num_vfs); } else { @@ -1737,6 +1964,8 @@ int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs) } } + mlx5_esw_reps_unblock(esw); + if (toggle_lag) mlx5_lag_enable_change(esw->dev); @@ -1760,6 +1989,9 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf) esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", esw->esw_funcs.num_vfs, esw->esw_funcs.num_ec_vfs, esw->enabled_vports); + mlx5_eswitch_invalidate_wq(esw); + mlx5_esw_reps_block(esw); + if (!mlx5_core_is_ecpf(esw->dev)) { mlx5_eswitch_unload_vf_vports(esw, esw->esw_funcs.num_vfs); if (clear_vf) @@ -1770,6 +2002,8 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf) mlx5_eswitch_clear_ec_vf_vports_info(esw); } + mlx5_esw_reps_unblock(esw); + if (esw->mode == MLX5_ESWITCH_OFFLOADS) { struct devlink *devlink = priv_to_devlink(esw->dev); @@ -1799,6 +2033,7 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw) mlx5_eq_notifier_unregister(esw->dev, &esw->nb); mlx5_eswitch_event_handler_unregister(esw); + mlx5_eswitch_invalidate_wq(esw); esw_info(esw->dev, "Disable: mode(%s), nvfs(%d), necvfs(%d), active vports(%d)\n", esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", @@ -1824,42 +2059,30 @@ void mlx5_eswitch_disable(struct mlx5_eswitch *esw) devl_assert_locked(priv_to_devlink(esw->dev)); mlx5_lag_disable_change(esw->dev); + + mlx5_esw_reps_block(esw); mlx5_eswitch_disable_locked(esw); + mlx5_esw_reps_unblock(esw); + esw->mode = MLX5_ESWITCH_LEGACY; + mlx5_sd_eswitch_mode_set(esw->dev, MLX5_ESWITCH_LEGACY); mlx5_lag_enable_change(esw->dev); } -static int mlx5_query_hca_cap_host_pf(struct mlx5_core_dev *dev, void *out) -{ - u16 opmod = (MLX5_CAP_GENERAL << 1) | (HCA_CAP_OPMOD_GET_MAX & 0x01); - u8 in[MLX5_ST_SZ_BYTES(query_hca_cap_in)] = {}; - - MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP); - MLX5_SET(query_hca_cap_in, in, op_mod, opmod); - MLX5_SET(query_hca_cap_in, in, function_id, MLX5_VPORT_PF); - MLX5_SET(query_hca_cap_in, in, other_function, true); - return mlx5_cmd_exec_inout(dev, query_hca_cap, in, out); -} - -int mlx5_esw_sf_max_hpf_functions(struct mlx5_core_dev *dev, u16 *max_sfs, u16 *sf_base_id) - +static int mlx5_esw_sf_max_pf_functions(struct mlx5_core_dev *dev, + u16 vport_num, u16 *max_sfs, + u16 *sf_base_id) { int query_out_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); void *query_ctx; void *hca_caps; int err; - if (!mlx5_core_is_ecpf(dev) || - !mlx5_esw_host_functions_enabled(dev)) { - *max_sfs = 0; - return 0; - } - query_ctx = kzalloc(query_out_sz, GFP_KERNEL); if (!query_ctx) return -ENOMEM; - err = mlx5_query_hca_cap_host_pf(dev, query_ctx); + err = mlx5_vport_get_other_func_general_cap(dev, vport_num, query_ctx); if (err) goto out_free; @@ -1872,6 +2095,131 @@ int mlx5_esw_sf_max_hpf_functions(struct mlx5_core_dev *dev, u16 *max_sfs, u16 * return err; } +int mlx5_esw_sf_max_hpf_functions(struct mlx5_core_dev *dev, u16 *max_sfs, + u16 *sf_base_id) +{ + if (!mlx5_core_is_ecpf(dev) || + !mlx5_esw_host_functions_enabled(dev)) { + *max_sfs = 0; + return 0; + } + + return mlx5_esw_sf_max_pf_functions(dev, MLX5_VPORT_HOST_PF, max_sfs, + sf_base_id); +} + +int mlx5_esw_sf_max_spf_functions(struct mlx5_core_dev *dev, int spf_idx, + u16 *max_sfs, u16 *sf_base_id) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + u16 vport_num; + + if (!mlx5_esw_allowed(esw)) { + *max_sfs = 0; + return 0; + } + + if (spf_idx >= esw->esw_funcs.num_spfs) + return -EINVAL; + + vport_num = esw->esw_funcs.spfs[spf_idx].vport_num; + return mlx5_esw_sf_max_pf_functions(dev, vport_num, max_sfs, + sf_base_id); +} + +int mlx5_esw_get_num_spfs(struct mlx5_core_dev *dev) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (!mlx5_esw_allowed(esw)) + return 0; + + return esw->esw_funcs.num_spfs; +} + +int mlx5_esw_spf_get_host_number(struct mlx5_core_dev *dev, int spf_idx, + u16 *host_number) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (!mlx5_esw_allowed(esw)) + return -EPERM; + + if (spf_idx >= esw->esw_funcs.num_spfs) + return -EINVAL; + + *host_number = esw->esw_funcs.spfs[spf_idx].host_number; + return 0; +} + +u16 mlx5_esw_get_hpf_host_number(struct mlx5_core_dev *dev) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (!mlx5_esw_allowed(esw)) + return 0; + + return esw->esw_funcs.hpf_host_number; +} + +u16 mlx5_esw_get_hpf_pf_num(struct mlx5_core_dev *dev) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (mlx5_core_is_ecpf_esw_manager(dev) && + MLX5_CAP_GEN(dev, query_host_net_function_v1)) + return esw->esw_funcs.hpf_pf_num; + + return PCI_FUNC(dev->pdev->devfn); +} + +u16 mlx5_esw_sf_controller_to_pfnum(struct mlx5_core_dev *dev, u32 controller) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + struct mlx5_esw_functions *esw_funcs; + int i; + + if (!controller) + return PCI_FUNC(dev->pdev->devfn); + + esw_funcs = &esw->esw_funcs; + for (i = 0; i < esw_funcs->num_spfs; i++) + if (controller == esw_funcs->spfs[i].host_number + 1) + return esw_funcs->spfs[i].pf_num; + + return mlx5_esw_get_hpf_pf_num(dev); +} + +bool mlx5_esw_has_spf_sfs(struct mlx5_core_dev *dev) +{ + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (!mlx5_esw_allowed(esw)) + return false; + + return esw->esw_funcs.has_spf_sfs; +} + +static int mlx5_esw_hpf_info_init(struct mlx5_eswitch *esw) +{ + struct mlx5_esw_pf_info host_pf_info; + const u32 *query_host_out; + + if (!mlx5_core_is_ecpf_esw_manager(esw->dev)) + return 0; + + query_host_out = mlx5_esw_query_functions(esw->dev); + if (IS_ERR(query_host_out)) + return PTR_ERR(query_host_out); + + /* Mark non local controller with non zero controller number. */ + host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, query_host_out); + esw->esw_funcs.hpf_host_number = host_pf_info.host_number; + esw->esw_funcs.hpf_pf_num = host_pf_info.pf_num; + kvfree(query_host_out); + return 0; +} + int mlx5_esw_vport_alloc(struct mlx5_eswitch *esw, int index, u16 vport_num) { struct mlx5_vport *vport; @@ -1906,11 +2254,111 @@ void mlx5_esw_vport_free(struct mlx5_eswitch *esw, struct mlx5_vport *vport) kfree(vport); } +static void mlx5_esw_spfs_cleanup(struct mlx5_eswitch *esw) +{ + struct mlx5_esw_functions *esw_funcs = &esw->esw_funcs; + int i; + + for (i = 0; i < esw_funcs->num_spfs; i++) + mlx5_esw_destroy_esw_vport(esw->dev, + esw_funcs->spfs[i].vport_num); + + kfree(esw_funcs->spfs); + esw_funcs->spfs = NULL; + esw_funcs->num_spfs = 0; +} + +static int mlx5_esw_spfs_init(struct mlx5_eswitch *esw) +{ + struct mlx5_esw_functions *esw_funcs = &esw->esw_funcs; + struct mlx5_core_dev *dev = esw->dev; + int num_entries; + const u8 *entry; + const u32 *out; + int err = 0; + int pf_type; + u16 vhca_id; + int i; + + if (!MLX5_CAP_GEN(dev, query_host_net_function_v1)) + return 0; + + out = mlx5_esw_query_functions(dev); + if (IS_ERR(out)) + return PTR_ERR(out); + + num_entries = MLX5_GET(query_esw_functions_out, out, net_function_num); + if (!num_entries) + goto out_free; + + esw_funcs->spfs = kcalloc(num_entries, sizeof(*esw_funcs->spfs), + GFP_KERNEL); + if (!esw_funcs->spfs) { + err = -ENOMEM; + goto out_free; + } + + entry = MLX5_ADDR_OF(query_esw_functions_out, out, net_function_params); + + for (i = 0; i < num_entries; i++) { + u16 vport_num; + + pf_type = MLX5_GET(network_function_params, entry, pci_pf_type); + if (pf_type != MLX5_PCI_PF_TYPE_SATELLITE_PF) { + entry += MLX5_UN_SZ_BYTES(net_function_params); + continue; + } + + if (!MLX5_GET(network_function_params, entry, + esw_vport_manual)) { + esw_warn(dev, "Satellite PF without esw_vport_manual is not supported\n"); + entry += MLX5_UN_SZ_BYTES(net_function_params); + continue; + } + + vhca_id = MLX5_GET(network_function_params, entry, vhca_id); + + err = mlx5_esw_create_esw_vport(dev, vhca_id, &vport_num); + if (err) { + esw_warn(dev, "Failed to create satellite PF vport for vhca_id 0x%x, err %d\n", + vhca_id, err); + goto spfs_cleanup; + } + + esw_funcs->spfs[esw_funcs->num_spfs].vport_num = vport_num; + esw_funcs->spfs[esw_funcs->num_spfs].vhca_id = vhca_id; + esw_funcs->spfs[esw_funcs->num_spfs].host_number = + MLX5_GET(network_function_params, entry, host_number); + esw_funcs->spfs[esw_funcs->num_spfs].pf_num = + MLX5_GET(network_function_params, entry, + pci_device_function); + esw_funcs->num_spfs++; + + entry += MLX5_UN_SZ_BYTES(net_function_params); + } + + if (!esw_funcs->num_spfs) { + kfree(esw_funcs->spfs); + esw_funcs->spfs = NULL; + } + + kvfree(out); + return 0; + +spfs_cleanup: + mlx5_esw_spfs_cleanup(esw); +out_free: + kvfree(out); + return err; +} + static void mlx5_esw_vports_cleanup(struct mlx5_eswitch *esw) { struct mlx5_vport *vport; unsigned long i; + mlx5_esw_spfs_cleanup(esw); + esw->esw_funcs.has_spf_sfs = false; mlx5_esw_for_each_vport(esw, i, vport) mlx5_esw_vport_free(esw, vport); xa_destroy(&esw->vports); @@ -1919,19 +2367,22 @@ static void mlx5_esw_vports_cleanup(struct mlx5_eswitch *esw) static int mlx5_esw_vports_init(struct mlx5_eswitch *esw) { struct mlx5_core_dev *dev = esw->dev; - u16 max_host_pf_sfs; - u16 base_sf_num; + u16 max_sfs, base_sf_num; int idx = 0; int err; int i; xa_init(&esw->vports); + err = mlx5_esw_hpf_info_init(esw); + if (err) + goto err; + if (mlx5_esw_host_functions_enabled(dev)) { - err = mlx5_esw_vport_alloc(esw, idx, MLX5_VPORT_PF); + err = mlx5_esw_vport_alloc(esw, idx, MLX5_VPORT_HOST_PF); if (err) goto err; - if (esw->first_host_vport == MLX5_VPORT_PF) + if (esw->first_host_vport == MLX5_VPORT_HOST_PF) xa_set_mark(&esw->vports, idx, MLX5_ESW_VPT_HOST_FN); idx++; for (i = 0; i < mlx5_core_max_vfs(dev); i++) { @@ -1953,10 +2404,10 @@ static int mlx5_esw_vports_init(struct mlx5_eswitch *esw) idx++; } - err = mlx5_esw_sf_max_hpf_functions(dev, &max_host_pf_sfs, &base_sf_num); + err = mlx5_esw_sf_max_hpf_functions(dev, &max_sfs, &base_sf_num); if (err) goto err; - for (i = 0; i < max_host_pf_sfs; i++) { + for (i = 0; i < max_sfs; i++) { err = mlx5_esw_vport_alloc(esw, idx, base_sf_num + i); if (err) goto err; @@ -1964,6 +2415,38 @@ static int mlx5_esw_vports_init(struct mlx5_eswitch *esw) idx++; } + err = mlx5_esw_spfs_init(esw); + if (err) + goto err; + + for (i = 0; i < esw->esw_funcs.num_spfs; i++) { + struct mlx5_vport *vport; + u16 vport_num; + + vport_num = esw->esw_funcs.spfs[i].vport_num; + err = mlx5_esw_vport_alloc(esw, idx++, vport_num); + if (err) + goto err; + vport = mlx5_eswitch_get_vport(esw, vport_num); + vport->vhca_id = esw->esw_funcs.spfs[i].vhca_id; + + err = mlx5_esw_sf_max_spf_functions(dev, i, + &max_sfs, &base_sf_num); + if (err) + goto err; + if (max_sfs) + esw->esw_funcs.has_spf_sfs = true; + for (int j = 0; j < max_sfs; j++) { + err = mlx5_esw_vport_alloc(esw, idx, + base_sf_num + j); + if (err) + goto err; + xa_set_mark(&esw->vports, base_sf_num + j, + MLX5_ESW_VPT_SF); + idx++; + } + } + if (mlx5_core_ec_sriov_enabled(esw->dev)) { int ec_vf_base_num = mlx5_core_ec_vf_vport_base(dev); @@ -2083,9 +2566,12 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) atomic64_set(&esw->offloads.num_flows, 0); ida_init(&esw->offloads.vport_metadata_ida); xa_init_flags(&esw->offloads.vhca_map, XA_FLAGS_ALLOC); + xa_init(&esw->vhca_type_map); mutex_init(&esw->state_lock); init_rwsem(&esw->mode_lock); refcount_set(&esw->qos.refcnt, 0); + init_waitqueue_head(&esw->work_queue_wait); + atomic_set(&esw->generation, 0); esw->enabled_vports = 0; esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE; @@ -2123,12 +2609,14 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) esw_info(esw->dev, "cleanup\n"); - mlx5_esw_qos_cleanup(esw); + mlx5_eswitch_invalidate_wq(esw); destroy_workqueue(esw->work_queue); + mlx5_esw_qos_cleanup(esw); WARN_ON(refcount_read(&esw->qos.refcnt)); mutex_destroy(&esw->state_lock); WARN_ON(!xa_empty(&esw->offloads.vhca_map)); xa_destroy(&esw->offloads.vhca_map); + xa_destroy(&esw->vhca_type_map); ida_destroy(&esw->offloads.vport_metadata_ida); mlx5e_mod_hdr_tbl_destroy(&esw->offloads.mod_hdr); mutex_destroy(&esw->offloads.encap_tbl_lock); @@ -2207,10 +2695,29 @@ bool mlx5_eswitch_is_vf_vport(struct mlx5_eswitch *esw, u16 vport_num) return mlx5_esw_check_port_type(esw, vport_num, MLX5_ESW_VPT_VF); } +int mlx5_esw_spf_vport_to_idx(struct mlx5_eswitch *esw, u16 vport_num) +{ + struct mlx5_esw_functions *esw_funcs = &esw->esw_funcs; + int i; + + for (i = 0; i < esw_funcs->num_spfs; i++) { + if (esw_funcs->spfs[i].vport_num == vport_num) + return i; + } + + return -ENOENT; +} + +bool mlx5_esw_is_spf_vport(struct mlx5_eswitch *esw, u16 vport_num) +{ + return mlx5_esw_spf_vport_to_idx(esw, vport_num) >= 0; +} + bool mlx5_eswitch_is_pf_vf_vport(struct mlx5_eswitch *esw, u16 vport_num) { - return vport_num == MLX5_VPORT_PF || - mlx5_eswitch_is_vf_vport(esw, vport_num); + return vport_num == MLX5_VPORT_HOST_PF || + mlx5_eswitch_is_vf_vport(esw, vport_num) || + mlx5_esw_is_spf_vport(esw, vport_num); } bool mlx5_esw_is_sf_vport(struct mlx5_eswitch *esw, u16 vport_num) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index e9cf7c592ce9..fea72b1dedab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,15 @@ struct mlx5_mapped_obj { }; }; +struct mlx5_esw_pf_info { + bool pf_not_exist; + bool pf_disabled; + u16 num_of_vfs; + u16 total_vfs; + u16 host_number; + u16 pf_num; +}; + #ifdef CONFIG_MLX5_ESWITCH #define ESW_OFFLOADS_DEFAULT_NUM_GROUPS 15 @@ -316,6 +326,7 @@ struct mlx5_esw_offload { DECLARE_HASHTABLE(termtbl_tbl, 8); struct mutex termtbl_mutex; /* protects termtbl hash */ struct xarray vhca_map; + struct mutex reps_lock; /* protects representor load/unload/register */ const struct mlx5_eswitch_rep_ops *rep_ops[NUM_REP_TYPES]; u8 inline_mode; atomic64_t num_flows; @@ -323,7 +334,6 @@ struct mlx5_esw_offload { u64 num_block_mode; enum devlink_eswitch_encap_mode encap; struct ida vport_metadata_ida; - unsigned int host_number; /* ECPF supports one external host */ }; /* E-Switch MC FDB table hash node */ @@ -337,14 +347,26 @@ struct mlx5_host_work { struct work_struct work; struct mlx5_eswitch *esw; int work_gen; + void (*func)(struct mlx5_eswitch *esw); +}; + +struct mlx5_esw_spf { + u16 vport_num; + u16 vhca_id; + u16 host_number; + u16 pf_num; }; struct mlx5_esw_functions { struct mlx5_nb nb; - atomic_t generation; bool host_funcs_disabled; u16 num_vfs; u16 num_ec_vfs; + u16 hpf_host_number; + u16 hpf_pf_num; + bool has_spf_sfs; + struct mlx5_esw_spf *spfs; + int num_spfs; }; enum { @@ -373,6 +395,7 @@ struct mlx5_eswitch { struct dentry *debugfs_root; struct workqueue_struct *work_queue; struct xarray vports; + struct xarray vhca_type_map; u32 flags; int total_vports; int enabled_vports; @@ -386,6 +409,7 @@ struct mlx5_eswitch { */ struct rw_semaphore mode_lock; atomic64_t user_count; + wait_queue_head_t work_queue_wait; /* Protected with the E-Switch qos domain lock. */ struct { @@ -411,10 +435,12 @@ struct mlx5_eswitch { struct mlx5_devcom_comp_dev *devcom; u16 enabled_ipsec_vf_count; bool eswitch_operation_in_progress; + atomic_t generation; }; void esw_offloads_disable(struct mlx5_eswitch *esw); int esw_offloads_enable(struct mlx5_eswitch *esw); +int mlx5_esw_offloads_init_deferred_metadata(struct mlx5_eswitch *esw); void esw_offloads_cleanup(struct mlx5_eswitch *esw); int esw_offloads_init(struct mlx5_eswitch *esw); @@ -645,6 +671,12 @@ bool mlx5_esw_multipath_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1); const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev); +struct mlx5_esw_pf_info mlx5_esw_get_host_pf_info(struct mlx5_core_dev *dev, + const u32 *out); +bool mlx5_esw_get_spf_disabled(struct mlx5_core_dev *dev, const u32 *out, + u16 vhca_id); +int mlx5_esw_pf_enable_hca(struct mlx5_core_dev *dev, u16 vport_num); +int mlx5_esw_pf_disable_hca(struct mlx5_core_dev *dev, u16 vport_num); int mlx5_esw_host_pf_enable_hca(struct mlx5_core_dev *dev); int mlx5_esw_host_pf_disable_hca(struct mlx5_core_dev *dev); @@ -652,6 +684,9 @@ void mlx5_esw_adjacent_vhcas_setup(struct mlx5_eswitch *esw); void mlx5_esw_adjacent_vhcas_cleanup(struct mlx5_eswitch *esw); int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev, u16 vport, bool connect); +int mlx5_esw_create_esw_vport(struct mlx5_core_dev *dev, u16 vhca_id, + u16 *vport_num); +void mlx5_esw_destroy_esw_vport(struct mlx5_core_dev *dev, u16 vport); #define MLX5_DEBUG_ESWITCH_MASK BIT(3) @@ -685,7 +720,7 @@ static inline bool mlx5_esw_is_owner(struct mlx5_eswitch *esw, u16 vport_num, static inline u16 mlx5_eswitch_first_host_vport_num(struct mlx5_core_dev *dev) { return mlx5_core_is_ecpf_esw_manager(dev) ? - MLX5_VPORT_PF : MLX5_VPORT_FIRST_VF; + MLX5_VPORT_HOST_PF : MLX5_VPORT_FIRST_HOST_VF; } static inline bool mlx5_eswitch_is_funcs_handler(const struct mlx5_core_dev *dev) @@ -757,6 +792,16 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw); MLX5_CAP_GEN_2((esw->dev), ec_vf_vport_base) +\ (last) - 1) +/* SPF vport numbers are not contiguous, iterate via the spfs array + * and look up each vport in the xarray. + */ +#define mlx5_esw_for_each_spf_vport(esw, index, vport) \ + for ((index) = 0; \ + (index) < (esw)->esw_funcs.num_spfs && \ + ((vport) = xa_load(&(esw)->vports, \ + (esw)->esw_funcs.spfs[(index)].vport_num)); \ + (index)++) + #define mlx5_esw_for_each_rep(esw, i, rep) \ xa_for_each(&((esw)->offloads.vport_reps), i, rep) @@ -769,6 +814,8 @@ struct mlx5_vport *__must_check mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num); bool mlx5_eswitch_is_vf_vport(struct mlx5_eswitch *esw, u16 vport_num); +int mlx5_esw_spf_vport_to_idx(struct mlx5_eswitch *esw, u16 vport_num); +bool mlx5_esw_is_spf_vport(struct mlx5_eswitch *esw, u16 vport_num); bool mlx5_eswitch_is_pf_vf_vport(struct mlx5_eswitch *esw, u16 vport_num); bool mlx5_esw_is_sf_vport(struct mlx5_eswitch *esw, u16 vport_num); @@ -854,6 +901,16 @@ void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_vport *vport); struct devlink_port *mlx5_esw_offloads_devlink_port(struct mlx5_eswitch *esw, u16 vport_num); int mlx5_esw_sf_max_hpf_functions(struct mlx5_core_dev *dev, u16 *max_sfs, u16 *sf_base_id); +int mlx5_esw_sf_max_spf_functions(struct mlx5_core_dev *dev, int spf_idx, + u16 *max_sfs, u16 *sf_base_id); + +int mlx5_esw_get_num_spfs(struct mlx5_core_dev *dev); +int mlx5_esw_spf_get_host_number(struct mlx5_core_dev *dev, int spf_idx, + u16 *host_number); +u16 mlx5_esw_get_hpf_host_number(struct mlx5_core_dev *dev); +u16 mlx5_esw_get_hpf_pf_num(struct mlx5_core_dev *dev); +u16 mlx5_esw_sf_controller_to_pfnum(struct mlx5_core_dev *dev, u32 controller); +bool mlx5_esw_has_spf_sfs(struct mlx5_core_dev *dev); int mlx5_esw_vport_vhca_id_map(struct mlx5_eswitch *esw, struct mlx5_vport *vport); @@ -861,6 +918,7 @@ void mlx5_esw_vport_vhca_id_unmap(struct mlx5_eswitch *esw, struct mlx5_vport *vport); int mlx5_eswitch_vhca_id_to_vport(struct mlx5_eswitch *esw, u16 vhca_id, u16 *vport_num); bool mlx5_esw_vport_vhca_id(struct mlx5_eswitch *esw, u16 vportn, u16 *vhca_id); +u16 mlx5_esw_vhca_id_to_func_type(struct mlx5_core_dev *dev, u16 vhca_id); void mlx5_esw_offloads_rep_remove(struct mlx5_eswitch *esw, const struct mlx5_vport *vport); @@ -893,11 +951,18 @@ void esw_vport_change_handle_locked(struct mlx5_vport *vport); bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 controller); +int mlx5_eswitch_offloads_vport_lag_add_one(struct mlx5_eswitch *master_esw, + struct mlx5_eswitch *slave_esw); +void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw, + struct mlx5_eswitch *slave_esw); int mlx5_eswitch_offloads_single_fdb_add_one(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw, int max_slaves); void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw); int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw); +void mlx5_eswitch_unload_reps(struct mlx5_eswitch *esw); +bool mlx5_eswitch_is_peer(struct mlx5_eswitch *esw, + struct mlx5_eswitch *peer_esw); bool mlx5_eswitch_block_encap(struct mlx5_core_dev *dev, bool from_fdb); void mlx5_eswitch_unblock_encap(struct mlx5_core_dev *dev); @@ -913,13 +978,6 @@ static inline int mlx5_eswitch_num_vfs(struct mlx5_eswitch *esw) return 0; } -static inline int mlx5_eswitch_get_npeers(struct mlx5_eswitch *esw) -{ - if (mlx5_esw_allowed(esw)) - return esw->num_peers; - return 0; -} - static inline struct mlx5_flow_table * mlx5_eswitch_get_slow_fdb(struct mlx5_eswitch *esw) { @@ -949,6 +1007,8 @@ mlx5_esw_lag_demux_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_handle * mlx5_esw_lag_demux_rule_create(struct mlx5_eswitch *esw, u16 vport_num, struct mlx5_flow_table *lag_ft); +void mlx5_esw_reps_block(struct mlx5_eswitch *esw); +void mlx5_esw_reps_unblock(struct mlx5_eswitch *esw); #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } @@ -969,6 +1029,12 @@ static inline const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) return ERR_PTR(-EOPNOTSUPP); } +static inline struct mlx5_esw_pf_info +mlx5_esw_get_host_pf_info(struct mlx5_core_dev *dev, const u32 *out) +{ + return (struct mlx5_esw_pf_info) {}; +} + static inline struct mlx5_flow_handle * esw_add_restore_rule(struct mlx5_eswitch *esw, u32 tag) { @@ -993,14 +1059,15 @@ static inline void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw, struct mlx5_eswitch *slave_esw) {} -static inline int mlx5_eswitch_get_npeers(struct mlx5_eswitch *esw) { return 0; } - static inline int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) { return 0; } +static inline void +mlx5_eswitch_unload_reps(struct mlx5_eswitch *esw) {} + static inline bool mlx5_eswitch_block_encap(struct mlx5_core_dev *dev, bool from_fdb) { @@ -1026,12 +1093,21 @@ mlx5_esw_host_functions_enabled(const struct mlx5_core_dev *dev) return true; } +static inline void mlx5_esw_reps_block(struct mlx5_eswitch *esw) {} +static inline void mlx5_esw_reps_unblock(struct mlx5_eswitch *esw) {} + static inline bool mlx5_esw_vport_vhca_id(struct mlx5_eswitch *esw, u16 vportn, u16 *vhca_id) { return false; } +static inline u16 +mlx5_esw_vhca_id_to_func_type(struct mlx5_core_dev *dev, u16 vhca_id) +{ + return MLX5_FUNC_TYPE_NONE; +} + static inline void mlx5_eswitch_safe_aux_devs_remove(struct mlx5_core_dev *dev) {} static inline struct mlx5_flow_handle * diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index a078d06f4567..907ee83a722d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "mlx5_core.h" #include "eswitch.h" #include "esw/indir_table.h" @@ -45,6 +46,7 @@ #include "fs_core.h" #include "lib/mlx5.h" #include "lib/devcom.h" +#include "lib/sd.h" #include "lib/eq.h" #include "lib/fs_chains.h" #include "en_tc.h" @@ -1216,9 +1218,10 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw, if (mlx5_core_is_ecpf_esw_manager(peer_dev) && mlx5_esw_host_functions_enabled(peer_dev)) { - peer_vport = mlx5_eswitch_get_vport(peer_esw, MLX5_VPORT_PF); + peer_vport = mlx5_eswitch_get_vport(peer_esw, + MLX5_VPORT_HOST_PF); esw_set_peer_miss_rule_source_port(esw, peer_esw, spec, - MLX5_VPORT_PF); + MLX5_VPORT_HOST_PF); flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), spec, &flow_act, &dest, 1); @@ -1229,6 +1232,19 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw, flows[peer_vport->index] = flow; } + mlx5_esw_for_each_spf_vport(peer_esw, i, peer_vport) { + esw_set_peer_miss_rule_source_port(esw, peer_esw, spec, + peer_vport->vport); + + flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw), + spec, &flow_act, &dest, 1); + if (IS_ERR(flow)) { + err = PTR_ERR(flow); + goto add_ecpf_flow_err; + } + flows[peer_vport->index] = flow; + } + if (mlx5_ecpf_vport_exists(peer_dev)) { peer_vport = mlx5_eswitch_get_vport(peer_esw, MLX5_VPORT_ECPF); MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_ECPF); @@ -1297,10 +1313,15 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw, mlx5_del_flow_rules(flows[peer_vport->index]); } add_ecpf_flow_err: - + mlx5_esw_for_each_spf_vport(peer_esw, i, peer_vport) { + if (!flows[peer_vport->index]) + continue; + mlx5_del_flow_rules(flows[peer_vport->index]); + } if (mlx5_core_is_ecpf_esw_manager(peer_dev) && mlx5_esw_host_functions_enabled(peer_dev)) { - peer_vport = mlx5_eswitch_get_vport(peer_esw, MLX5_VPORT_PF); + peer_vport = mlx5_eswitch_get_vport(peer_esw, + MLX5_VPORT_HOST_PF); mlx5_del_flow_rules(flows[peer_vport->index]); } add_pf_flow_err: @@ -1340,9 +1361,13 @@ static void esw_del_fdb_peer_miss_rules(struct mlx5_eswitch *esw, mlx5_del_flow_rules(flows[peer_vport->index]); } + mlx5_esw_for_each_spf_vport(peer_esw, i, peer_vport) + mlx5_del_flow_rules(flows[peer_vport->index]); + if (mlx5_core_is_ecpf_esw_manager(peer_dev) && mlx5_esw_host_functions_enabled(peer_dev)) { - peer_vport = mlx5_eswitch_get_vport(peer_esw, MLX5_VPORT_PF); + peer_vport = mlx5_eswitch_get_vport(peer_esw, + MLX5_VPORT_HOST_PF); mlx5_del_flow_rules(flows[peer_vport->index]); } @@ -2410,23 +2435,44 @@ static int esw_create_restore_table(struct mlx5_eswitch *esw) return err; } +static void mlx5_esw_assert_reps_locked(struct mlx5_eswitch *esw) +{ + lockdep_assert_held(&esw->offloads.reps_lock); +} + +void mlx5_esw_reps_block(struct mlx5_eswitch *esw) +{ + mutex_lock(&esw->offloads.reps_lock); +} + +static void mlx5_esw_reps_block_nested(struct mlx5_eswitch *esw) +{ + mutex_lock_nested(&esw->offloads.reps_lock, SINGLE_DEPTH_NESTING); +} + +void mlx5_esw_reps_unblock(struct mlx5_eswitch *esw) +{ + mutex_unlock(&esw->offloads.reps_lock); +} + static void esw_mode_change(struct mlx5_eswitch *esw, u16 mode) { + mlx5_esw_reps_unblock(esw); mlx5_devcom_comp_lock(esw->dev->priv.hca_devcom_comp); if (esw->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_IB_ADEV || mlx5_core_mp_enabled(esw->dev)) { esw->mode = mode; - mlx5_rescan_drivers_locked(esw->dev); - mlx5_devcom_comp_unlock(esw->dev->priv.hca_devcom_comp); - return; + goto out; } esw->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; mlx5_rescan_drivers_locked(esw->dev); esw->mode = mode; esw->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; +out: mlx5_rescan_drivers_locked(esw->dev); mlx5_devcom_comp_unlock(esw->dev->priv.hca_devcom_comp); + mlx5_esw_reps_block(esw); } static void mlx5_esw_fdb_drop_destroy(struct mlx5_eswitch *esw) @@ -2642,6 +2688,7 @@ static void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw) mlx5_esw_for_each_rep(esw, i, rep) mlx5_esw_offloads_rep_cleanup(esw, rep); xa_destroy(&esw->offloads.vport_reps); + mutex_destroy(&esw->offloads.reps_lock); } static int esw_offloads_init_reps(struct mlx5_eswitch *esw) @@ -2651,6 +2698,7 @@ static int esw_offloads_init_reps(struct mlx5_eswitch *esw) int err; xa_init(&esw->offloads.vport_reps); + mutex_init(&esw->offloads.reps_lock); mlx5_esw_for_each_vport(esw, i, vport) { err = mlx5_esw_offloads_rep_add(esw, vport); @@ -2701,7 +2749,7 @@ static int esw_port_metadata_get(struct devlink *devlink, u32 id, } static int esw_port_metadata_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); @@ -2759,11 +2807,28 @@ void esw_offloads_cleanup(struct mlx5_eswitch *esw) } static int __esw_offloads_load_rep(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep, u8 rep_type) + struct mlx5_eswitch_rep *rep, + u8 rep_type, bool *newly_loaded) { + int err; + + mlx5_esw_assert_reps_locked(esw); + + if (newly_loaded) + *newly_loaded = false; + if (atomic_cmpxchg(&rep->rep_data[rep_type].state, - REP_REGISTERED, REP_LOADED) == REP_REGISTERED) - return esw->offloads.rep_ops[rep_type]->load(esw->dev, rep); + REP_REGISTERED, REP_LOADED) != REP_REGISTERED) + return 0; + + err = esw->offloads.rep_ops[rep_type]->load(esw->dev, rep); + if (err) { + atomic_set(&rep->rep_data[rep_type].state, REP_REGISTERED); + return err; + } + + if (newly_loaded) + *newly_loaded = true; return 0; } @@ -2771,6 +2836,8 @@ static int __esw_offloads_load_rep(struct mlx5_eswitch *esw, static void __esw_offloads_unload_rep(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep, u8 rep_type) { + mlx5_esw_assert_reps_locked(esw); + if (atomic_cmpxchg(&rep->rep_data[rep_type].state, REP_LOADED, REP_REGISTERED) == REP_LOADED) { if (rep_type == REP_ETH) @@ -2791,22 +2858,31 @@ static void __unload_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type) static int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num) { struct mlx5_eswitch_rep *rep; + unsigned long loaded = 0; + bool newly_loaded; int rep_type; int err; + if (vport_num != MLX5_VPORT_UPLINK && + mlx5_get_sd(esw->dev) && !mlx5_lag_is_active(esw->dev)) + return 0; + rep = mlx5_eswitch_get_rep(esw, vport_num); for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) { - err = __esw_offloads_load_rep(esw, rep, rep_type); + err = __esw_offloads_load_rep(esw, rep, rep_type, + &newly_loaded); if (err) goto err_reps; + if (newly_loaded) + loaded |= BIT(rep_type); } return 0; err_reps: - atomic_set(&rep->rep_data[rep_type].state, REP_REGISTERED); - for (--rep_type; rep_type >= 0; rep_type--) - __esw_offloads_unload_rep(esw, rep, rep_type); + while (--rep_type >= 0) + if (test_bit(rep_type, &loaded)) + __esw_offloads_unload_rep(esw, rep, rep_type); return err; } @@ -2969,6 +3045,136 @@ static int __esw_set_master_egress_rule(struct mlx5_core_dev *master, return err; } +static int esw_slave_egress_create_resources(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + struct mlx5_flow_table_attr ft_attr = { + .max_fte = 1, .prio = 0, .level = 0, + }; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_namespace *ns; + struct mlx5_flow_table *acl; + struct mlx5_flow_group *g; + u32 *flow_group_in; + int err = 0; + + if (vport->egress.acl) + return 0; + + xa_init_flags(&vport->egress.offloads.bounce_rules, XA_FLAGS_ALLOC); + ns = mlx5_get_flow_vport_namespace(esw->dev, + MLX5_FLOW_NAMESPACE_ESW_EGRESS, + vport->index); + if (!ns) + return -EINVAL; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + return -ENOMEM; + + if (vport->vport || mlx5_core_is_ecpf(esw->dev)) + ft_attr.flags = MLX5_FLOW_TABLE_OTHER_VPORT; + + acl = mlx5_create_vport_flow_table(ns, &ft_attr, vport->vport); + if (IS_ERR(acl)) { + err = PTR_ERR(acl); + goto out; + } + + g = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + goto err_table; + } + + vport->egress.acl = acl; + vport->egress.offloads.bounce_grp = g; + vport->egress.type = VPORT_EGRESS_ACL_TYPE_SHARED_FDB; + err = 0; + +err_table: + if (err && !IS_ERR_OR_NULL(acl)) { + mlx5_destroy_flow_table(acl); + vport->egress.acl = NULL; + } +out: + kvfree(flow_group_in); + return err; +} + +static void esw_slave_egress_destroy_resources(struct mlx5_vport *vport) +{ + if (!IS_ERR_OR_NULL(vport->egress.offloads.bounce_grp)) { + mlx5_destroy_flow_group(vport->egress.offloads.bounce_grp); + vport->egress.offloads.bounce_grp = NULL; + } + if (!IS_ERR_OR_NULL(vport->egress.acl)) { + esw_acl_egress_ofld_cleanup(vport); + xa_destroy(&vport->egress.offloads.bounce_rules); + } +} + +static int esw_set_slave_egress_rule(struct mlx5_core_dev *master, + struct mlx5_core_dev *slave) +{ + struct mlx5_eswitch *slave_esw = slave->priv.eswitch; + u16 master_vhca = MLX5_CAP_GEN(master, vhca_id); + struct mlx5_flow_destination dest = {}; + struct mlx5_flow_handle *bounce_rule; + struct mlx5_flow_act flow_act = {}; + struct mlx5_vport *slave_vport; + int err; + + slave_vport = mlx5_eswitch_get_vport(slave_esw, + slave_esw->manager_vport); + if (IS_ERR(slave_vport)) + return PTR_ERR(slave_vport); + + err = esw_slave_egress_create_resources(slave_esw, slave_vport); + if (err) + return err; + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; + dest.vport.num = master->priv.eswitch->manager_vport; + dest.vport.vhca_id = master_vhca; + dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID; + + bounce_rule = mlx5_add_flow_rules(slave_vport->egress.acl, NULL, + &flow_act, &dest, 1); + if (IS_ERR(bounce_rule)) { + err = PTR_ERR(bounce_rule); + goto err_rule; + } + err = xa_insert(&slave_vport->egress.offloads.bounce_rules, + master_vhca, bounce_rule, GFP_KERNEL); + if (err) + goto err_insert; + + return 0; +err_insert: + mlx5_del_flow_rules(bounce_rule); +err_rule: + esw_slave_egress_destroy_resources(slave_vport); + return err; +} + +static void esw_unset_slave_egress_rule(struct mlx5_core_dev *master, + struct mlx5_core_dev *slave) +{ + struct mlx5_eswitch *slave_esw = slave->priv.eswitch; + u16 master_vhca = MLX5_CAP_GEN(master, vhca_id); + struct mlx5_vport *slave_vport; + + slave_vport = mlx5_eswitch_get_vport(slave_esw, + slave_esw->manager_vport); + if (IS_ERR(slave_vport)) + return; + + esw_acl_egress_ofld_bounce_rule_destroy(slave_vport, master_vhca); + esw_slave_egress_destroy_resources(slave_vport); +} + static int esw_master_egress_create_resources(struct mlx5_eswitch *esw, struct mlx5_flow_namespace *egress_ns, struct mlx5_vport *vport, size_t count) @@ -3093,6 +3299,9 @@ static void esw_unset_master_egress_rule(struct mlx5_core_dev *dev, vport = mlx5_eswitch_get_vport(dev->priv.eswitch, dev->priv.eswitch->manager_vport); + if (!vport->egress.acl) + return; + esw_acl_egress_ofld_bounce_rule_destroy(vport, MLX5_CAP_GEN(slave_dev, vhca_id)); if (xa_empty(&vport->egress.offloads.bounce_rules)) { @@ -3111,6 +3320,9 @@ int mlx5_eswitch_offloads_single_fdb_add_one(struct mlx5_eswitch *master_esw, if (err) return err; + if (!mlx5_sd_is_primary(slave_esw->dev)) + return 0; + err = esw_set_master_egress_rule(master_esw->dev, slave_esw->dev, max_slaves); if (err) @@ -3130,6 +3342,18 @@ void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw, esw_unset_master_egress_rule(master_esw->dev, slave_esw->dev); } +int mlx5_eswitch_offloads_vport_lag_add_one(struct mlx5_eswitch *master_esw, + struct mlx5_eswitch *slave_esw) +{ + return esw_set_slave_egress_rule(master_esw->dev, slave_esw->dev); +} + +void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw, + struct mlx5_eswitch *slave_esw) +{ + esw_unset_slave_egress_rule(master_esw->dev, slave_esw->dev); +} + #define ESW_OFFLOADS_DEVCOM_PAIR (0) #define ESW_OFFLOADS_DEVCOM_UNPAIR (1) @@ -3225,6 +3449,18 @@ static int mlx5_esw_offloads_set_ns_peer(struct mlx5_eswitch *esw, return 0; } +bool mlx5_eswitch_is_peer(struct mlx5_eswitch *esw, + struct mlx5_eswitch *peer_esw) +{ + u16 peer_esw_i; + + if (!mlx5_esw_allowed(esw) || !mlx5_esw_allowed(peer_esw)) + return false; + + peer_esw_i = MLX5_CAP_GEN(peer_esw->dev, vhca_id); + return !!xa_load(&esw->paired, peer_esw_i); +} + static int mlx5_esw_offloads_devcom_event(int event, void *my_data, void *event_data) @@ -3318,7 +3554,7 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw, return; if ((MLX5_VPORT_MANAGER(esw->dev) || mlx5_core_is_ecpf_esw_manager(esw->dev)) && - !mlx5_lag_is_supported(esw->dev)) + (!mlx5_lag_is_supported(esw->dev) && !mlx5_get_sd(esw->dev))) return; xa_init(&esw->paired); @@ -3389,12 +3625,12 @@ u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw) u32 vport_end_ida = (1 << ESW_VPORT_BITS) - 1; /* Reserve 0xf for internal port offload */ u32 max_pf_num = (1 << ESW_PFNUM_BITS) - 2; - u32 pf_num; + int pf_num; int id; /* Only 4 bits of pf_num */ - pf_num = mlx5_get_dev_index(esw->dev); - if (pf_num > max_pf_num) + pf_num = mlx5_sd_pf_num_get(esw->dev); + if (pf_num < 0 || pf_num > max_pf_num) return 0; /* Metadata is 4 bits of PFNUM and 12 bits of unique id */ @@ -3439,6 +3675,7 @@ static void esw_offloads_vport_metadata_cleanup(struct mlx5_eswitch *esw, WARN_ON(vport->metadata != vport->default_metadata); mlx5_esw_match_metadata_free(esw, vport->default_metadata); + vport->default_metadata = 0; } static void esw_offloads_metadata_uninit(struct mlx5_eswitch *esw) @@ -3475,6 +3712,73 @@ static int esw_offloads_metadata_init(struct mlx5_eswitch *esw) return err; } +/* Deferred metadata init for SD devices: allocate vport metadata and + * refresh the ingress ACL for every vport whose ACL was created with + * metadata=0 in esw_create_offloads_acl_tables() / esw_vport_setup(). + * + * No Rep is loaded at this point ==> no Rep net-dev exists, so no need + * to take rtnl lock. + * + * Safe to call multiple times - subsequent calls are no-ops. + */ +int mlx5_esw_offloads_init_deferred_metadata(struct mlx5_eswitch *esw) +{ + struct mlx5_vport *manager, *vport; + unsigned long i; + int err; + + if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) + return 0; + + manager = mlx5_eswitch_get_vport(esw, esw->manager_vport); + if (IS_ERR(manager)) + return PTR_ERR(manager); + + /* Sanity check: skip if metadata was already initialized */ + if (manager->default_metadata) + return 0; + + err = esw_offloads_metadata_init(esw); + if (err) + return err; + + mutex_lock(&esw->state_lock); + /* Manager vport doesn't have a rep/netdev loaded but its ingress ACL + * was programmed with metadata=0 - refresh it explicitly. + */ + err = mlx5_esw_acl_ingress_vport_metadata_update(esw, + esw->manager_vport, + 0); + if (err) + goto err_acl; + + /* UPLINK is never marked enabled but its ACL is programmed in + * esw_create_offloads_acl_tables(); refresh it explicitly. + */ + err = mlx5_esw_acl_ingress_vport_metadata_update(esw, MLX5_VPORT_UPLINK, + 0); + if (err) + goto err_acl; + + mlx5_esw_for_each_vport(esw, i, vport) { + if (!vport || !vport->enabled) + continue; + err = mlx5_esw_acl_ingress_vport_metadata_update(esw, + vport->vport, + 0); + if (err) + goto err_acl; + } + + mutex_unlock(&esw->state_lock); + return 0; + +err_acl: + esw_offloads_metadata_uninit(esw); + mutex_unlock(&esw->state_lock); + return err; +} + int esw_vport_create_offloads_acl_tables(struct mlx5_eswitch *esw, struct mlx5_vport *vport) @@ -3547,6 +3851,21 @@ static void esw_destroy_offloads_acl_tables(struct mlx5_eswitch *esw) esw_vport_destroy_offloads_acl_tables(esw, vport); } +void mlx5_eswitch_unload_reps(struct mlx5_eswitch *esw) +{ + struct mlx5_eswitch_rep *rep; + unsigned long i; + + if (!esw || esw->mode != MLX5_ESWITCH_OFFLOADS) + return; + + mlx5_esw_for_each_rep(esw, i, rep) { + if (rep->vport == MLX5_VPORT_UPLINK) + continue; + mlx5_esw_offloads_rep_unload(esw, rep->vport); + } +} + int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) { struct mlx5_eswitch_rep *rep; @@ -3560,13 +3879,25 @@ int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) if (atomic_read(&rep->rep_data[REP_ETH].state) != REP_LOADED) return 0; - ret = __esw_offloads_load_rep(esw, rep, REP_IB); - if (ret) - return ret; + /* SD secondary devices share the primary's uplink and do not + * have their own uplink representor. Only load VF/SF vports. + */ + if (mlx5_sd_is_primary(esw->dev)) { + ret = __esw_offloads_load_rep(esw, rep, REP_IB, NULL); + if (ret) + return ret; + } mlx5_esw_for_each_rep(esw, i, rep) { + if (!mlx5_sd_is_primary(esw->dev) && + rep->vport == MLX5_VPORT_UPLINK) + continue; + if (rep->vport != MLX5_VPORT_UPLINK && + mlx5_get_sd(esw->dev) && !mlx5_lag_is_active(esw->dev)) + continue; + if (atomic_read(&rep->rep_data[REP_ETH].state) == REP_LOADED) - __esw_offloads_load_rep(esw, rep, REP_IB); + __esw_offloads_load_rep(esw, rep, REP_IB, NULL); } return 0; @@ -3655,29 +3986,23 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) mutex_destroy(&esw->fdb_table.offloads.vports.lock); } -static void -esw_vfs_changed_event_handler(struct mlx5_eswitch *esw, int work_gen, - const u32 *out) +static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw) { - struct devlink *devlink; - bool host_pf_disabled; + struct mlx5_esw_pf_info host_pf_info; u16 new_num_vfs; + const u32 *out; - devlink = priv_to_devlink(esw->dev); - devl_lock(devlink); + out = mlx5_esw_query_functions(esw->dev); + if (IS_ERR(out)) + return; - /* Stale work from one or more mode changes ago. Bail out. */ - if (work_gen != atomic_read(&esw->esw_funcs.generation)) - goto unlock; + host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, out); + new_num_vfs = host_pf_info.num_of_vfs; - new_num_vfs = MLX5_GET(query_esw_functions_out, out, - host_params_context.host_num_of_vfs); - host_pf_disabled = MLX5_GET(query_esw_functions_out, out, - host_params_context.host_pf_disabled); - - if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_disabled) - goto unlock; + if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_info.pf_disabled) + goto free; + mlx5_esw_reps_block(esw); /* Number of VFs can only change from "0 to x" or "x to 0". */ if (esw->esw_funcs.num_vfs > 0) { mlx5_eswitch_unload_vf_vports(esw, esw->esw_funcs.num_vfs); @@ -3686,77 +4011,99 @@ esw_vfs_changed_event_handler(struct mlx5_eswitch *esw, int work_gen, err = mlx5_eswitch_load_vf_vports(esw, new_num_vfs, MLX5_VPORT_UC_ADDR_CHANGE); - if (err) { - devl_unlock(devlink); - return; - } + if (err) + goto unblock; } esw->esw_funcs.num_vfs = new_num_vfs; -unlock: - devl_unlock(devlink); +unblock: + mlx5_esw_reps_unblock(esw); +free: + kvfree(out); } -static void esw_functions_changed_event_handler(struct work_struct *work) +static void esw_wq_handler(struct work_struct *work) { struct mlx5_host_work *host_work; struct mlx5_eswitch *esw; - const u32 *out; + struct devlink *devlink; + int work_gen; host_work = container_of(work, struct mlx5_host_work, work); esw = host_work->esw; + work_gen = host_work->work_gen; + devlink = priv_to_devlink(esw->dev); - out = mlx5_esw_query_functions(esw->dev); - if (IS_ERR(out)) - goto out; + /* Do not block on devlink lock until stale work is filtered out. + * Teardown can invalidate the generation and then wait for this + * workqueue while holding devlink lock. + */ + for (;;) { + if (work_gen != atomic_read(&esw->generation)) + goto free; - esw_vfs_changed_event_handler(esw, host_work->work_gen, out); - kvfree(out); -out: + if (devl_trylock(devlink)) + break; + + wait_event_timeout(esw->work_queue_wait, + work_gen != atomic_read(&esw->generation), + msecs_to_jiffies(60)); + } + + /* Stale work from one or more mode changes ago. Bail out. */ + if (work_gen != atomic_read(&esw->generation)) + goto unlock; + + host_work->func(esw); + +unlock: + devl_unlock(devlink); +free: kfree(host_work); } -int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, unsigned long type, void *data) +static int mlx5_esw_add_work(struct mlx5_eswitch *esw, + void (*func)(struct mlx5_eswitch *esw), + gfp_t gfp) +{ + struct mlx5_host_work *host_work; + + host_work = kzalloc_obj(*host_work, gfp); + if (!host_work) + return -ENOMEM; + + host_work->esw = esw; + host_work->work_gen = atomic_read(&esw->generation); + + host_work->func = func; + INIT_WORK(&host_work->work, esw_wq_handler); + queue_work(esw->work_queue, &host_work->work); + + return 0; +} + +int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, + unsigned long type, void *data) { struct mlx5_esw_functions *esw_funcs; - struct mlx5_host_work *host_work; struct mlx5_eswitch *esw; - - host_work = kzalloc_obj(*host_work, GFP_ATOMIC); - if (!host_work) - return NOTIFY_DONE; + int ret; esw_funcs = mlx5_nb_cof(nb, struct mlx5_esw_functions, nb); esw = container_of(esw_funcs, struct mlx5_eswitch, esw_funcs); - host_work->esw = esw; - host_work->work_gen = atomic_read(&esw_funcs->generation); - - INIT_WORK(&host_work->work, esw_functions_changed_event_handler); - queue_work(esw->work_queue, &host_work->work); + ret = mlx5_esw_add_work(esw, esw_vfs_changed_event_handler, + GFP_ATOMIC); + if (ret) + return NOTIFY_DONE; return NOTIFY_OK; } -static int mlx5_esw_host_number_init(struct mlx5_eswitch *esw) -{ - const u32 *query_host_out; - - if (!mlx5_core_is_ecpf_esw_manager(esw->dev)) - return 0; - - query_host_out = mlx5_esw_query_functions(esw->dev); - if (IS_ERR(query_host_out)) - return PTR_ERR(query_host_out); - - /* Mark non local controller with non zero controller number. */ - esw->offloads.host_number = MLX5_GET(query_esw_functions_out, query_host_out, - host_params_context.host_number); - kvfree(query_host_out); - return 0; -} - bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 controller) { + const struct mlx5_esw_functions *esw_funcs; + int i; + /* Local controller is always valid */ if (controller == 0) return true; @@ -3765,12 +4112,21 @@ bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 cont return false; /* External host number starts with zero in device */ - return (controller == esw->offloads.host_number + 1); + if (controller == mlx5_esw_get_hpf_host_number(esw->dev) + 1) + return true; + + esw_funcs = &esw->esw_funcs; + for (i = 0; i < esw_funcs->num_spfs; i++) { + if (controller == esw_funcs->spfs[i].host_number + 1) + return true; + } + return false; } int esw_offloads_enable(struct mlx5_eswitch *esw) { u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES]; + struct mlx5_devcom_match_attr attr = {}; struct mapping_ctx *reg_c0_obj_pool; struct mlx5_vport *vport; unsigned long i; @@ -3784,13 +4140,14 @@ int esw_offloads_enable(struct mlx5_eswitch *esw) if (err) goto err_roce; - err = mlx5_esw_host_number_init(esw); - if (err) - goto err_metadata; - - err = esw_offloads_metadata_init(esw); - if (err) - goto err_metadata; + /* SD devices defer metadata init until SD is ready and + * mlx5_sd_pf_num_get() can return the correct pf_num. + */ + if (!mlx5_get_sd(esw->dev)) { + err = esw_offloads_metadata_init(esw); + if (err) + goto err_metadata; + } err = esw_set_passing_vport_metadata(esw, true); if (err) @@ -3835,6 +4192,10 @@ int esw_offloads_enable(struct mlx5_eswitch *esw) if (err) goto err_vports; + memcpy(attr.key.buf, mapping_id, id_len); + attr.flags = MLX5_DEVCOM_MATCH_FLAGS_NS; + attr.net = mlx5_core_net(esw->dev); + mlx5_esw_offloads_devcom_init(esw, &attr); return 0; err_vports: @@ -3879,6 +4240,7 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw, void esw_offloads_disable(struct mlx5_eswitch *esw) { + mlx5_esw_offloads_devcom_cleanup(esw); mlx5_eswitch_disable_pf_vf_vports(esw); mlx5_esw_offloads_rep_unload(esw, MLX5_VPORT_UPLINK); esw_set_passing_vport_metadata(esw, false); @@ -4110,12 +4472,6 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, if (esw_mode_from_devlink(mode, &mlx5_mode)) return -EINVAL; - if (mlx5_mode == MLX5_ESWITCH_OFFLOADS && mlx5_get_sd(esw->dev)) { - NL_SET_ERR_MSG_MOD(extack, - "Can't change E-Switch mode to switchdev when multi-PF netdev (Socket Direct) is configured."); - return -EPERM; - } - /* Avoid try_lock, active/inactive mode change is not restricted */ if (mlx5_devlink_switchdev_active_mode_change(esw, mode)) return 0; @@ -4139,9 +4495,14 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, goto unlock; } + /* Keep mode_lock and reps_lock unnested. The operation flag excludes + * mode users while mode_lock is dropped before taking reps_lock. + */ esw->eswitch_operation_in_progress = true; up_write(&esw->mode_lock); + mlx5_esw_reps_block(esw); + if (mlx5_mode == MLX5_ESWITCH_OFFLOADS && !mlx5_devlink_netdev_netns_immutable_set(devlink, true)) { NL_SET_ERR_MSG_MOD(extack, @@ -4174,12 +4535,19 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, skip: if (mlx5_mode == MLX5_ESWITCH_OFFLOADS && err) mlx5_devlink_netdev_netns_immutable_set(devlink, false); + /* Reconfiguration is done; drop reps_lock before taking mode_lock again + * to clear the operation flag. + */ + mlx5_esw_reps_unblock(esw); down_write(&esw->mode_lock); esw->eswitch_operation_in_progress = false; unlock: mlx5_esw_unlock(esw); enable_lag: mlx5_lag_enable_change(esw->dev); + /* Shared FDB activation is creating LAG which is changing reps. */ + if (!err) + mlx5_sd_eswitch_mode_set(esw->dev, mlx5_mode); return err; } @@ -4435,7 +4803,7 @@ static bool mlx5_eswitch_vport_has_rep(const struct mlx5_eswitch *esw, u16 vport_num) { /* Currently, only ECPF based device has representor for host PF. */ - if (vport_num == MLX5_VPORT_PF && + if (vport_num == MLX5_VPORT_HOST_PF && (!mlx5_core_is_ecpf_esw_manager(esw->dev) || !mlx5_esw_host_functions_enabled(esw->dev))) return false; @@ -4447,9 +4815,10 @@ mlx5_eswitch_vport_has_rep(const struct mlx5_eswitch *esw, u16 vport_num) return true; } -void mlx5_eswitch_register_vport_reps(struct mlx5_eswitch *esw, - const struct mlx5_eswitch_rep_ops *ops, - u8 rep_type) +static void +mlx5_eswitch_register_vport_reps_blocked(struct mlx5_eswitch *esw, + const struct mlx5_eswitch_rep_ops *ops, + u8 rep_type) { struct mlx5_eswitch_rep_data *rep_data; struct mlx5_eswitch_rep *rep; @@ -4464,25 +4833,130 @@ void mlx5_eswitch_register_vport_reps(struct mlx5_eswitch *esw, } } } + +static void mlx5_eswitch_reload_reps_blocked(struct mlx5_eswitch *esw) +{ + struct mlx5_eswitch_rep *uplink; + struct mlx5_vport *vport; + bool newly_loaded; + unsigned long i; + + if (esw->mode != MLX5_ESWITCH_OFFLOADS) + return; + + uplink = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK); + if (__esw_offloads_load_rep(esw, uplink, REP_ETH, &newly_loaded)) + return; + if (mlx5_sd_is_primary(esw->dev) && + __esw_offloads_load_rep(esw, uplink, REP_IB, NULL)) { + if (newly_loaded) + __esw_offloads_unload_rep(esw, uplink, REP_ETH); + return; + } + + if (mlx5_get_sd(esw->dev) && !mlx5_lag_is_active(esw->dev)) + return; + + mlx5_esw_for_each_vport(esw, i, vport) { + if (!vport) + continue; + if (!vport->enabled) + continue; + if (vport->vport == MLX5_VPORT_UPLINK) + continue; + if (!mlx5_eswitch_vport_has_rep(esw, vport->vport)) + continue; + + mlx5_esw_offloads_rep_load(esw, vport->vport); + } +} + +static void mlx5_eswitch_reload_reps(struct mlx5_eswitch *esw) +{ + mlx5_esw_reps_block(esw); + mlx5_eswitch_reload_reps_blocked(esw); + mlx5_esw_reps_unblock(esw); +} + +static void +mlx5_eswitch_register_vport_reps_locked(struct mlx5_eswitch *esw, + const struct mlx5_eswitch_rep_ops *ops, + u8 rep_type, bool nested) +{ + if (nested) + mlx5_esw_reps_block_nested(esw); + else + mlx5_esw_reps_block(esw); + mlx5_eswitch_register_vport_reps_blocked(esw, ops, rep_type); + mlx5_esw_reps_unblock(esw); + + mlx5_esw_add_work(esw, mlx5_eswitch_reload_reps, GFP_KERNEL); +} + +void mlx5_eswitch_register_vport_reps(struct mlx5_eswitch *esw, + const struct mlx5_eswitch_rep_ops *ops, + u8 rep_type) +{ + mlx5_eswitch_register_vport_reps_locked(esw, ops, rep_type, false); +} EXPORT_SYMBOL(mlx5_eswitch_register_vport_reps); -void mlx5_eswitch_unregister_vport_reps(struct mlx5_eswitch *esw, u8 rep_type) +void +mlx5_eswitch_register_vport_reps_nested(struct mlx5_eswitch *esw, + const struct mlx5_eswitch_rep_ops *ops, + u8 rep_type) +{ + mlx5_eswitch_register_vport_reps_locked(esw, ops, rep_type, true); +} +EXPORT_SYMBOL(mlx5_eswitch_register_vport_reps_nested); + +static void +mlx5_eswitch_unregister_vport_reps_blocked(struct mlx5_eswitch *esw, + u8 rep_type) { struct mlx5_eswitch_rep *rep; unsigned long i; - if (esw->mode == MLX5_ESWITCH_OFFLOADS) - __unload_reps_all_vport(esw, rep_type); + __unload_reps_all_vport(esw, rep_type); mlx5_esw_for_each_rep(esw, i, rep) atomic_set(&rep->rep_data[rep_type].state, REP_UNREGISTERED); + + esw->offloads.rep_ops[rep_type] = NULL; +} + +static void +mlx5_eswitch_unregister_vport_reps_locked(struct mlx5_eswitch *esw, + u8 rep_type, bool nested) +{ + if (nested) + mlx5_esw_reps_block_nested(esw); + else + mlx5_esw_reps_block(esw); + mlx5_eswitch_unregister_vport_reps_blocked(esw, rep_type); + mlx5_esw_reps_unblock(esw); +} + +void mlx5_eswitch_unregister_vport_reps(struct mlx5_eswitch *esw, u8 rep_type) +{ + mlx5_eswitch_unregister_vport_reps_locked(esw, rep_type, false); } EXPORT_SYMBOL(mlx5_eswitch_unregister_vport_reps); +void mlx5_eswitch_unregister_vport_reps_nested(struct mlx5_eswitch *esw, + u8 rep_type) +{ + mlx5_eswitch_unregister_vport_reps_locked(esw, rep_type, true); +} +EXPORT_SYMBOL(mlx5_eswitch_unregister_vport_reps_nested); + void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type) { + struct mlx5_core_dev *primary = mlx5_sd_get_primary(esw->dev); struct mlx5_eswitch_rep *rep; + if (primary) + esw = primary->priv.eswitch; rep = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK); return rep->rep_data[rep_type].priv; } @@ -4504,6 +4978,11 @@ EXPORT_SYMBOL(mlx5_eswitch_get_proto_dev); void *mlx5_eswitch_uplink_get_proto_dev(struct mlx5_eswitch *esw, u8 rep_type) { + struct mlx5_core_dev *primary = mlx5_sd_get_primary(esw->dev); + + if (primary) + esw = primary->priv.eswitch; + return mlx5_eswitch_get_proto_dev(esw, MLX5_VPORT_UPLINK, rep_type); } EXPORT_SYMBOL(mlx5_eswitch_uplink_get_proto_dev); @@ -4640,8 +5119,9 @@ int mlx5_devlink_port_fn_migratable_get(struct devlink_port *port, bool *is_enab return -EOPNOTSUPP; } - if (!MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) { - NL_SET_ERR_MSG_MOD(extack, "Device doesn't support VHCA management"); + if (!MLX5_CAP_GEN(esw->dev, vport_group_manager)) { + NL_SET_ERR_MSG_MOD(extack, + "Device doesn't support vport group management"); return -EOPNOTSUPP; } @@ -4716,8 +5196,9 @@ int mlx5_devlink_port_fn_roce_get(struct devlink_port *port, bool *is_enabled, struct mlx5_eswitch *esw = mlx5_devlink_eswitch_nocheck_get(port->devlink); struct mlx5_vport *vport = mlx5_devlink_port_vport_get(port); - if (!MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) { - NL_SET_ERR_MSG_MOD(extack, "Device doesn't support VHCA management"); + if (!MLX5_CAP_GEN(esw->dev, vport_group_manager)) { + NL_SET_ERR_MSG_MOD(extack, + "Device doesn't support vport group management"); return -EOPNOTSUPP; } @@ -4766,8 +5247,8 @@ int mlx5_devlink_port_fn_roce_set(struct devlink_port *port, bool enable, hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); MLX5_SET(cmd_hca_cap, hca_caps, roce, enable); - err = mlx5_vport_set_other_func_cap(esw->dev, hca_caps, vport_num, - MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE); + err = mlx5_vport_set_other_func_general_cap(esw->dev, hca_caps, + vport_num); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed setting HCA roce cap"); goto out_free; @@ -4788,10 +5269,11 @@ int mlx5_devlink_pf_port_fn_state_get(struct devlink_port *port, struct netlink_ext_ack *extack) { struct mlx5_vport *vport = mlx5_devlink_port_vport_get(port); + struct mlx5_eswitch *esw = vport->dev->priv.eswitch; const u32 *query_out; bool pf_disabled; - if (vport->vport != MLX5_VPORT_PF) { + if (mlx5_eswitch_is_vf_vport(esw, vport->vport)) { NL_SET_ERR_MSG_MOD(extack, "State get is not supported for VF"); return -EOPNOTSUPP; } @@ -4803,8 +5285,16 @@ int mlx5_devlink_pf_port_fn_state_get(struct devlink_port *port, if (IS_ERR(query_out)) return PTR_ERR(query_out); - pf_disabled = MLX5_GET(query_esw_functions_out, query_out, - host_params_context.host_pf_disabled); + if (vport->vport == MLX5_VPORT_HOST_PF) { + struct mlx5_esw_pf_info host_pf_info; + + host_pf_info = mlx5_esw_get_host_pf_info(vport->dev, + query_out); + pf_disabled = host_pf_info.pf_disabled; + } else { + pf_disabled = mlx5_esw_get_spf_disabled(vport->dev, query_out, + vport->vhca_id); + } *opstate = pf_disabled ? DEVLINK_PORT_FN_OPSTATE_DETACHED : DEVLINK_PORT_FN_OPSTATE_ATTACHED; @@ -4818,9 +5308,10 @@ int mlx5_devlink_pf_port_fn_state_set(struct devlink_port *port, struct netlink_ext_ack *extack) { struct mlx5_vport *vport = mlx5_devlink_port_vport_get(port); + struct mlx5_eswitch *esw = vport->dev->priv.eswitch; struct mlx5_core_dev *dev; - if (vport->vport != MLX5_VPORT_PF) { + if (mlx5_eswitch_is_vf_vport(esw, vport->vport)) { NL_SET_ERR_MSG_MOD(extack, "State set is not supported for VF"); return -EOPNOTSUPP; } @@ -4829,9 +5320,9 @@ int mlx5_devlink_pf_port_fn_state_set(struct devlink_port *port, switch (state) { case DEVLINK_PORT_FN_STATE_ACTIVE: - return mlx5_esw_host_pf_enable_hca(dev); + return mlx5_esw_pf_enable_hca(dev, vport->vport); case DEVLINK_PORT_FN_STATE_INACTIVE: - return mlx5_esw_host_pf_disable_hca(dev); + return mlx5_esw_pf_disable_hca(dev, vport->vport); default: return -EOPNOTSUPP; } @@ -5036,9 +5527,9 @@ mlx5_devlink_port_fn_max_io_eqs_get(struct devlink_port *port, u32 *max_io_eqs, int err; esw = mlx5_devlink_eswitch_nocheck_get(port->devlink); - if (!MLX5_CAP_GEN(esw->dev, vhca_resource_manager)) { + if (!MLX5_CAP_GEN(esw->dev, vport_group_manager)) { NL_SET_ERR_MSG_MOD(extack, - "Device doesn't support VHCA management"); + "Device doesn't support vport group management"); return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 1cd4cd898ec2..8af73393770c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -1217,3 +1217,24 @@ int mlx5_fs_cmd_set_tx_flow_table_root(struct mlx5_core_dev *dev, u32 ft_id, boo return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } + +int mlx5_fs_cmd_query_l2table_silent(struct mlx5_core_dev *dev, u8 *silent_mode) +{ + u32 out[MLX5_ST_SZ_DW(query_l2_table_entry_out)] = {}; + u32 in[MLX5_ST_SZ_DW(query_l2_table_entry_in)] = {}; + int err; + + if (!MLX5_CAP_GEN(dev, silent_mode_query)) + return -EOPNOTSUPP; + + MLX5_SET(query_l2_table_entry_in, in, opcode, + MLX5_CMD_OP_QUERY_L2_TABLE_ENTRY); + MLX5_SET(query_l2_table_entry_in, in, silent_mode_query, 1); + + err = mlx5_cmd_exec_inout(dev, query_l2_table_entry, in, out); + if (err) + return err; + + *silent_mode = MLX5_GET(query_l2_table_entry_out, out, silent_mode); + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 7eb7b3ffe3d8..60280ff7da50 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -124,6 +124,8 @@ const struct mlx5_flow_cmds *mlx5_fs_cmd_get_fw_cmds(void); int mlx5_fs_cmd_set_l2table_entry_silent(struct mlx5_core_dev *dev, u8 silent_mode); int mlx5_fs_cmd_set_tx_flow_table_root(struct mlx5_core_dev *dev, u32 ft_id, bool disconnect); +int mlx5_fs_cmd_query_l2table_silent(struct mlx5_core_dev *dev, + u8 *silent_mode); static inline bool mlx5_fs_cmd_is_fw_term_table(struct mlx5_flow_table *ft) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 61a6ba1e49dd..c8f6adae6f51 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -3765,11 +3765,11 @@ static int init_egress_root_ns(struct mlx5_flow_steering *steering) } static int mlx5_fs_mode_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); - char *value = val.vstr; + char *value = val->vstr; u8 eswitch_mode; eswitch_mode = mlx5_eswitch_mode(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index 3b2f54ca30a8..9b3b32408c64 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -285,6 +285,8 @@ const struct ethtool_ops mlx5i_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USE_ADAPTIVE, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM, .get_drvinfo = mlx5i_get_drvinfo, .get_strings = mlx5i_get_strings, .get_sset_count = mlx5i_get_sset_count, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index f8e70ac5a85b..28d16fdc3f06 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -139,9 +139,44 @@ static int mlx5_cmd_modify_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev, return mlx5_cmd_exec_in(dev, modify_lag, in); } +static u32 mlx5_lag_dev_group_id(struct mlx5_core_dev *dev) +{ + struct mlx5_lag *ldev = mlx5_lag_dev(dev); + struct lag_func *pf; + int i; + + if (!ldev) + return 0; + + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { + pf = mlx5_lag_pf(ldev, i); + if (pf->dev == dev) + return pf->sd_fdb_active ? pf->group_id : 0; + } + return 0; +} + +static int mlx5_lag_is_sw_lag(struct mlx5_core_dev *dev) +{ + return mlx5_lag_is_sd(dev); +} + int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev) { u32 in[MLX5_ST_SZ_DW(create_vport_lag_in)] = {}; + struct mlx5_lag *ldev = mlx5_lag_dev(dev); + int ret; + + if (mlx5_lag_is_sw_lag(dev)) { + if (!ldev) + return -ENODEV; + + mutex_lock(&ldev->lock); + ret = mlx5_lag_create_vport_lag(mlx5_lag_dev(dev), + mlx5_lag_dev_group_id(dev)); + mutex_unlock(&ldev->lock); + return ret; + } MLX5_SET(create_vport_lag_in, in, opcode, MLX5_CMD_OP_CREATE_VPORT_LAG); @@ -152,6 +187,18 @@ EXPORT_SYMBOL(mlx5_cmd_create_vport_lag); int mlx5_cmd_destroy_vport_lag(struct mlx5_core_dev *dev) { u32 in[MLX5_ST_SZ_DW(destroy_vport_lag_in)] = {}; + struct mlx5_lag *ldev = mlx5_lag_dev(dev); + + if (mlx5_lag_is_sw_lag(dev)) { + if (!ldev) + return 0; + + mutex_lock(&ldev->lock); + mlx5_lag_destroy_vport_lag(mlx5_lag_dev(dev), + mlx5_lag_dev_group_id(dev)); + mutex_unlock(&ldev->lock); + return 0; + } MLX5_SET(destroy_vport_lag_in, in, opcode, MLX5_CMD_OP_DESTROY_VPORT_LAG); @@ -242,7 +289,7 @@ static void mlx5_ldev_free(struct kref *ref) unregister_netdevice_notifier_net(net, &ldev->nb); } - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { pf = mlx5_lag_pf(ldev, i); if (pf->port_change_nb.nb.notifier_call) { struct mlx5_nb *nb = &pf->port_change_nb; @@ -293,11 +340,14 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev) INIT_DELAYED_WORK(&ldev->bond_work, mlx5_do_bond_work); INIT_WORK(&ldev->speed_update_work, mlx5_mpesw_speed_update_work); - ldev->nb.notifier_call = mlx5_lag_netdev_event; - write_pnet(&ldev->net, mlx5_core_net(dev)); - if (register_netdevice_notifier_net(read_pnet(&ldev->net), &ldev->nb)) { - ldev->nb.notifier_call = NULL; - mlx5_core_err(dev, "Failed to register LAG netdev notifier\n"); + if (!mlx5_sd_is_supported(dev)) { + ldev->nb.notifier_call = mlx5_lag_netdev_event; + write_pnet(&ldev->net, mlx5_core_net(dev)); + if (register_netdevice_notifier_net(read_pnet(&ldev->net), + &ldev->nb)) { + ldev->nb.notifier_call = NULL; + mlx5_core_err(dev, "Failed to register LAG netdev notifier\n"); + } } ldev->mode = MLX5_LAG_MODE_NONE; @@ -370,6 +420,22 @@ int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq) return -ENOENT; } +/* Return the appropriate iterator filter for a device in LAG: + * - SD shared FDB active: iterate only the device's SD group + * - SD group exists but shared FDB not active: iterate all devices + * - No SD: iterate ports only + */ +static u32 mlx5_lag_get_filter(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + if (pf && pf->sd_fdb_active) + return pf->group_id; + if (pf && pf->group_id) + return MLX5_LAG_FILTER_ALL; + return MLX5_LAG_FILTER_PORTS; +} + /* Reverse of mlx5_lag_get_dev_index_by_seq: given a device, return its * sequence number in the LAG. Master is always 0, others numbered * sequentially starting from 1. @@ -379,11 +445,13 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) struct mlx5_lag *ldev = mlx5_lag_dev(dev); int master_idx, i, num = 1; struct lag_func *pf; + u32 filter; if (!ldev) return -ENOENT; - master_idx = mlx5_lag_get_master_idx(ldev); + filter = mlx5_lag_get_filter(ldev, dev); + master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, 0, filter); if (master_idx < 0) return -ENOENT; @@ -391,7 +459,7 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) if (pf && pf->dev == dev) return 0; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, filter) { if (i == master_idx) continue; pf = mlx5_lag_pf(ldev, i); @@ -403,6 +471,69 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev) } EXPORT_SYMBOL(mlx5_lag_get_dev_seq); +/* seq 0 = master, then all remaining devices */ +static int mlx5_lag_get_dev_index_by_seq_all(struct mlx5_lag *ldev, int seq) +{ + int master_idx, i, num = 0; + + master_idx = mlx5_lag_get_master_idx(ldev); + + if (master_idx >= 0) { + if (seq == 0) + return master_idx; + num++; + } + + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { + if (i == master_idx) + continue; + if (num == seq) + return i; + num++; + } + return -ENOENT; +} + +/* From group POV, port-marked entry is the lag master */ +static int mlx5_lag_get_dev_index_by_seq_group(struct mlx5_lag *ldev, int seq, + u32 group_id) +{ + int i, num = 0; + + mlx5_lag_for_each(i, 0, ldev, group_id) { + if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) { + if (seq == 0) + return i; + num++; + break; + } + } + + mlx5_lag_for_each(i, 0, ldev, group_id) { + if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) + continue; + if (num == seq) + return i; + num++; + } + return -ENOENT; +} + +int mlx5_lag_get_dev_index_by_seq_filter(struct mlx5_lag *ldev, int seq, + u32 filter) +{ + if (!ldev) + return -ENOENT; + + if (!filter || filter == MLX5_LAG_FILTER_PORTS) + return mlx5_lag_get_dev_index_by_seq(ldev, seq); + + if (filter == MLX5_LAG_FILTER_ALL) + return mlx5_lag_get_dev_index_by_seq_all(ldev, seq); + + return mlx5_lag_get_dev_index_by_seq_group(ldev, seq, filter); +} + /* Devcom events for LAG master marking */ #define LAG_DEVCOM_PAIR (0) #define LAG_DEVCOM_UNPAIR (1) @@ -512,6 +643,14 @@ static bool __mlx5_lag_is_sriov(struct mlx5_lag *ldev) return ldev->mode == MLX5_LAG_MODE_SRIOV; } +static bool __mlx5_lag_is_sd_active(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + return pf && pf->sd_fdb_active; +} + /* Create a mapping between steering slots and active ports. * As we have ldev->buckets slots per port first assume the native * mapping should be used. @@ -817,43 +956,6 @@ char *mlx5_get_str_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags) } } -static int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) -{ - int master_idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); - struct mlx5_eswitch *master_esw; - struct mlx5_core_dev *dev0; - int i, j; - int err; - - if (master_idx < 0) - return -EINVAL; - - dev0 = mlx5_lag_pf(ldev, master_idx)->dev; - master_esw = dev0->priv.eswitch; - mlx5_ldev_for_each(i, 0, ldev) { - struct mlx5_eswitch *slave_esw; - - if (i == master_idx) - continue; - - slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; - - err = mlx5_eswitch_offloads_single_fdb_add_one(master_esw, - slave_esw, ldev->ports); - if (err) - goto err; - } - return 0; -err: - mlx5_ldev_for_each_reverse(j, i, 0, ldev) { - if (j == master_idx) - continue; - mlx5_eswitch_offloads_single_fdb_del_one(master_esw, - mlx5_lag_pf(ldev, j)->dev->priv.eswitch); - } - return err; -} - static int mlx5_create_lag(struct mlx5_lag *ldev, struct lag_tracker *tracker, enum mlx5_lag_mode mode, @@ -964,27 +1066,19 @@ int mlx5_deactivate_lag(struct mlx5_lag *ldev) u32 in[MLX5_ST_SZ_DW(destroy_lag_in)] = {}; bool roce_lag = __mlx5_lag_is_roce(ldev); unsigned long flags = ldev->mode_flags; - struct mlx5_eswitch *master_esw; struct mlx5_core_dev *dev0; int err; - int i; if (master_idx < 0) return -EINVAL; dev0 = mlx5_lag_pf(ldev, master_idx)->dev; - master_esw = dev0->priv.eswitch; ldev->mode = MLX5_LAG_MODE_NONE; ldev->mode_flags = 0; mlx5_lag_mp_reset(ldev); if (test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &flags)) { - mlx5_ldev_for_each(i, 0, ldev) { - if (i == master_idx) - continue; - mlx5_eswitch_offloads_single_fdb_del_one(master_esw, - mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - } + mlx5_lag_destroy_single_fdb(ldev); clear_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &flags); } @@ -1063,36 +1157,187 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev) return true; } -void mlx5_lag_add_devices(struct mlx5_lag *ldev) +static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev, u32 filter) { + struct mlx5_devcom_comp_dev *devcom = NULL; struct lag_func *pf; int i; - mlx5_ldev_for_each(i, 0, ldev) { + lockdep_assert_held(&ldev->lock); + + i = mlx5_get_next_lag_func(ldev, 0, filter); + if (i < MLX5_MAX_PORTS) { + pf = mlx5_lag_pf(ldev, i); + if (filter == MLX5_LAG_FILTER_PORTS || + filter == MLX5_LAG_FILTER_ALL) + devcom = pf->dev->priv.hca_devcom_comp; + else + devcom = mlx5_sd_get_devcom(pf->dev); + } + mlx5_devcom_comp_assert_locked(devcom); +} + +static void mlx5_lag_drop_lock_for_reps(struct mlx5_lag *ldev, u32 filter) +{ + mlx5_lag_assert_locked_transition(ldev, filter); + + /* Keep PF membership stable while ldev->lock is dropped. Device add + * and remove paths observe mode_changes_in_progress and retry. + */ + ldev->mode_changes_in_progress++; + mutex_unlock(&ldev->lock); +} + +static void mlx5_lag_retake_lock_after_reps(struct mlx5_lag *ldev) +{ + mutex_lock(&ldev->lock); + ldev->mode_changes_in_progress--; +} + +void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev, + bool enable) +{ + if (dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) + return; + + if (enable) + dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + else + dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + + /* Auxiliary bus probe/remove can register or unregister representor + * callbacks and take reps_lock. Drop ldev->lock so the only ordering + * remains reps_lock -> ldev->lock from representor callbacks. + */ + mlx5_lag_drop_lock_for_reps(ldev, mlx5_lag_get_filter(ldev, dev)); + mlx5_rescan_drivers_locked(dev); + mlx5_lag_retake_lock_after_reps(ldev); +} + +static void mlx5_lag_rescan_devices_locked_filter(struct mlx5_lag *ldev, + bool enable, u32 filter) +{ + struct mlx5_core_dev *devs[MLX5_MAX_PORTS]; + struct lag_func *pf; + int num_devs = 0; + int i; + + mlx5_lag_assert_locked_transition(ldev, filter); + + mlx5_lag_for_each(i, 0, ldev, filter) { pf = mlx5_lag_pf(ldev, i); if (pf->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) continue; - pf->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(pf->dev); + if (enable) + pf->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + else + pf->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; + devs[num_devs++] = pf->dev; } + + mlx5_lag_drop_lock_for_reps(ldev, filter); + for (i = 0; i < num_devs; i++) + mlx5_rescan_drivers_locked(devs[i]); + mlx5_lag_retake_lock_after_reps(ldev); +} + +void mlx5_lag_add_devices_filter(struct mlx5_lag *ldev, u32 filter) +{ + mlx5_lag_rescan_devices_locked_filter(ldev, true, filter); +} + +void mlx5_lag_add_devices(struct mlx5_lag *ldev) +{ + mlx5_lag_add_devices_filter(ldev, MLX5_LAG_FILTER_PORTS); +} + +void mlx5_lag_remove_devices_filter(struct mlx5_lag *ldev, u32 filter) +{ + mlx5_lag_rescan_devices_locked_filter(ldev, false, filter); } void mlx5_lag_remove_devices(struct mlx5_lag *ldev) +{ + mlx5_lag_remove_devices_filter(ldev, MLX5_LAG_FILTER_PORTS); +} + +static int mlx5_lag_reload_ib_reps_unlocked(struct mlx5_lag *ldev, u32 flags, + u32 filter, bool cont_on_fail) +{ + struct lag_func *pf; + int ret; + int i; + + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + if (!(pf->dev->priv.flags & flags)) { + struct mlx5_eswitch *esw; + + esw = pf->dev->priv.eswitch; + mlx5_esw_reps_block(esw); + ret = mlx5_eswitch_reload_ib_reps(esw); + mlx5_esw_reps_unblock(esw); + if (ret && !cont_on_fail) + return ret; + } + } + + return 0; +} + +static int mlx5_lag_reload_ib_reps(struct mlx5_lag *ldev, u32 flags, + u32 filter, bool cont_on_fail) +{ + int ret; + + /* The HCA devcom component lock serializes LAG mode transitions while + * ldev->lock is dropped here. Dropping ldev->lock is required because + * the reload takes the per-E-Switch reps_lock, and representor + * load/unload callbacks can re-enter LAG netdev add/remove and take + * ldev->lock. Keep the ordering reps_lock -> ldev->lock. + */ + mlx5_lag_drop_lock_for_reps(ldev, filter); + ret = mlx5_lag_reload_ib_reps_unlocked(ldev, flags, filter, + cont_on_fail); + mlx5_lag_retake_lock_after_reps(ldev); + + return ret; +} + +int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, + u32 filter, bool cont_on_fail) +{ + return mlx5_lag_reload_ib_reps(ldev, flags, filter, cont_on_fail); +} + +static void mlx5_lag_unload_reps_unlocked(struct mlx5_lag *ldev, u32 filter) { struct lag_func *pf; int i; - mlx5_ldev_for_each(i, 0, ldev) { - pf = mlx5_lag_pf(ldev, i); - if (pf->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV) - continue; + mlx5_lag_for_each(i, 0, ldev, filter) { + struct mlx5_eswitch *esw; - pf->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(pf->dev); + pf = mlx5_lag_pf(ldev, i); + esw = pf->dev->priv.eswitch; + mlx5_esw_reps_block(esw); + mlx5_eswitch_unload_reps(esw); + mlx5_esw_reps_unblock(esw); } } +void mlx5_lag_unload_reps_from_locked(struct mlx5_lag *ldev, u32 filter) +{ + /* Same lock dance as mlx5_lag_reload_ib_reps: drop ldev->lock around + * the per-eswitch reps_lock to keep the reps_lock -> ldev->lock order. + */ + mlx5_lag_drop_lock_for_reps(ldev, filter); + mlx5_lag_unload_reps_unlocked(ldev, filter); + mlx5_lag_retake_lock_after_reps(ldev); +} + void mlx5_disable_lag(struct mlx5_lag *ldev) { bool shared_fdb = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); @@ -1105,16 +1350,16 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) if (idx < 0) return; + if (shared_fdb) { + mlx5_lag_shared_fdb_destroy(ldev, 0); + return; + } + dev0 = mlx5_lag_pf(ldev, idx)->dev; roce_lag = __mlx5_lag_is_roce(ldev); - if (shared_fdb) { - mlx5_lag_remove_devices(ldev); - } else if (roce_lag) { - if (!(dev0->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)) { - dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - } + if (roce_lag) { + mlx5_lag_rescan_dev_locked(ldev, dev0, false); mlx5_ldev_for_each(i, 0, ldev) { if (i == idx) continue; @@ -1126,49 +1371,8 @@ void mlx5_disable_lag(struct mlx5_lag *ldev) if (err) return; - if (shared_fdb || roce_lag) + if (roce_lag) mlx5_lag_add_devices(ldev); - - if (shared_fdb) - mlx5_ldev_for_each(i, 0, ldev) - if (!(mlx5_lag_pf(ldev, i)->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); -} - -bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) -{ - struct mlx5_core_dev *dev; - bool ret = false; - int idx; - int i; - - idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); - if (idx < 0) - return false; - - mlx5_ldev_for_each(i, 0, ldev) { - if (i == idx) - continue; - dev = mlx5_lag_pf(ldev, i)->dev; - if (is_mdev_switchdev_mode(dev) && - mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && - MLX5_CAP_GEN(dev, lag_native_fdb_selection) && - MLX5_CAP_ESW(dev, root_ft_on_other_esw) && - mlx5_eswitch_get_npeers(dev->priv.eswitch) == - MLX5_CAP_GEN(dev, num_lag_ports) - 1) - continue; - return false; - } - - dev = mlx5_lag_pf(ldev, idx)->dev; - if (is_mdev_switchdev_mode(dev) && - mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && - mlx5_esw_offloads_devcom_is_ready(dev->priv.eswitch) && - MLX5_CAP_ESW(dev, esw_shared_ingress_acl) && - mlx5_eswitch_get_npeers(dev->priv.eswitch) == MLX5_CAP_GEN(dev, num_lag_ports) - 1) - ret = true; - - return ret; } static bool mlx5_lag_is_roce_lag(struct mlx5_lag *ldev) @@ -1383,57 +1587,37 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) roce_lag = mlx5_lag_is_roce_lag(ldev); - if (shared_fdb || roce_lag) - mlx5_lag_remove_devices(ldev); - - err = mlx5_activate_lag(ldev, &tracker, - roce_lag ? MLX5_LAG_MODE_ROCE : - MLX5_LAG_MODE_SRIOV, - shared_fdb); - if (err) { - if (shared_fdb || roce_lag) - mlx5_lag_add_devices(ldev); - if (shared_fdb) { - mlx5_ldev_for_each(i, 0, ldev) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - } - - return; - } - - if (roce_lag) { - struct mlx5_core_dev *dev; - - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - mlx5_ldev_for_each(i, 0, ldev) { - if (i == idx) - continue; - dev = mlx5_lag_pf(ldev, i)->dev; - if (mlx5_get_roce_state(dev)) - mlx5_nic_vport_enable_roce(dev); - } - } else if (shared_fdb) { - int i; - - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - - mlx5_ldev_for_each(i, 0, ldev) { - err = mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - if (err) - break; - } - - if (err) { - dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - mlx5_deactivate_lag(ldev); - mlx5_lag_add_devices(ldev); - mlx5_ldev_for_each(i, 0, ldev) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - mlx5_core_err(dev0, "Failed to enable lag\n"); + if (shared_fdb) { + err = mlx5_lag_shared_fdb_create(ldev, &tracker, + MLX5_LAG_MODE_SRIOV, + 0); + if (err) return; + } else { + if (roce_lag) + mlx5_lag_remove_devices(ldev); + + err = mlx5_activate_lag(ldev, &tracker, + roce_lag ? MLX5_LAG_MODE_ROCE : + MLX5_LAG_MODE_SRIOV, + false); + if (err) { + if (roce_lag) + mlx5_lag_add_devices(ldev); + return; + } + + if (roce_lag) { + struct mlx5_core_dev *dev; + + mlx5_lag_rescan_dev_locked(ldev, dev0, true); + mlx5_ldev_for_each(i, 0, ldev) { + if (i == idx) + continue; + dev = mlx5_lag_pf(ldev, i)->dev; + if (mlx5_get_roce_state(dev)) + mlx5_nic_vport_enable_roce(dev); + } } } if (tracker.tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) { @@ -1446,7 +1630,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) ndev); dev_put(ndev); } - mlx5_lag_set_vports_agg_speed(ldev); + if (!shared_fdb) + mlx5_lag_set_vports_agg_speed(ldev); } else if (mlx5_lag_should_modify_lag(ldev, do_bond)) { mlx5_modify_lag(ldev, &tracker); mlx5_lag_set_vports_agg_speed(ldev); @@ -1467,7 +1652,7 @@ struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev) int i; mutex_lock(&ldev->lock); - i = mlx5_get_next_ldev_func(ldev, 0); + i = mlx5_get_next_lag_func(ldev, 0, MLX5_LAG_FILTER_PORTS); if (i < MLX5_MAX_PORTS) { pf = mlx5_lag_pf(ldev, i); devcom = pf->dev->priv.hca_devcom_comp; @@ -1478,7 +1663,7 @@ struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev) static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev, struct mlx5_flow_table_attr *ft_attr, - struct mlx5_lag *ldev) + struct lag_func *pf) { #ifdef CONFIG_MLX5_ESWITCH struct mlx5_flow_namespace *ns; @@ -1489,20 +1674,20 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev, if (!ns) return 0; - ldev->lag_demux_ft = mlx5_create_flow_table(ns, ft_attr); - if (IS_ERR(ldev->lag_demux_ft)) - return PTR_ERR(ldev->lag_demux_ft); + pf->lag_demux_ft = mlx5_create_flow_table(ns, ft_attr); + if (IS_ERR(pf->lag_demux_ft)) + return PTR_ERR(pf->lag_demux_ft); fg = mlx5_esw_lag_demux_fg_create(dev->priv.eswitch, - ldev->lag_demux_ft); + pf->lag_demux_ft); if (IS_ERR(fg)) { err = PTR_ERR(fg); - mlx5_destroy_flow_table(ldev->lag_demux_ft); - ldev->lag_demux_ft = NULL; + mlx5_destroy_flow_table(pf->lag_demux_ft); + pf->lag_demux_ft = NULL; return err; } - ldev->lag_demux_fg = fg; + pf->lag_demux_fg = fg; return 0; #else return -EOPNOTSUPP; @@ -1511,7 +1696,7 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev, static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev, struct mlx5_flow_table_attr *ft_attr, - struct mlx5_lag *ldev) + struct lag_func *pf) { struct mlx5_flow_namespace *ns; int err; @@ -1520,12 +1705,12 @@ static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev, if (!ns) return 0; - ldev->lag_demux_fg = NULL; + pf->lag_demux_fg = NULL; ft_attr->max_fte = 1; - ldev->lag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr); - if (IS_ERR(ldev->lag_demux_ft)) { - err = PTR_ERR(ldev->lag_demux_ft); - ldev->lag_demux_ft = NULL; + pf->lag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr); + if (IS_ERR(pf->lag_demux_ft)) { + err = PTR_ERR(pf->lag_demux_ft); + pf->lag_demux_ft = NULL; return err; } @@ -1536,6 +1721,7 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev, struct mlx5_flow_table_attr *ft_attr) { struct mlx5_lag *ldev; + struct lag_func *pf; if (!ft_attr) return -EINVAL; @@ -1544,12 +1730,16 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev, if (!ldev) return -ENODEV; - xa_init(&ldev->lag_demux_rules); + pf = mlx5_lag_pf_by_dev(ldev, dev); + if (!pf) + return -ENODEV; - if (mlx5_get_sd(dev)) - return mlx5_lag_demux_ft_fg_init(dev, ft_attr, ldev); + xa_init(&pf->lag_demux_rules); - return mlx5_lag_demux_fw_init(dev, ft_attr, ldev); + if (mlx5_lag_is_sw_lag(dev)) + return mlx5_lag_demux_ft_fg_init(dev, ft_attr, pf); + + return mlx5_lag_demux_fw_init(dev, ft_attr, pf); } EXPORT_SYMBOL(mlx5_lag_demux_init); @@ -1558,40 +1748,63 @@ void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev) struct mlx5_flow_handle *rule; struct mlx5_lag *ldev; unsigned long vport_num; + struct lag_func *pf; ldev = mlx5_lag_dev(dev); if (!ldev) return; - xa_for_each(&ldev->lag_demux_rules, vport_num, rule) - mlx5_del_flow_rules(rule); - xa_destroy(&ldev->lag_demux_rules); + pf = mlx5_lag_pf_by_dev(ldev, dev); + if (!pf) + return; - if (ldev->lag_demux_fg) - mlx5_destroy_flow_group(ldev->lag_demux_fg); - if (ldev->lag_demux_ft) - mlx5_destroy_flow_table(ldev->lag_demux_ft); - ldev->lag_demux_fg = NULL; - ldev->lag_demux_ft = NULL; + xa_for_each(&pf->lag_demux_rules, vport_num, rule) + mlx5_del_flow_rules(rule); + xa_destroy(&pf->lag_demux_rules); + + if (pf->lag_demux_fg) + mlx5_destroy_flow_group(pf->lag_demux_fg); + if (pf->lag_demux_ft) + mlx5_destroy_flow_table(pf->lag_demux_ft); + pf->lag_demux_fg = NULL; + pf->lag_demux_ft = NULL; } EXPORT_SYMBOL(mlx5_lag_demux_cleanup); +static struct lag_func *mlx5_lag_dev_get_master_pf(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev) +{ + u32 filter = mlx5_lag_get_filter(ldev, dev); + int idx; + + idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, filter); + if (idx < 0) + return NULL; + + return mlx5_lag_pf(ldev, idx); +} + int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num, int index) { struct mlx5_flow_handle *rule; + struct lag_func *master; struct mlx5_lag *ldev; int err; ldev = mlx5_lag_dev(vport_dev); - if (!ldev || !ldev->lag_demux_fg) + if (!ldev) return 0; - if (xa_load(&ldev->lag_demux_rules, index)) + master = mlx5_lag_dev_get_master_pf(ldev, vport_dev); + if (!master || !master->lag_demux_fg) + return 0; + + if (xa_load(&master->lag_demux_rules, index)) return 0; rule = mlx5_esw_lag_demux_rule_create(vport_dev->priv.eswitch, - vport_num, ldev->lag_demux_ft); + vport_num, master->lag_demux_ft); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_warn(vport_dev, @@ -1600,7 +1813,7 @@ int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num, return err; } - err = xa_err(xa_store(&ldev->lag_demux_rules, index, rule, + err = xa_err(xa_store(&master->lag_demux_rules, index, rule, GFP_KERNEL)); if (err) { mlx5_del_flow_rules(rule); @@ -1616,13 +1829,18 @@ EXPORT_SYMBOL(mlx5_lag_demux_rule_add); void mlx5_lag_demux_rule_del(struct mlx5_core_dev *dev, int index) { struct mlx5_flow_handle *rule; + struct lag_func *master_pf; struct mlx5_lag *ldev; ldev = mlx5_lag_dev(dev); - if (!ldev || !ldev->lag_demux_fg) + if (!ldev) return; - rule = xa_erase(&ldev->lag_demux_rules, index); + master_pf = mlx5_lag_dev_get_master_pf(ldev, dev); + if (!master_pf || !master_pf->lag_demux_fg) + return; + + rule = xa_erase(&master_pf->lag_demux_rules, index); if (rule) mlx5_del_flow_rules(rule); } @@ -1950,8 +2168,9 @@ static void mlx5_ldev_remove_netdev(struct mlx5_lag *ldev, spin_unlock_irqrestore(&lag_lock, flags); } -static int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, - struct mlx5_core_dev *dev) +int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev, + u32 group_id) { struct lag_func *pf; u32 idx; @@ -1970,8 +2189,14 @@ static int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, pf->idx = idx; pf->dev = dev; + pf->group_id = group_id; dev->priv.lag = ldev; + if (group_id) + return 0; + + xa_set_mark(&ldev->pfs, idx, MLX5_LAG_XA_MARK_PORT); + MLX5_NB_INIT(&pf->port_change_nb, mlx5_lag_mpesw_port_change_event, PORT_CHANGE); mlx5_eq_notifier_register(dev, &pf->port_change_nb); @@ -1979,13 +2204,13 @@ static int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, return 0; } -static void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, - struct mlx5_core_dev *dev) +void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev) { struct lag_func *pf; int i; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { pf = mlx5_lag_pf(ldev, i); if (pf->dev == dev) break; @@ -2020,7 +2245,7 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev) mlx5_core_err(dev, "Failed to alloc lag dev\n"); return 0; } - err = mlx5_ldev_add_mdev(ldev, dev); + err = mlx5_ldev_add_mdev(ldev, dev, 0); if (err) { mlx5_core_err(dev, "Failed to add mdev to lag dev\n"); mlx5_ldev_put(ldev); @@ -2035,7 +2260,7 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev) return -EAGAIN; } mlx5_ldev_get(ldev); - err = mlx5_ldev_add_mdev(ldev, dev); + err = mlx5_ldev_add_mdev(ldev, dev, 0); if (err) { mlx5_ldev_put(ldev); mutex_unlock(&ldev->lock); @@ -2172,27 +2397,47 @@ void mlx5_lag_add_netdev(struct mlx5_core_dev *dev, mlx5_queue_bond_work(ldev, 0); } -int mlx5_get_pre_ldev_func(struct mlx5_lag *ldev, int start_idx, int end_idx) +int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx, + u32 filter) { struct lag_func *pf; int i; for (i = start_idx; i >= end_idx; i--) { pf = xa_load(&ldev->pfs, i); - if (pf && pf->dev) + if (!pf || !pf->dev) + continue; + if (filter == MLX5_LAG_FILTER_PORTS) { + if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) + return i; + } else if (filter == MLX5_LAG_FILTER_ALL || + filter == pf->group_id) { return i; + } } return -1; } -int mlx5_get_next_ldev_func(struct mlx5_lag *ldev, int start_idx) +int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter) { struct lag_func *pf; unsigned long idx; - xa_for_each_start(&ldev->pfs, idx, pf, start_idx) - if (pf->dev) + if (filter == MLX5_LAG_FILTER_PORTS) { + xa_for_each_marked_start(&ldev->pfs, idx, pf, + MLX5_LAG_XA_MARK_PORT, start_idx) + if (pf->dev) + return idx; + return MLX5_MAX_PORTS; + } + + xa_for_each_start(&ldev->pfs, idx, pf, start_idx) { + if (!pf->dev) + continue; + if (filter == MLX5_LAG_FILTER_ALL || + filter == pf->group_id) return idx; + } return MLX5_MAX_PORTS; } @@ -2219,7 +2464,8 @@ bool mlx5_lag_is_active(struct mlx5_core_dev *dev) spin_lock_irqsave(&lag_lock, flags); ldev = mlx5_lag_dev(dev); - res = ldev && __mlx5_lag_is_active(ldev); + res = ldev && (__mlx5_lag_is_active(ldev) || + __mlx5_lag_is_sd_active(ldev, dev)); spin_unlock_irqrestore(&lag_lock, flags); return res; @@ -2252,10 +2498,17 @@ bool mlx5_lag_is_master(struct mlx5_core_dev *dev) spin_lock_irqsave(&lag_lock, flags); ldev = mlx5_lag_dev(dev); - idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); - if (ldev && __mlx5_lag_is_active(ldev) && idx >= 0) { - pf = mlx5_lag_pf(ldev, idx); - res = pf && dev == pf->dev; + if (ldev) { + u32 filter; + + filter = mlx5_lag_get_filter(ldev, dev); + idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + if ((__mlx5_lag_is_active(ldev) || + __mlx5_lag_is_sd_active(ldev, dev)) && idx >= 0) { + pf = mlx5_lag_pf(ldev, idx); + res = pf && dev == pf->dev; + } } spin_unlock_irqrestore(&lag_lock, flags); @@ -2278,7 +2531,7 @@ bool mlx5_lag_is_sriov(struct mlx5_core_dev *dev) } EXPORT_SYMBOL(mlx5_lag_is_sriov); -bool mlx5_lag_is_shared_fdb(struct mlx5_core_dev *dev) +bool mlx5_lag_is_sd(struct mlx5_core_dev *dev) { struct mlx5_lag *ldev; unsigned long flags; @@ -2286,7 +2539,26 @@ bool mlx5_lag_is_shared_fdb(struct mlx5_core_dev *dev) spin_lock_irqsave(&lag_lock, flags); ldev = mlx5_lag_dev(dev); - res = ldev && test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); + res = ldev && __mlx5_lag_is_sd(ldev, dev); + spin_unlock_irqrestore(&lag_lock, flags); + + return res; +} + +bool mlx5_lag_is_shared_fdb(struct mlx5_core_dev *dev) +{ + struct mlx5_lag *ldev; + unsigned long flags; + bool res = false; + + spin_lock_irqsave(&lag_lock, flags); + ldev = mlx5_lag_dev(dev); + if (ldev) { + res = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, + &ldev->mode_flags); + if (__mlx5_lag_is_sd(ldev, dev) && !__mlx5_lag_is_active(ldev)) + res = __mlx5_lag_is_sd_active(ldev, dev); + } spin_unlock_irqrestore(&lag_lock, flags); return res; @@ -2295,13 +2567,26 @@ EXPORT_SYMBOL(mlx5_lag_is_shared_fdb); void mlx5_lag_disable_change(struct mlx5_core_dev *dev) { + struct mlx5_devcom_comp_dev *sd_devcom = mlx5_sd_get_devcom(dev); + struct mlx5_core_dev *primary = dev; struct mlx5_lag *ldev; + struct lag_func *pf; + bool mpesw; + int i; ldev = mlx5_lag_dev(dev); if (!ldev) return; - mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp); + if (sd_devcom) { + mlx5_devcom_comp_lock(sd_devcom); + primary = mlx5_sd_get_primary(dev) ?: dev; + mlx5_devcom_comp_unlock(sd_devcom); + } + mlx5_devcom_comp_lock(primary->priv.hca_devcom_comp); + mpesw = ldev->mode == MLX5_LAG_MODE_MPESW; + if (mpesw) + mlx5_mpesw_sd_devcoms_lock(ldev); mutex_lock(&ldev->lock); ldev->mode_changes_in_progress++; @@ -2313,7 +2598,25 @@ void mlx5_lag_disable_change(struct mlx5_core_dev *dev) } mutex_unlock(&ldev->lock); - mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp); + if (mpesw) + mlx5_mpesw_sd_devcoms_unlock(ldev); + mlx5_devcom_comp_unlock(primary->priv.hca_devcom_comp); + + if (!sd_devcom) + return; + + /* Teardown SD shared FDB for this device's group if active */ + mlx5_devcom_comp_lock(sd_devcom); + mutex_lock(&ldev->lock); + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { + pf = mlx5_lag_pf(ldev, i); + if (pf->dev == dev && pf->sd_fdb_active) { + mlx5_lag_shared_fdb_destroy(ldev, pf->group_id); + break; + } + } + mutex_unlock(&ldev->lock); + mlx5_devcom_comp_unlock(sd_devcom); } void mlx5_lag_enable_change(struct mlx5_core_dev *dev) @@ -2387,7 +2690,7 @@ struct mlx5_core_dev *mlx5_lag_get_next_peer_mdev(struct mlx5_core_dev *dev, int if (*i == MLX5_MAX_PORTS) goto unlock; - mlx5_ldev_for_each(idx, *i, ldev) { + mlx5_lag_for_each(idx, *i, ldev, mlx5_lag_get_filter(ldev, dev)) { pf = mlx5_lag_pf(ldev, idx); if (pf->dev != dev) break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h index 6c911374f409..e9f0ef83ce1d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h @@ -15,6 +15,13 @@ * Note: XA_MARK_0 is reserved by XA_FLAGS_ALLOC for free-slot tracking. */ #define MLX5_LAG_XA_MARK_MASTER XA_MARK_1 +/* XArray mark for port-level entries (excludes SD secondaries) */ +#define MLX5_LAG_XA_MARK_PORT XA_MARK_2 + +/* Like xa_for_each_marked but starting from a given index */ +#define xa_for_each_marked_start(xa, index, entry, filter, start) \ + for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \ + entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter)) #include "mlx5_core.h" #include "mp.h" @@ -50,6 +57,12 @@ struct lag_func { bool has_drop; unsigned int idx; /* xarray index assigned by LAG */ struct mlx5_nb port_change_nb; + u32 group_id; /* SD group ID, 0 = not SD */ + bool sd_fdb_active; /* set on all SD group members */ + /* Lag demux resources - only populated on master devices */ + struct mlx5_flow_table *lag_demux_ft; + struct mlx5_flow_group *lag_demux_fg; + struct xarray lag_demux_rules; }; /* Used for collection of netdev event info. */ @@ -86,9 +99,6 @@ struct mlx5_lag { /* Protect lag fields/state changes */ struct mutex lock; struct lag_mpesw lag_mpesw; - struct mlx5_flow_table *lag_demux_ft; - struct mlx5_flow_group *lag_demux_fg; - struct xarray lag_demux_rules; }; static inline struct mlx5_lag * @@ -125,6 +135,36 @@ mlx5_lag_pf_by_dev_idx(struct mlx5_lag *ldev, int dev_idx) return NULL; } +/* Find lag_func by mlx5_core_dev pointer */ +static inline struct lag_func * +mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf; + unsigned long idx; + + xa_for_each(&ldev->pfs, idx, pf) { + if (pf->dev == dev) + return pf; + } + return NULL; +} + +static inline bool +__mlx5_lag_is_sd(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + return pf && pf->group_id != 0; +} + +static inline bool +__mlx5_lag_dev_is_port(struct mlx5_lag *ldev, struct mlx5_core_dev *dev) +{ + struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev); + + return pf && xa_get_mark(&ldev->pfs, pf->idx, MLX5_LAG_XA_MARK_PORT); +} + static inline bool __mlx5_lag_is_active(struct mlx5_lag *ldev) { @@ -137,8 +177,55 @@ mlx5_lag_is_ready(struct mlx5_lag *ldev) return test_bit(MLX5_LAG_FLAG_NDEVS_READY, &ldev->state_flags); } +#ifdef CONFIG_MLX5_ESWITCH +int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + enum mlx5_lag_mode mode, + u32 group_id); +void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id); +int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id); +int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id); +int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev); +void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev); bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev); +bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter); +#else +static inline int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + enum mlx5_lag_mode mode, + u32 group_id) +{ + return -EOPNOTSUPP; +} + +static inline void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, + u32 group_id) {} + +static inline int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, + u32 group_id) +{ + return -EOPNOTSUPP; +} + +static inline int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, + u32 group_id) +{ + return -EOPNOTSUPP; +} + +static inline int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) +{ + return -EOPNOTSUPP; +} + +static inline void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev) {} +static inline bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) +{ + return false; +} +#endif bool mlx5_lag_check_prereq(struct mlx5_lag *ldev); +bool mlx5_lag_is_sd(struct mlx5_core_dev *dev); int mlx5_lag_demux_init(struct mlx5_core_dev *dev, struct mlx5_flow_table_attr *ft_attr); void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev); @@ -162,8 +249,13 @@ void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev); void mlx5_ldev_remove_debugfs(struct dentry *dbg); void mlx5_disable_lag(struct mlx5_lag *ldev); void mlx5_lag_remove_devices(struct mlx5_lag *ldev); +void mlx5_lag_remove_devices_filter(struct mlx5_lag *ldev, u32 filter); int mlx5_deactivate_lag(struct mlx5_lag *ldev); void mlx5_lag_add_devices(struct mlx5_lag *ldev); +void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev, + struct mlx5_core_dev *dev, + bool enable); +void mlx5_lag_add_devices_filter(struct mlx5_lag *ldev, u32 filter); struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev); #ifdef CONFIG_MLX5_ESWITCH @@ -185,18 +277,41 @@ static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev) return true; } -#define mlx5_ldev_for_each(i, start_index, ldev) \ - for (int tmp = start_index; tmp = mlx5_get_next_ldev_func(ldev, tmp), \ +/* Iterator filter constants for mlx5_lag_for_each() */ +#define MLX5_LAG_FILTER_PORTS 0 /* iterate ports only (XA_MARK_PORT) */ +#define MLX5_LAG_FILTER_ALL U32_MAX /* iterate ALL devices */ +/* any other value = iterate devices with that specific group_id */ + +#define mlx5_lag_for_each(i, start_index, ldev, filter) \ + for (int tmp = start_index; \ + tmp = mlx5_get_next_lag_func(ldev, tmp, filter), \ i = tmp, tmp < MLX5_MAX_PORTS; tmp++) -#define mlx5_ldev_for_each_reverse(i, start_index, end_index, ldev) \ +#define mlx5_lag_for_each_reverse(i, start_index, end_index, ldev, filter) \ for (int tmp = start_index, tmp1 = end_index; \ - tmp = mlx5_get_pre_ldev_func(ldev, tmp, tmp1), \ + tmp = mlx5_get_pre_lag_func(ldev, tmp, tmp1, filter), \ i = tmp, tmp >= tmp1; tmp--) -int mlx5_get_pre_ldev_func(struct mlx5_lag *ldev, int start_idx, int end_idx); -int mlx5_get_next_ldev_func(struct mlx5_lag *ldev, int start_idx); +/* Convenience wrappers - keeps existing behavior */ +#define mlx5_ldev_for_each(i, start_index, ldev) \ + mlx5_lag_for_each(i, start_index, ldev, MLX5_LAG_FILTER_PORTS) + +#define mlx5_ldev_for_each_reverse(i, start_index, end_index, ldev) \ + mlx5_lag_for_each_reverse(i, start_index, end_index, ldev, \ + MLX5_LAG_FILTER_PORTS) + +int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx, + u32 filter); +int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter); int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq); +int mlx5_lag_get_dev_index_by_seq_filter(struct mlx5_lag *ldev, int seq, + u32 filter); int mlx5_lag_num_devs(struct mlx5_lag *ldev); int mlx5_lag_num_netdevs(struct mlx5_lag *ldev); +int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, + u32 filter, bool cont_on_fail); +void mlx5_lag_unload_reps_from_locked(struct mlx5_lag *ldev, u32 filter); +int mlx5_ldev_add_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev, + u32 group_id); +void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev); #endif /* __MLX5_LAG_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c index f42e051fa7e7..65c76bd748c6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c @@ -26,6 +26,10 @@ static bool mlx5_lag_multipath_check_prereq(struct mlx5_lag *ldev) if (__mlx5_lag_is_active(ldev) && !__mlx5_lag_is_multipath(ldev)) return false; + if (__mlx5_lag_is_sd(ldev, mlx5_lag_pf(ldev, idx0)->dev) || + __mlx5_lag_is_sd(ldev, mlx5_lag_pf(ldev, idx1)->dev)) + return false; + if (ldev->ports > MLX5_LAG_MULTIPATH_OFFLOADS_SUPPORTED_PORTS) return false; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 5eea12a6887a..50bfb450c71e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -15,7 +15,7 @@ static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev) u32 pf_metadata; int i; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { dev = mlx5_lag_pf(ldev, i)->dev; esw = dev->priv.eswitch; pf_metadata = ldev->lag_mpesw.pf_metadata[i]; @@ -36,7 +36,7 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev) u32 pf_metadata; int i, err; - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { dev = mlx5_lag_pf(ldev, i)->dev; esw = dev->priv.eswitch; pf_metadata = mlx5_esw_match_metadata_alloc(esw); @@ -52,7 +52,7 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev) goto err_metadata; } - mlx5_ldev_for_each(i, 0, ldev) { + mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) { dev = mlx5_lag_pf(ldev, i)->dev; mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW, (void *)0); @@ -65,12 +65,53 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev) return err; } +static void mlx5_mpesw_restore_sd_fdb(struct mlx5_lag *ldev) +{ + struct lag_func *pf; + int err, i; + + mlx5_ldev_for_each(i, 0, ldev) { + pf = mlx5_lag_pf(ldev, i); + err = mlx5_lag_shared_fdb_create(ldev, NULL, 0, pf->group_id); + if (err) + mlx5_core_warn(pf->dev, + "Failed to restore SD shared FDB (%d)\n", + err); + } +} + +static int mlx5_mpesw_teardown_sd_fdb(struct mlx5_lag *ldev) +{ + struct lag_func *pf; + int i; + + mlx5_ldev_for_each(i, 0, ldev) { + pf = mlx5_lag_pf(ldev, i); + if (!pf->sd_fdb_active) + continue; + mlx5_lag_shared_fdb_destroy(ldev, pf->group_id); + } + return 0; +} + +static bool mlx5_lag_has_sd_group(struct mlx5_lag *ldev) +{ + struct lag_func *pf; + int i; + + mlx5_ldev_for_each(i, 0, ldev) { + pf = mlx5_lag_pf(ldev, i); + if (pf->group_id) + return true; + } + return false; +} + static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) { int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); struct mlx5_core_dev *dev0; int err; - int i; if (ldev->mode == MLX5_LAG_MODE_MPESW) return 0; @@ -86,50 +127,63 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) || !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) || !mlx5_lag_check_prereq(ldev) || - !mlx5_lag_shared_fdb_supported(ldev)) + !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL)) return -EOPNOTSUPP; err = mlx5_mpesw_metadata_set(ldev); if (err) return err; - mlx5_lag_remove_devices(ldev); + if (mlx5_lag_has_sd_group(ldev)) + mlx5_mpesw_teardown_sd_fdb(ldev); - err = mlx5_activate_lag(ldev, NULL, MLX5_LAG_MODE_MPESW, true); + err = mlx5_lag_shared_fdb_create(ldev, NULL, MLX5_LAG_MODE_MPESW, + MLX5_LAG_FILTER_ALL); if (err) { - mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err); - goto err_add_devices; + mlx5_core_warn(dev0, + "Failed to create LAG in MPESW mode (%d)\n", + err); + if (mlx5_lag_has_sd_group(ldev)) + mlx5_mpesw_restore_sd_fdb(ldev); + mlx5_mpesw_metadata_cleanup(ldev); + return err; } - dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - mlx5_ldev_for_each(i, 0, ldev) { - err = mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - if (err) - goto err_rescan_drivers; - } - - mlx5_lag_set_vports_agg_speed(ldev); - return 0; - -err_rescan_drivers: - dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; - mlx5_rescan_drivers_locked(dev0); - mlx5_deactivate_lag(ldev); -err_add_devices: - mlx5_lag_add_devices(ldev); - mlx5_ldev_for_each(i, 0, ldev) - mlx5_eswitch_reload_ib_reps(mlx5_lag_pf(ldev, i)->dev->priv.eswitch); - mlx5_mpesw_metadata_cleanup(ldev); - return err; } void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev) { - if (ldev->mode == MLX5_LAG_MODE_MPESW) { - mlx5_mpesw_metadata_cleanup(ldev); - mlx5_disable_lag(ldev); + if (ldev->mode != MLX5_LAG_MODE_MPESW) + return; + + mlx5_mpesw_metadata_cleanup(ldev); + mlx5_lag_shared_fdb_destroy(ldev, MLX5_LAG_FILTER_ALL); + if (mlx5_lag_has_sd_group(ldev)) + mlx5_mpesw_restore_sd_fdb(ldev); +} + +void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev) +{ + struct mlx5_devcom_comp_dev *sd_devcom; + int i; + + mlx5_ldev_for_each(i, 0, ldev) { + sd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)->dev); + if (sd_devcom) + mlx5_devcom_comp_lock(sd_devcom); + } +} + +void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev) +{ + struct mlx5_devcom_comp_dev *sd_devcom; + int i; + + mlx5_ldev_for_each_reverse(i, MLX5_MAX_PORTS, 0, ldev) { + sd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)->dev); + if (sd_devcom) + mlx5_devcom_comp_unlock(sd_devcom); } } @@ -144,6 +198,7 @@ static void mlx5_mpesw_work(struct work_struct *work) return; mlx5_devcom_comp_lock(devcom); + mlx5_mpesw_sd_devcoms_lock(ldev); mutex_lock(&ldev->lock); if (ldev->mode_changes_in_progress) { mpesww->result = -EAGAIN; @@ -156,6 +211,7 @@ static void mlx5_mpesw_work(struct work_struct *work) mlx5_lag_disable_mpesw(ldev); unlock: mutex_unlock(&ldev->lock); + mlx5_mpesw_sd_devcoms_unlock(ldev); mlx5_devcom_comp_unlock(devcom); complete(&mpesww->comp); } @@ -221,7 +277,8 @@ bool mlx5_lag_is_mpesw(struct mlx5_core_dev *dev) { struct mlx5_lag *ldev = mlx5_lag_dev(dev); - return ldev && ldev->mode == MLX5_LAG_MODE_MPESW; + return ldev && ldev->mode == MLX5_LAG_MODE_MPESW && + __mlx5_lag_dev_is_port(ldev, dev); } EXPORT_SYMBOL(mlx5_lag_is_mpesw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h index b767dbb4f457..5099723ba0f7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h @@ -33,8 +33,12 @@ void mlx5_lag_mpesw_disable(struct mlx5_core_dev *dev); int mlx5_lag_mpesw_enable(struct mlx5_core_dev *dev); #ifdef CONFIG_MLX5_ESWITCH void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev); +void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev); +void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev); #else static inline void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev) {} +static inline void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev) {} +static inline void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev) {} #endif /* CONFIG_MLX5_ESWITCH */ #ifdef CONFIG_MLX5_ESWITCH diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c new file mode 100644 index 000000000000..113866494d16 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include +#include +#include +#include "mlx5_core.h" +#include "lag.h" +#include "eswitch.h" + +bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter) +{ + int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_core_dev *dev0, *dev; + bool ret = false; + int i; + + if (idx < 0) + return false; + + dev0 = mlx5_lag_pf(ldev, idx)->dev; + mlx5_lag_for_each(i, 0, ldev, filter) { + if (i == idx) + continue; + dev = mlx5_lag_pf(ldev, i)->dev; + if (is_mdev_switchdev_mode(dev) && + mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) && + MLX5_CAP_GEN(dev, lag_native_fdb_selection) && + MLX5_CAP_ESW(dev, root_ft_on_other_esw) && + mlx5_eswitch_is_peer(dev0->priv.eswitch, dev->priv.eswitch)) + continue; + return false; + } + + if (is_mdev_switchdev_mode(dev0) && + mlx5_eswitch_vport_match_metadata_enabled(dev0->priv.eswitch) && + mlx5_esw_offloads_devcom_is_ready(dev0->priv.eswitch) && + MLX5_CAP_ESW(dev0, esw_shared_ingress_acl)) + ret = true; + + return ret; +} + +bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev) +{ + return mlx5_lag_shared_fdb_supported_filter(ldev, + MLX5_LAG_FILTER_PORTS); +} + +static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter) +{ + int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_eswitch *master_esw; + struct mlx5_core_dev *dev0; + int i, j; + int err; + + if (master_idx < 0) + return -EINVAL; + + dev0 = mlx5_lag_pf(ldev, master_idx)->dev; + master_esw = dev0->priv.eswitch; + mlx5_lag_for_each(i, 0, ldev, filter) { + struct mlx5_eswitch *slave_esw; + + if (i == master_idx) + continue; + + slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; + + err = mlx5_eswitch_offloads_single_fdb_add_one(master_esw, + slave_esw, + ldev->ports); + if (err) + goto err; + } + return 0; +err: + mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) { + struct mlx5_eswitch *slave_esw; + + if (j == master_idx) + continue; + slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch; + mlx5_eswitch_offloads_single_fdb_del_one(master_esw, slave_esw); + } + return err; +} + +int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id) +{ + u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; + int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_eswitch *master_esw; + struct mlx5_core_dev *dev0; + int i, j; + int err; + + if (master_idx < 0) + return -EINVAL; + + dev0 = mlx5_lag_pf(ldev, master_idx)->dev; + master_esw = dev0->priv.eswitch; + + mlx5_lag_for_each(i, 0, ldev, filter) { + struct mlx5_eswitch *slave_esw; + + if (i == master_idx) + continue; + + slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; + err = mlx5_eswitch_offloads_vport_lag_add_one(master_esw, + slave_esw); + if (err) + goto err; + } + + return 0; + +err: + mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) { + struct mlx5_eswitch *slave_esw; + + if (j == master_idx) + continue; + slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch; + mlx5_eswitch_offloads_vport_lag_del_one(master_esw, slave_esw); + } + return err; +} + +int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id) +{ + u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; + int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_eswitch *master_esw; + struct mlx5_core_dev *dev0; + int i; + + if (master_idx < 0) + return 0; + + dev0 = mlx5_lag_pf(ldev, master_idx)->dev; + master_esw = dev0->priv.eswitch; + + mlx5_lag_for_each(i, 0, ldev, filter) { + struct mlx5_core_dev *dev; + + if (i == master_idx) + continue; + dev = mlx5_lag_pf(ldev, i)->dev; + mlx5_eswitch_offloads_vport_lag_del_one(master_esw, + dev->priv.eswitch); + } + return 0; +} + +static void mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag *ldev, + u32 filter) +{ + int master_idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_eswitch *master_esw; + struct mlx5_eswitch *peer_esw; + int i; + + if (master_idx < 0) + return; + + master_esw = mlx5_lag_pf(ldev, master_idx)->dev->priv.eswitch; + mlx5_lag_for_each(i, 0, ldev, filter) { + if (i == master_idx) + continue; + + peer_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch; + mlx5_eswitch_offloads_single_fdb_del_one(master_esw, peer_esw); + } +} + +int mlx5_lag_create_single_fdb(struct mlx5_lag *ldev) +{ + return mlx5_lag_create_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL); +} + +void mlx5_lag_destroy_single_fdb(struct mlx5_lag *ldev) +{ + mlx5_lag_destroy_single_fdb_filter(ldev, MLX5_LAG_FILTER_ALL); +} + +/** + * mlx5_lag_shared_fdb_create - Create shared FDB LAG + * @ldev: LAG device + * @tracker: LAG tracker (NULL for SD) + * @mode: LAG mode (unused for SD) + * @group_id: SD group ID; 0 (MLX5_LAG_FILTER_PORTS) for ports LAG; + * MLX5_LAG_FILTER_ALL for all-device (mpesw) LAG + * + * When group_id is 0 (MLX5_LAG_FILTER_PORTS) or MLX5_LAG_FILTER_ALL, + * activates a FW LAG with shared FDB. + * When group_id is a specific SD group ID, creates a software-only shared + * FDB scoped to that group (no FW LAG commands). + * + * Return: 0 on success, negative error code on failure. + */ +int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev, + struct lag_tracker *tracker, + enum mlx5_lag_mode mode, + u32 group_id) +{ + u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; + int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, + filter); + struct mlx5_core_dev *dev0; + struct lag_func *pf; + int err; + int i; + + if (idx < 0) + return -EINVAL; + + dev0 = mlx5_lag_pf(ldev, idx)->dev; + + mlx5_lag_remove_devices_filter(ldev, filter); + + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { + err = mlx5_activate_lag(ldev, tracker, mode, true); + if (err) { + mlx5_core_warn(dev0, + "Failed to create LAG in shared FDB mode (%d)\n", + err); + goto err_add_devices; + } + } else { + err = mlx5_lag_create_single_fdb_filter(ldev, group_id); + if (err) { + mlx5_core_warn(dev0, + "Failed to create SD shared FDB (%d)\n", + err); + goto err_add_devices; + } + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + pf->sd_fdb_active = true; + } + BLOCKING_INIT_NOTIFIER_HEAD(&dev0->priv.lag_nh); + } + + mlx5_lag_rescan_dev_locked(ldev, dev0, true); + err = mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, false); + if (err) { + mlx5_core_err(dev0, "Failed to enable lag\n"); + goto err_rescan_drivers; + } + + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) + mlx5_lag_set_vports_agg_speed(ldev); + return 0; + +err_rescan_drivers: + mlx5_lag_rescan_dev_locked(ldev, dev0, false); + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { + mlx5_deactivate_lag(ldev); + } else { + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + pf->sd_fdb_active = false; + } + mlx5_lag_destroy_single_fdb_filter(ldev, group_id); + } +err_add_devices: + mlx5_lag_add_devices_filter(ldev, filter); + mlx5_lag_reload_ib_reps_from_locked(ldev, 0, filter, true); + return err; +} + +void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id) +{ + u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL; + struct lag_func *pf; + int err; + int i; + + mlx5_lag_remove_devices_filter(ldev, filter); + + if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL) { + err = mlx5_deactivate_lag(ldev); + if (err) + return; + } else { + mlx5_lag_for_each(i, 0, ldev, filter) { + pf = mlx5_lag_pf(ldev, i); + pf->sd_fdb_active = false; + } + mlx5_lag_destroy_single_fdb_filter(ldev, group_id); + mlx5_lag_unload_reps_from_locked(ldev, filter); + } + + mlx5_lag_add_devices_filter(ldev, filter); + mlx5_lag_reload_ib_reps_from_locked(ldev, + MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV, + filter, true); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c index 4b5ac2db55ce..64f92427602d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c @@ -3,6 +3,7 @@ #include #include +#include #include "lib/devcom.h" #include "lib/mlx5.h" #include "mlx5_core.h" @@ -286,9 +287,9 @@ int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom) return kref_read(&comp->ref); } -int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom, - int event, int rollback_event, - void *event_data) +int mlx5_devcom_locked_send_event(struct mlx5_devcom_comp_dev *devcom, + int event, int rollback_event, + void *event_data) { struct mlx5_devcom_comp_dev *pos; struct mlx5_devcom_comp *comp; @@ -298,24 +299,28 @@ int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom, if (!devcom) return -ENODEV; + lockdep_assert_held_write(&devcom->comp->sem); comp = devcom->comp; - down_write(&comp->sem); list_for_each_entry(pos, &comp->comp_dev_list_head, list) { data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem)); if (pos != devcom && data) { err = comp->handler(event, data, event_data); - if (err) + if (err && rollback_event != DEVCOM_CANT_FAIL) { goto rollback; + } else if (err && rollback_event == DEVCOM_CANT_FAIL) { + WARN_ONCE(1, "devcom component %d event %d failed: %d\n", + comp->id, event, err); + return err; + } } } - up_write(&comp->sem); return 0; rollback: if (list_entry_is_head(pos, &comp->comp_dev_list_head, list)) - goto out; + return err; pos = list_prev_entry(pos, list); list_for_each_entry_from_reverse(pos, &comp->comp_dev_list_head, list) { data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem)); @@ -323,7 +328,23 @@ int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom, if (pos != devcom && data) comp->handler(rollback_event, data, event_data); } -out: + return err; +} + +int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom, + int event, int rollback_event, + void *event_data) +{ + struct mlx5_devcom_comp *comp; + int err; + + if (!devcom) + return -ENODEV; + + comp = devcom->comp; + down_write(&comp->sem); + err = mlx5_devcom_locked_send_event(devcom, event, rollback_event, + event_data); up_write(&comp->sem); return err; } @@ -438,3 +459,10 @@ int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom) return 0; return down_write_trylock(&devcom->comp->sem); } + +void mlx5_devcom_comp_assert_locked(struct mlx5_devcom_comp_dev *devcom) +{ + if (!devcom) + return; + lockdep_assert_held_write(&devcom->comp->sem); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h index 91e5ae529d5c..7a704fafdbd3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h @@ -46,6 +46,11 @@ mlx5_devcom_register_component(struct mlx5_devcom_dev *devc, void *data); void mlx5_devcom_unregister_component(struct mlx5_devcom_comp_dev *devcom); +#define DEVCOM_CANT_FAIL (INT_MAX) + +int mlx5_devcom_locked_send_event(struct mlx5_devcom_comp_dev *devcom, + int event, int rollback_event, + void *event_data); int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom, int event, int rollback_event, void *event_data); @@ -75,5 +80,6 @@ void *mlx5_devcom_get_next_peer_data_rcu(struct mlx5_devcom_comp_dev *devcom, void mlx5_devcom_comp_lock(struct mlx5_devcom_comp_dev *devcom); void mlx5_devcom_comp_unlock(struct mlx5_devcom_comp_dev *devcom); int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom); +void mlx5_devcom_comp_assert_locked(struct mlx5_devcom_comp_dev *devcom); #endif /* __LIB_MLX5_DEVCOM_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/ipsec_fs_roce.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/ipsec_fs_roce.c index 28cb670ba33e..9aadb20b8b8e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/ipsec_fs_roce.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/ipsec_fs_roce.c @@ -116,6 +116,16 @@ static int ipsec_fs_create_aliased_ft(struct mlx5_core_dev *ibv_owner, memcpy(alias_attr.access_key, alias_key, ACCESS_KEY_LEN); alias_attr.obj_id = aliased_object_id; alias_attr.obj_type = MLX5_GENERAL_OBJECT_TYPES_FLOW_TABLE_ALIAS; + if (MLX5_CAP_GEN_2(ibv_owner, sw_vhca_id_valid) && + MLX5_CAP_GEN(ibv_allowed, ft_alias_sw_vhca_id)) { + vhca_id_to_be_accessed = MLX5_CAP_GEN_2(ibv_owner, sw_vhca_id); + alias_attr.vhca_id_type = VHCA_ID_TYPE_SW; + } else { + vhca_id_to_be_accessed = MLX5_CAP_GEN(ibv_owner, vhca_id); + alias_attr.vhca_id_type = VHCA_ID_TYPE_HW; + if (MLX5_CAP_GEN_2(ibv_owner, sw_vhca_id_valid)) + mlx5_core_warn(ibv_owner, "IPsec with migration isn't supported, if migration is required update FW.\n"); + } alias_attr.vhca_id = vhca_id_to_be_accessed; ret = mlx5_cmd_alias_obj_create(ibv_allowed, &alias_attr, obj_id); if (ret) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c index 19bb620b7436..4a7275e8b62e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c @@ -270,13 +270,13 @@ mlx5_nv_param_devlink_cqe_compress_get(struct devlink *devlink, u32 id, static int mlx5_nv_param_devlink_cqe_compress_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { int i; for (i = 0; i < ARRAY_SIZE(cqe_compress_str); i++) { - if (!strcmp(val.vstr, cqe_compress_str[i])) + if (!strcmp(val->vstr, cqe_compress_str[i])) return 0; } @@ -374,7 +374,7 @@ mlx5_devlink_swp_l4_csum_mode_get(struct devlink *devlink, u32 id, static int mlx5_devlink_swp_l4_csum_mode_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); @@ -383,7 +383,7 @@ mlx5_devlink_swp_l4_csum_mode_validate(struct devlink *devlink, u32 id, int err, i; for (i = 0; i < ARRAY_SIZE(swp_l4_csum_mode_str); i++) { - if (!strcmp(val.vstr, swp_l4_csum_mode_str[i])) + if (!strcmp(val->vstr, swp_l4_csum_mode_str[i])) break; } @@ -727,7 +727,7 @@ static int mlx5_devlink_total_vfs_set(struct devlink *devlink, u32 id, } static int mlx5_devlink_total_vfs_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { struct mlx5_core_dev *dev = devlink_priv(devlink); @@ -746,7 +746,7 @@ static int mlx5_devlink_total_vfs_validate(struct devlink *devlink, u32 id, return 0; /* optimistic, but set might fail later */ max = MLX5_GET(nv_global_pci_cap, data, max_vfs_per_pf); - if (val.vu16 > max) { + if (val->vu16 > max) { NL_SET_ERR_MSG_FMT_MOD(extack, "Max allowed by device is %u", max); return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c index 6e199161b008..ee2fdefa1945 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c @@ -2,9 +2,13 @@ /* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include "lib/sd.h" +#include "../lag/lag.h" #include "mlx5_core.h" #include "lib/mlx5.h" +#include "devlink.h" +#include "eswitch.h" #include "fs_cmd.h" +#include #include #include @@ -20,14 +24,19 @@ struct mlx5_sd { struct dentry *dfs; u8 state; bool primary; + bool fw_silents_secondaries; union { struct { /* primary */ struct mlx5_core_dev *secondaries[MLX5_SD_MAX_GROUP_SZ - 1]; struct mlx5_flow_table *tx_ft; + /* Next index for secondary registration */ + u8 next_secondary_idx; }; struct { /* secondary */ struct mlx5_core_dev *primary_dev; u32 alias_obj_id; + /* TX flow table root in switchdev (silent) config */ + bool tx_root_silent; }; }; }; @@ -47,16 +56,60 @@ static int mlx5_sd_get_host_buses(struct mlx5_core_dev *dev) return sd->host_buses; } -static struct mlx5_core_dev *mlx5_sd_get_primary(struct mlx5_core_dev *dev) +struct mlx5_core_dev *mlx5_sd_get_primary(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); if (!sd) return dev; + if (!mlx5_devcom_comp_is_ready(sd->devcom)) + return NULL; + return sd->primary ? dev : sd->primary_dev; } +struct mlx5_devcom_comp_dev *mlx5_sd_get_devcom(struct mlx5_core_dev *dev) +{ + struct mlx5_sd *sd = mlx5_get_sd(dev); + + if (!sd) + return NULL; + + return sd->devcom; +} + +bool mlx5_sd_is_primary(struct mlx5_core_dev *dev) +{ + struct mlx5_sd *sd = mlx5_get_sd(dev); + + if (!sd) + return true; + + return sd->primary; +} + +int mlx5_sd_pf_num_get(struct mlx5_core_dev *dev) +{ + struct mlx5_sd *sd = mlx5_get_sd(dev); + int pf_num = mlx5_get_dev_index(dev); + struct mlx5_core_dev *pos; + int i; + + if (!sd) + return pf_num; + + mlx5_devcom_comp_assert_locked(sd->devcom); + if (!mlx5_devcom_comp_is_ready(sd->devcom)) + return -ENODEV; + + mlx5_sd_for_each_dev(i, mlx5_sd_get_primary(dev), pos) + if (pos == dev) + break; + + return pf_num * sd->host_buses + i; +} + struct mlx5_core_dev * mlx5_sd_primary_get_peer(struct mlx5_core_dev *primary, int idx) { @@ -74,11 +127,17 @@ mlx5_sd_primary_get_peer(struct mlx5_core_dev *primary, int idx) int mlx5_sd_ch_ix_get_dev_ix(struct mlx5_core_dev *dev, int ch_ix) { + if (is_mdev_switchdev_mode(dev)) + return 0; + return ch_ix % mlx5_sd_get_host_buses(dev); } int mlx5_sd_ch_ix_get_vec_ix(struct mlx5_core_dev *dev, int ch_ix) { + if (is_mdev_switchdev_mode(dev)) + return ch_ix; + return ch_ix / mlx5_sd_get_host_buses(dev); } @@ -104,33 +163,6 @@ static bool ft_create_alias_supported(struct mlx5_core_dev *dev) return true; } -static bool mlx5_sd_is_supported(struct mlx5_core_dev *dev, u8 host_buses) -{ - /* Honor the SW implementation limit */ - if (host_buses > MLX5_SD_MAX_GROUP_SZ) - return false; - - /* Disconnect secondaries from the network */ - if (!MLX5_CAP_GEN(dev, eswitch_manager)) - return false; - if (!MLX5_CAP_GEN(dev, silent_mode_set)) - return false; - - /* RX steering from primary to secondaries */ - if (!MLX5_CAP_GEN(dev, cross_vhca_rqt)) - return false; - if (host_buses > MLX5_CAP_GEN_2(dev, max_rqt_vhca_id)) - return false; - - /* TX steering from secondaries to primary */ - if (!ft_create_alias_supported(dev)) - return false; - if (!MLX5_CAP_FLOWTABLE_NIC_TX(dev, reset_root_to_default)) - return false; - - return true; -} - static int mlx5_query_sd(struct mlx5_core_dev *dev, bool *sdm, u8 *host_buses) { @@ -152,6 +184,58 @@ static u32 mlx5_sd_group_id(struct mlx5_core_dev *dev, u8 sd_group) return (u32)((MLX5_CAP_GEN(dev, native_port_num) << 8) | sd_group); } +static bool mlx5_sd_caps_supported(struct mlx5_core_dev *dev, u8 host_buses) +{ + /* Honor the SW implementation limit */ + if (host_buses > MLX5_SD_MAX_GROUP_SZ) + return false; + + /* Disconnect secondaries from the network */ + if (!MLX5_CAP_GEN(dev, eswitch_manager)) + return false; + if (!MLX5_CAP_GEN(dev, silent_mode_set) && + !MLX5_CAP_GEN(dev, silent_mode_query)) + return false; + + /* RX steering from primary to secondaries */ + if (!MLX5_CAP_GEN(dev, cross_vhca_rqt)) + return false; + if (host_buses > MLX5_CAP_GEN_2(dev, max_rqt_vhca_id)) + return false; + + /* TX steering from secondaries to primary */ + if (!ft_create_alias_supported(dev)) + return false; + if (!MLX5_CAP_FLOWTABLE_NIC_TX(dev, reset_root_to_default)) + return false; + + return true; +} + +bool mlx5_sd_is_supported(struct mlx5_core_dev *dev) +{ + u8 host_buses, sd_group; + bool sdm; + int err; + + /* Feature is currently implemented for PFs only */ + if (!mlx5_core_is_pf(dev)) + return false; + + err = mlx5_query_nic_vport_sd_group(dev, &sd_group); + if (err || !sd_group) + return false; + + if (!MLX5_CAP_MCAM_REG(dev, mpir)) + return false; + + err = mlx5_query_sd(dev, &sdm, &host_buses); + if (err || !sdm) + return false; + + return mlx5_sd_caps_supported(dev, host_buses); +} + static int sd_init(struct mlx5_core_dev *dev) { u8 host_buses, sd_group; @@ -164,10 +248,6 @@ static int sd_init(struct mlx5_core_dev *dev) if (!mlx5_core_is_pf(dev)) return 0; - /* Block on embedded CPU PFs */ - if (mlx5_core_is_ecpf(dev)) - return 0; - err = mlx5_query_nic_vport_sd_group(dev, &sd_group); if (err) return err; @@ -187,8 +267,8 @@ static int sd_init(struct mlx5_core_dev *dev) group_id = mlx5_sd_group_id(dev, sd_group); - if (!mlx5_sd_is_supported(dev, host_buses)) { - sd_warn(dev, "can't support requested netdev combining for group id 0x%x), skipping\n", + if (!mlx5_sd_caps_supported(dev, host_buses)) { + sd_warn(dev, "can't support requested netdev combining for group id 0x%x, skipping\n", group_id); return 0; } @@ -213,62 +293,298 @@ static void sd_cleanup(struct mlx5_core_dev *dev) kfree(sd); } +static int sd_lag_state_show(struct seq_file *file, void *priv) +{ + struct mlx5_core_dev *dev = file->private; + struct mlx5_lag *ldev; + struct lag_func *pf; + bool active = false; + int i; + + ldev = mlx5_lag_dev(dev); + if (!ldev) + return -EINVAL; + + mutex_lock(&ldev->lock); + mlx5_ldev_for_each(i, 0, ldev) { + pf = mlx5_lag_pf(ldev, i); + if (pf->dev == dev) { + active = pf->sd_fdb_active; + break; + } + } + mutex_unlock(&ldev->lock); + + seq_printf(file, "%s\n", active ? "active" : "disabled"); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(sd_lag_state); + +/* SD LAG integration is optional. If LAG isn't available on this device + * (e.g. lag caps are off), or registering secondaries fails, just warn + * and continue - SD can operate without the LAG-side bookkeeping. + */ +static void sd_lag_init(struct mlx5_core_dev *dev) +{ + struct mlx5_core_dev *primary = mlx5_sd_get_primary(dev); + struct mlx5_sd *sd = mlx5_get_sd(primary); + struct mlx5_core_dev *pos, *to; + struct mlx5_lag *ldev; + struct lag_func *pf; + int err; + int i; + + ldev = mlx5_lag_dev(primary); + if (!ldev) { + sd_warn(primary, "%s: no ldev (LAG caps off?), skipping\n", + __func__); + return; + } + + mutex_lock(&ldev->lock); + pf = mlx5_lag_pf_by_dev(ldev, primary); + if (!pf) { + sd_warn(primary, "%s: primary not registered in ldev, skipping\n", + __func__); + goto out; + } + + pf->group_id = sd->group_id; + + mlx5_sd_for_each_secondary(i, primary, pos) { + err = mlx5_ldev_add_mdev(ldev, pos, sd->group_id); + if (err) { + sd_warn(primary, "%s: failed to add secondary %s to ldev: %d\n", + __func__, dev_name(pos->device), err); + goto err; + } + } + +out: + mutex_unlock(&ldev->lock); + return; + +err: + to = pos; + mlx5_sd_for_each_secondary_to(i, primary, to, pos) + mlx5_ldev_remove_mdev(ldev, pos); + pf->group_id = 0; + mutex_unlock(&ldev->lock); +} + +static void sd_lag_cleanup(struct mlx5_core_dev *dev) +{ + struct mlx5_core_dev *primary = mlx5_sd_get_primary(dev); + struct mlx5_core_dev *pos; + struct mlx5_lag *ldev; + struct lag_func *pf; + int i; + + ldev = mlx5_lag_dev(primary); + if (!ldev) + return; + + mutex_lock(&ldev->lock); + mlx5_sd_for_each_secondary(i, primary, pos) + mlx5_ldev_remove_mdev(ldev, pos); + + pf = mlx5_lag_pf_by_dev(ldev, primary); + if (pf) + pf->group_id = 0; + mutex_unlock(&ldev->lock); +} + +enum { + SD_PRIMARY_SET, + SD_SECONDARIES_SET, + SD_FW_SILENT_CHECK, +}; + +static int sd_handle_fw_silent_check(struct mlx5_core_dev *dev, + struct mlx5_core_dev *peer) +{ + struct mlx5_sd *peer_sd = mlx5_get_sd(peer); + struct mlx5_sd *sd = mlx5_get_sd(dev); + u8 dev_silent = 0, peer_silent = 0; + int err; + + if (peer_sd->fw_silents_secondaries) { + sd->fw_silents_secondaries = true; + return 0; + } + + err = mlx5_fs_cmd_query_l2table_silent(dev, &dev_silent); + if (err) { + sd_warn(dev, "Failed to query silent mode for dev: %d\n", err); + return err; + } + + err = mlx5_fs_cmd_query_l2table_silent(peer, &peer_silent); + if (err) { + sd_warn(dev, "Failed to query silent mode for peer: %d\n", err); + return err; + } + + if (dev_silent || peer_silent) { + sd->fw_silents_secondaries = true; + peer_sd->fw_silents_secondaries = true; + sd_info(dev, "FW indicates at least one device is silent\n"); + } + return 0; +} + +static int sd_handle_primary_set(struct mlx5_core_dev *dev, + struct mlx5_core_dev *peer) +{ + struct mlx5_sd *peer_sd = mlx5_get_sd(peer); + struct mlx5_sd *sd = mlx5_get_sd(dev); + struct mlx5_core_dev *candidate; + struct mlx5_sd *candidate_sd; + bool dev_should_be_primary; + + /* Peer is the device that being sent to all the other devices in the + * group. Hence, use peer to get the candidate device. + */ + candidate = peer_sd->primary ? peer : peer_sd->primary_dev; + + if (sd->fw_silents_secondaries) { + u8 candidate_silent = 0; + int err; + + err = mlx5_fs_cmd_query_l2table_silent(candidate, + &candidate_silent); + if (err) { + sd_warn(candidate, "Failed to query silent mode for dev: %d\n", + err); + return err; + } + /* Candidate is silent, dev should be primary */ + dev_should_be_primary = candidate_silent; + } else { + /* No FW silent mode, use bus number */ + dev_should_be_primary = + dev->pdev->bus->number < candidate->pdev->bus->number; + } + + if (!dev_should_be_primary) + return 0; + + candidate_sd = mlx5_get_sd(candidate); + + sd->primary = true; + candidate_sd->primary = false; + candidate_sd->primary_dev = dev; + peer_sd->primary = false; + peer_sd->primary_dev = dev; + return 0; +} + +static void sd_handle_secondaries_set(struct mlx5_core_dev *dev, + struct mlx5_core_dev *peer) +{ + struct mlx5_sd *peer_sd = mlx5_get_sd(peer); + struct mlx5_sd *sd = mlx5_get_sd(dev); + u8 idx; + + /* Primary has nothing to register with itself. */ + if (sd->primary) + return; + + /* dev is a secondary device, peer is the primary device. + * Secondary registers itself with the primary. + */ + idx = peer_sd->next_secondary_idx++; + peer_sd->secondaries[idx] = dev; + sd->primary_dev = peer; +} + +static int mlx5_sd_devcom_event(int event, void *my_data, void *event_data) +{ + struct mlx5_core_dev *peer = event_data; + struct mlx5_core_dev *dev = my_data; + + switch (event) { + case SD_FW_SILENT_CHECK: + return sd_handle_fw_silent_check(dev, peer); + case SD_PRIMARY_SET: + return sd_handle_primary_set(dev, peer); + case SD_SECONDARIES_SET: + sd_handle_secondaries_set(dev, peer); + return 0; + } + + return 0; +} + static int sd_register(struct mlx5_core_dev *dev) { - struct mlx5_devcom_comp_dev *devcom, *pos; struct mlx5_devcom_match_attr attr = {}; - struct mlx5_core_dev *peer, *primary; - struct mlx5_sd *sd, *primary_sd; - int err, i; + struct mlx5_devcom_comp_dev *devcom; + struct mlx5_core_dev *primary; + struct mlx5_sd *primary_sd; + struct mlx5_sd *sd; + int err; sd = mlx5_get_sd(dev); attr.key.val = sd->group_id; attr.flags = MLX5_DEVCOM_MATCH_FLAGS_NS; attr.net = mlx5_core_net(dev); - devcom = mlx5_devcom_register_component(dev->priv.devc, MLX5_DEVCOM_SD_GROUP, - &attr, NULL, dev); + devcom = mlx5_devcom_register_component(dev->priv.devc, + MLX5_DEVCOM_SD_GROUP, + &attr, mlx5_sd_devcom_event, + dev); if (!devcom) return -EINVAL; sd->devcom = devcom; - if (mlx5_devcom_comp_get_size(devcom) != sd->host_buses) - return 0; - mlx5_devcom_comp_lock(devcom); - mlx5_devcom_comp_set_ready(devcom, true); - mlx5_devcom_comp_unlock(devcom); + if (mlx5_devcom_comp_get_size(devcom) != sd->host_buses || + mlx5_devcom_comp_is_ready(devcom)) + goto out; - if (!mlx5_devcom_for_each_peer_begin(devcom)) { - err = -ENODEV; - goto err_devcom_unreg; + /* If silent mode query is supported, ask each device whether it is + * silent and propagate the result to the whole group. In each group + * only one device is not silent + */ + if (MLX5_CAP_GEN(dev, silent_mode_query)) { + err = mlx5_devcom_locked_send_event(devcom, SD_FW_SILENT_CHECK, + SD_FW_SILENT_CHECK, dev); + if (err) + goto err_devcom_unreg; } - primary = dev; - mlx5_devcom_for_each_peer_entry(devcom, peer, pos) - if (peer->pdev->bus->number < primary->pdev->bus->number) - primary = peer; + /* Send SD_PRIMARY_SET event with this device. + * All peers will receive this event and compare to this device. + * If fw_silents_secondaries is set, choose non-silent device. + * Otherwise use bus number. + */ + sd->primary = true; + err = mlx5_devcom_locked_send_event(devcom, SD_PRIMARY_SET, + SD_PRIMARY_SET, dev); + if (err) + goto err_devcom_unreg; + + /* Broadcast SD_SECONDARIES_SET. Each non-sender peer's handler runs; + * the primary's handler returns early so only secondaries register. + */ + primary = sd->primary ? dev : sd->primary_dev; + if (!sd->primary) + sd_handle_secondaries_set(dev, primary); + mlx5_devcom_locked_send_event(devcom, SD_SECONDARIES_SET, + DEVCOM_CANT_FAIL, primary); primary_sd = mlx5_get_sd(primary); - primary_sd->primary = true; - i = 0; - /* loop the secondaries */ - mlx5_devcom_for_each_peer_entry(primary_sd->devcom, peer, pos) { - struct mlx5_sd *peer_sd = mlx5_get_sd(peer); - - primary_sd->secondaries[i++] = peer; - peer_sd->primary = false; - peer_sd->primary_dev = primary; - } - - mlx5_devcom_for_each_peer_end(devcom); + if (primary_sd->next_secondary_idx + 1 == sd->host_buses) + mlx5_devcom_comp_set_ready(devcom, true); +out: + mlx5_devcom_comp_unlock(devcom); return 0; err_devcom_unreg: - mlx5_devcom_comp_lock(sd->devcom); - mlx5_devcom_comp_set_ready(sd->devcom, false); - mlx5_devcom_comp_unlock(sd->devcom); - mlx5_devcom_unregister_component(sd->devcom); + mlx5_devcom_comp_unlock(devcom); + mlx5_devcom_unregister_component(devcom); return err; } @@ -352,6 +668,29 @@ static void sd_secondary_destroy_alias_ft(struct mlx5_core_dev *secondary) MLX5_GENERAL_OBJECT_TYPES_FLOW_TABLE_ALIAS); } +static int mlx5_sd_secondary_conf_tx_root(struct mlx5_core_dev *secondary, + bool disconnect) +{ + struct mlx5_sd *sd = mlx5_get_sd(secondary); + int err; + + /* Idempotent: skip if TX root is already in the requested state. */ + if (sd->tx_root_silent == disconnect) + return 0; + + if (disconnect) + err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, 0, true); + else + err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, + sd->alias_obj_id, + false); + if (err) + return err; + + sd->tx_root_silent = disconnect; + return 0; +} + static int sd_cmd_set_secondary(struct mlx5_core_dev *secondary, struct mlx5_core_dev *primary, u8 *alias_key) @@ -360,33 +699,42 @@ static int sd_cmd_set_secondary(struct mlx5_core_dev *secondary, struct mlx5_sd *sd = mlx5_get_sd(secondary); int err; - err = mlx5_fs_cmd_set_l2table_entry_silent(secondary, 1); - if (err) - return err; + if (!primary_sd->fw_silents_secondaries) { + err = mlx5_fs_cmd_set_l2table_entry_silent(secondary, 1); + if (err) + return err; + } err = sd_secondary_create_alias_ft(secondary, primary, primary_sd->tx_ft, &sd->alias_obj_id, alias_key); if (err) goto err_unset_silent; - err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, sd->alias_obj_id, false); + err = mlx5_fs_cmd_set_tx_flow_table_root(secondary, sd->alias_obj_id, + false); if (err) goto err_destroy_alias_ft; + sd->tx_root_silent = false; return 0; err_destroy_alias_ft: sd_secondary_destroy_alias_ft(secondary); err_unset_silent: - mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0); + if (!primary_sd->fw_silents_secondaries) + mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0); return err; } static void sd_cmd_unset_secondary(struct mlx5_core_dev *secondary) { - mlx5_fs_cmd_set_tx_flow_table_root(secondary, 0, true); + struct mlx5_sd *primary_sd; + + primary_sd = mlx5_get_sd(mlx5_sd_get_primary(secondary)); + mlx5_sd_secondary_conf_tx_root(secondary, true); sd_secondary_destroy_alias_ft(secondary); - mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0); + if (!primary_sd->fw_silents_secondaries) + mlx5_fs_cmd_set_l2table_entry_silent(secondary, 0); } static void sd_print_group(struct mlx5_core_dev *primary) @@ -463,27 +811,32 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) if (err) goto err_sd_unregister; - primary_sd->dfs = - debugfs_create_dir("multi-pf", - mlx5_debugfs_get_dev_root(primary)); - debugfs_create_x32("group_id", 0400, primary_sd->dfs, - &primary_sd->group_id); - debugfs_create_file("primary", 0400, primary_sd->dfs, primary, - &dev_fops); - mlx5_sd_for_each_secondary(i, primary, pos) { - char name[32]; - err = sd_cmd_set_secondary(pos, primary, alias_key); if (err) goto err_unset_secondaries; + } + + sd_lag_init(primary); + + primary_sd->dfs = + debugfs_create_dir("multi-pf", + mlx5_debugfs_get_dev_root(primary)); + mlx5_sd_for_each_secondary(i, primary, pos) { + char name[32]; snprintf(name, sizeof(name), "secondary_%d", i - 1); debugfs_create_file(name, 0400, primary_sd->dfs, pos, &dev_fops); - } + debugfs_create_file("sd_lag_state", 0400, primary_sd->dfs, primary, + &sd_lag_state_fops); + debugfs_create_x32("group_id", 0400, primary_sd->dfs, + &primary_sd->group_id); + debugfs_create_file("primary", 0400, primary_sd->dfs, primary, + &dev_fops); + sd_info(primary, "group id %#x, size %d, combined\n", sd->group_id, mlx5_devcom_comp_get_size(sd->devcom)); sd_print_group(primary); @@ -498,8 +851,6 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) mlx5_sd_for_each_secondary_to(i, primary, to, pos) sd_cmd_unset_secondary(pos); sd_cmd_unset_primary(primary); - debugfs_remove_recursive(primary_sd->dfs); - primary_sd->dfs = NULL; err_sd_unregister: mlx5_sd_for_each_secondary(i, primary, pos) { struct mlx5_sd *peer_sd = mlx5_get_sd(pos); @@ -508,6 +859,7 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) peer_sd->primary_dev = NULL; } primary_sd->primary = false; + primary_sd->next_secondary_idx = 0; mlx5_devcom_comp_set_ready(sd->devcom, false); mlx5_devcom_comp_unlock(sd->devcom); sd_unregister(dev); @@ -538,11 +890,12 @@ void mlx5_sd_cleanup(struct mlx5_core_dev *dev) if (primary_sd->state != MLX5_SD_STATE_UP) goto out_clear_peers; + debugfs_remove_recursive(primary_sd->dfs); + primary_sd->dfs = NULL; + sd_lag_cleanup(primary); mlx5_sd_for_each_secondary(i, primary, pos) sd_cmd_unset_secondary(pos); sd_cmd_unset_primary(primary); - debugfs_remove_recursive(primary_sd->dfs); - primary_sd->dfs = NULL; sd_info(primary, "group id %#x, uncombined\n", sd->group_id); primary_sd->state = MLX5_SD_STATE_DOWN; @@ -554,6 +907,7 @@ void mlx5_sd_cleanup(struct mlx5_core_dev *dev) peer_sd->primary_dev = NULL; } primary_sd->primary = false; + primary_sd->next_secondary_idx = 0; out_ready_false: mlx5_devcom_comp_set_ready(sd->devcom, false); out_unlock: @@ -606,6 +960,127 @@ struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev, return &primary_adev->adev; } +#ifdef CONFIG_MLX5_ESWITCH +/* All SD members must have completed esw_offloads_enable (i.e., reached + * mlx5_esw_offloads_devcom_init) and become eswitch-peers of the primary. + * Until then, mlx5_eswitch_is_peer() returns false for the not-yet-paired + * member and shared_fdb_supported_filter would reject. When all PFs transition + * in parallel, only the last one to finish satisfies this gate; the earlier + * ones return 0 silently here. + */ +static bool mlx5_sd_all_paired(struct mlx5_core_dev *primary) +{ + struct mlx5_eswitch *primary_esw = primary->priv.eswitch; + struct mlx5_core_dev *pos; + int i; + + mlx5_sd_for_each_secondary(i, primary, pos) { + if (!mlx5_eswitch_is_peer(primary_esw, pos->priv.eswitch)) + return false; + } + return true; +} + +static void mlx5_sd_activate_shared_fdb(struct mlx5_core_dev *primary) +{ + struct mlx5_sd *sd = mlx5_get_sd(primary); + struct mlx5_core_dev *pos; + struct mlx5_lag *ldev; + struct lag_func *pf; + int err; + int i; + + ldev = mlx5_lag_dev(primary); + if (!ldev) { + sd_warn(primary, "Shared FDB MUST have ldev\n"); + return; + } + + mutex_lock(&ldev->lock); + + if (ldev->mode_changes_in_progress) + goto unlock; + + if (!mlx5_sd_all_paired(primary)) + goto unlock; + + /* Check if SD FDB is already active for this group */ + mlx5_lag_for_each(i, 0, ldev, sd->group_id) { + pf = mlx5_lag_pf(ldev, i); + if (pf->sd_fdb_active) + goto unlock; + break; + } + + if (!mlx5_lag_shared_fdb_supported_filter(ldev, sd->group_id)) { + sd_warn(primary, "Shared FDB not supported\n"); + goto unlock; + } + + /* Initialize vport metadata for all group devices. This is deferred + * from esw_offloads_enable() because mlx5_sd_pf_num_get() requires + * the SD group to be ready. + */ + mlx5_sd_for_each_dev(i, primary, pos) { + struct mlx5_eswitch *esw = pos->priv.eswitch; + + err = mlx5_esw_offloads_init_deferred_metadata(esw); + if (err) { + sd_warn(primary, "Failed to init metadata for %s: %d\n", + dev_name(pos->device), err); + goto unlock; + } + } + + err = mlx5_lag_shared_fdb_create(ldev, NULL, 0, sd->group_id); + if (err) + sd_warn(primary, "Failed to create shared FDB: %d\n", err); + else + sd_info(primary, "Shared FDB created\n"); + +unlock: + mutex_unlock(&ldev->lock); +} + +void mlx5_sd_eswitch_mode_set(struct mlx5_core_dev *dev, u16 mlx5_mode) +{ + struct mlx5_core_dev *primary; + struct mlx5_sd *sd; + int err; + + sd = mlx5_get_sd(dev); + if (!sd || !mlx5_devcom_comp_is_ready(sd->devcom)) + return; + + mlx5_devcom_comp_lock(sd->devcom); + if (!mlx5_devcom_comp_is_ready(sd->devcom)) + goto unlock; + + primary = mlx5_sd_get_primary(dev); + + /* Secondary devices need TX root reconfiguration */ + if (dev != primary) { + bool disconnect = (mlx5_mode == MLX5_ESWITCH_OFFLOADS); + + err = mlx5_sd_secondary_conf_tx_root(dev, disconnect); + if (err) { + sd_warn(dev, "Failed to set TX root: %d\n", err); + goto unlock; + } + } + + /* Try to activate shared FDB when all devices are in switchdev. + * Shared FDB is optional - failure here doesn't fail the transition. + */ + if (mlx5_mode == MLX5_ESWITCH_OFFLOADS) + mlx5_sd_activate_shared_fdb(primary); + +unlock: + mlx5_devcom_comp_unlock(sd->devcom); +} + +#endif /* CONFIG_MLX5_ESWITCH */ + void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, struct auxiliary_device *adev) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h index 9bfd5b9756b5..cb88bf34079a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h @@ -4,10 +4,15 @@ #ifndef __MLX5_LIB_SD_H__ #define __MLX5_LIB_SD_H__ +#include + #define MLX5_SD_MAX_GROUP_SZ 2 struct mlx5_sd; +struct mlx5_core_dev *mlx5_sd_get_primary(struct mlx5_core_dev *dev); +bool mlx5_sd_is_primary(struct mlx5_core_dev *dev); +int mlx5_sd_pf_num_get(struct mlx5_core_dev *dev); struct mlx5_core_dev *mlx5_sd_primary_get_peer(struct mlx5_core_dev *primary, int idx); int mlx5_sd_ch_ix_get_dev_ix(struct mlx5_core_dev *dev, int ch_ix); int mlx5_sd_ch_ix_get_vec_ix(struct mlx5_core_dev *dev, int ch_ix); @@ -18,9 +23,35 @@ struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev, void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, struct auxiliary_device *adev); +#ifdef CONFIG_MLX5_CORE_EN +bool mlx5_sd_is_supported(struct mlx5_core_dev *dev); +#else +static inline bool mlx5_sd_is_supported(struct mlx5_core_dev *dev) +{ + return false; +} +#endif + int mlx5_sd_init(struct mlx5_core_dev *dev); void mlx5_sd_cleanup(struct mlx5_core_dev *dev); +#ifdef CONFIG_MLX5_CORE_EN +struct mlx5_devcom_comp_dev *mlx5_sd_get_devcom(struct mlx5_core_dev *dev); +#else +static inline struct mlx5_devcom_comp_dev * +mlx5_sd_get_devcom(struct mlx5_core_dev *dev) +{ + return NULL; +} +#endif + +#ifdef CONFIG_MLX5_ESWITCH +void mlx5_sd_eswitch_mode_set(struct mlx5_core_dev *dev, u16 mlx5_mode); +#else +static inline void +mlx5_sd_eswitch_mode_set(struct mlx5_core_dev *dev, u16 mlx5_mode) { return; } +#endif + #define mlx5_sd_for_each_dev_from_to(i, primary, ix_from, to, pos) \ for (i = ix_from; \ (pos = mlx5_sd_primary_get_peer(primary, i)) && pos != (to); i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 74827e8ca125..643b4aac2033 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -527,7 +527,6 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) { struct mlx5_profile *prof = &dev->profile; void *set_hca_cap; - int max_uc_list; int err; err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL); @@ -597,6 +596,9 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) if (MLX5_CAP_GEN_MAX(dev, release_all_pages)) MLX5_SET(cmd_hca_cap, set_hca_cap, release_all_pages, 1); + if (MLX5_CAP_GEN_MAX(dev, icm_mng_function_id_mode)) + MLX5_SET(cmd_hca_cap, set_hca_cap, icm_mng_function_id_mode, 1); + if (MLX5_CAP_GEN_MAX(dev, mkey_by_name)) MLX5_SET(cmd_hca_cap, set_hca_cap, mkey_by_name, 1); @@ -610,10 +612,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx) MLX5_SET(cmd_hca_cap, set_hca_cap, roce, mlx5_is_roce_on(dev)); - max_uc_list = max_uc_list_get_devlink_param(dev); - if (max_uc_list > 0) - MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_current_uc_list, - ilog2(max_uc_list)); + if (MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) { + int max_uc_list = max_uc_list_get_devlink_param(dev); + + if (max_uc_list > 0) + MLX5_SET(cmd_hca_cap, set_hca_cap, + log_max_current_uc_list, ilog2(max_uc_list)); + } /* enable absolute native port num */ if (MLX5_CAP_GEN_MAX(dev, abs_native_port_num)) @@ -987,11 +992,12 @@ static int mlx5_init_once(struct mlx5_core_dev *dev) mlx5_core_err(dev, "Failed to init eswitch %d\n", err); goto err_sriov_cleanup; } + mlx5_pages_by_func_type_debugfs_init(dev); err = mlx5_fpga_init(dev); if (err) { mlx5_core_err(dev, "Failed to init fpga device %d\n", err); - goto err_eswitch_cleanup; + goto err_page_debugfs_cleanup; } err = mlx5_vhca_event_init(dev); @@ -1034,7 +1040,8 @@ static int mlx5_init_once(struct mlx5_core_dev *dev) mlx5_vhca_event_cleanup(dev); err_fpga_cleanup: mlx5_fpga_cleanup(dev); -err_eswitch_cleanup: +err_page_debugfs_cleanup: + mlx5_pages_by_func_type_debugfs_cleanup(dev); mlx5_eswitch_cleanup(dev->priv.eswitch); err_sriov_cleanup: mlx5_sriov_cleanup(dev); @@ -1072,6 +1079,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) mlx5_sf_hw_table_cleanup(dev); mlx5_vhca_event_cleanup(dev); mlx5_fpga_cleanup(dev); + mlx5_pages_by_func_type_debugfs_cleanup(dev); mlx5_eswitch_cleanup(dev->priv.eswitch); mlx5_sriov_cleanup(dev); mlx5_mpfs_cleanup(dev); @@ -1363,7 +1371,6 @@ static int mlx5_load(struct mlx5_core_dev *dev) static void mlx5_unload(struct mlx5_core_dev *dev) { - mlx5_eswitch_disable(dev->priv.eswitch); mlx5_devlink_traps_unregister(priv_to_devlink(dev)); mlx5_vhca_event_stop(dev); mlx5_sf_dev_table_destroy(dev); @@ -1478,6 +1485,7 @@ void mlx5_uninit_one(struct mlx5_core_dev *dev) mlx5_hwmon_dev_unregister(dev); mlx5_crdump_disable(dev); + mlx5_eswitch_disable(dev->priv.eswitch); mlx5_unregister_device(dev); if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) { @@ -1562,6 +1570,7 @@ void mlx5_unload_one_devl_locked(struct mlx5_core_dev *dev, bool suspend) devl_assert_locked(priv_to_devlink(dev)); mutex_lock(&dev->intf_state_mutex); + mlx5_eswitch_disable(dev->priv.eswitch); mlx5_detach_device(dev, suspend); if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) { @@ -1817,6 +1826,10 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) priv->dbg.dbg_root = debugfs_create_dir(dev_name(dev->device), mlx5_debugfs_root); + err = mlx5_frag_buf_pools_init(dev); + if (err) + goto err_frag_buf_pools_init; + INIT_LIST_HEAD(&priv->traps); err = mlx5_cmd_init(dev); @@ -1878,6 +1891,8 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) err_timeout_init: mlx5_cmd_cleanup(dev); err_cmd_init: + mlx5_frag_buf_pools_cleanup(dev); +err_frag_buf_pools_init: debugfs_remove(dev->priv.dbg.dbg_root); mutex_destroy(&priv->pgdir_mutex); mutex_destroy(&priv->alloc_mutex); @@ -1902,6 +1917,7 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev) mlx5_health_cleanup(dev); mlx5_tout_cleanup(dev); mlx5_cmd_cleanup(dev); + mlx5_frag_buf_pools_cleanup(dev); debugfs_remove_recursive(dev->priv.dbg.dbg_root); mutex_destroy(&priv->pgdir_mutex); mutex_destroy(&priv->alloc_mutex); @@ -2203,19 +2219,26 @@ static int mlx5_resume(struct pci_dev *pdev) static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTIB) }, - { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF}, /* Connect-IB VF */ + { PCI_VDEVICE(MELLANOX, 0x1012), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* Connect-IB VF */ { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTX4) }, - { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4 VF */ + { PCI_VDEVICE(MELLANOX, 0x1014), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* ConnectX-4 VF */ { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX) }, - { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */ + { PCI_VDEVICE(MELLANOX, 0x1016), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* ConnectX-4LX VF */ { PCI_VDEVICE(MELLANOX, 0x1017) }, /* ConnectX-5, PCIe 3.0 */ - { PCI_VDEVICE(MELLANOX, 0x1018), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 VF */ + { PCI_VDEVICE(MELLANOX, 0x1018), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* ConnectX-5 VF */ { PCI_VDEVICE(MELLANOX, 0x1019) }, /* ConnectX-5 Ex */ - { PCI_VDEVICE(MELLANOX, 0x101a), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 Ex VF */ + { PCI_VDEVICE(MELLANOX, 0x101a), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* ConnectX-5 Ex VF */ { PCI_VDEVICE(MELLANOX, 0x101b) }, /* ConnectX-6 */ - { PCI_VDEVICE(MELLANOX, 0x101c), MLX5_PCI_DEV_IS_VF}, /* ConnectX-6 VF */ + { PCI_VDEVICE(MELLANOX, 0x101c), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* ConnectX-6 VF */ { PCI_VDEVICE(MELLANOX, 0x101d) }, /* ConnectX-6 Dx */ - { PCI_VDEVICE(MELLANOX, 0x101e), MLX5_PCI_DEV_IS_VF}, /* ConnectX Family mlx5Gen Virtual Function */ + { PCI_VDEVICE(MELLANOX, 0x101e), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* ConnectX Family mlx5Gen Virtual Function */ { PCI_VDEVICE(MELLANOX, 0x101f) }, /* ConnectX-6 LX */ { PCI_VDEVICE(MELLANOX, 0x1021) }, /* ConnectX-7 */ { PCI_VDEVICE(MELLANOX, 0x1023) }, /* ConnectX-8 */ @@ -2223,11 +2246,12 @@ static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1027) }, /* ConnectX-10 */ { PCI_VDEVICE(MELLANOX, 0x2101) }, /* ConnectX-10 NVLink-C2C */ { PCI_VDEVICE(MELLANOX, 0xa2d2) }, /* BlueField integrated ConnectX-5 network controller */ - { PCI_VDEVICE(MELLANOX, 0xa2d3), MLX5_PCI_DEV_IS_VF}, /* BlueField integrated ConnectX-5 network controller VF */ + { PCI_VDEVICE(MELLANOX, 0xa2d3), + .driver_data = MLX5_PCI_DEV_IS_VF }, /* BlueField integrated ConnectX-5 network controller VF */ { PCI_VDEVICE(MELLANOX, 0xa2d6) }, /* BlueField-2 integrated ConnectX-6 Dx network controller */ { PCI_VDEVICE(MELLANOX, 0xa2dc) }, /* BlueField-3 integrated ConnectX-7 network controller */ { PCI_VDEVICE(MELLANOX, 0xa2df) }, /* BlueField-4 integrated ConnectX-8 network controller */ - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, mlx5_core_pci_table); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 1507e881d962..51637e58a48b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -110,6 +110,7 @@ struct mlx5_cmd_allow_other_vhca_access_attr { struct mlx5_cmd_alias_obj_create_attr { u32 obj_id; u16 vhca_id; + u8 vhca_id_type; u16 obj_type; u8 access_key[ACCESS_KEY_LEN]; }; @@ -436,6 +437,8 @@ mlx5_sf_coredev_to_adev(struct mlx5_core_dev *mdev) int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx); void mlx5_mdev_uninit(struct mlx5_core_dev *dev); +int mlx5_frag_buf_pools_init(struct mlx5_core_dev *dev); +void mlx5_frag_buf_pools_cleanup(struct mlx5_core_dev *dev); int mlx5_init_one(struct mlx5_core_dev *dev); int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev); void mlx5_uninit_one(struct mlx5_core_dev *dev); @@ -449,11 +452,17 @@ void mlx5_unload_one_light(struct mlx5_core_dev *dev); void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf, u8 *len); +bool mlx5_vport_use_vhca_id_as_func_id(struct mlx5_core_dev *dev, + u16 vport_num, u16 *vhca_id); int mlx5_vport_set_other_func_cap(struct mlx5_core_dev *dev, const void *hca_cap, u16 vport, u16 opmod); #define mlx5_vport_get_other_func_general_cap(dev, vport, out) \ mlx5_vport_get_other_func_cap(dev, vport, out, MLX5_CAP_GENERAL) +#define mlx5_vport_set_other_func_general_cap(dev, hca_cap, vport) \ + mlx5_vport_set_other_func_cap(dev, hca_cap, vport, \ + MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE) + static inline u32 mlx5_sriov_get_vf_total_msix(struct pci_dev *pdev) { struct mlx5_core_dev *dev = pci_get_drvdata(pdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 77ffa31cc505..7fef3a7fee6e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -38,6 +38,7 @@ #include "mlx5_core.h" #include "lib/eq.h" #include "lib/tout.h" +#include "eswitch.h" enum { MLX5_PAGES_CANT_GIVE = 0, @@ -59,6 +60,7 @@ struct fw_page { u64 addr; struct page *page; u32 function; + u16 func_type; unsigned long bitmask; struct list_head list; unsigned int free_count; @@ -69,9 +71,24 @@ enum { MLX5_NUM_4K_IN_PAGE = PAGE_SIZE / MLX5_ADAPTER_PAGE_SIZE, }; -static u32 get_function(u16 func_id, bool ec_function) +static bool mlx5_page_mgt_mode_is_vhca_id(const struct mlx5_core_dev *dev) { - return (u32)func_id | (ec_function << 16); + return dev->priv.page_mgt_mode == MLX5_PAGE_MGT_MODE_VHCA_ID; +} + +static void mlx5_page_mgt_mode_set(struct mlx5_core_dev *dev, + enum mlx5_page_mgt_mode mode) +{ + dev->priv.page_mgt_mode = mode; +} + +static u32 get_function_key(struct mlx5_core_dev *dev, u16 func_vhca_id, + bool ec_function) +{ + if (mlx5_page_mgt_mode_is_vhca_id(dev)) + return (u32)func_vhca_id; + + return (u32)func_vhca_id | (ec_function << 16); } static u16 func_id_to_type(struct mlx5_core_dev *dev, u16 func_id, bool ec_function) @@ -89,12 +106,21 @@ static u16 func_id_to_type(struct mlx5_core_dev *dev, u16 func_id, bool ec_funct return MLX5_SF; } +static u16 func_vhca_id_to_type(struct mlx5_core_dev *dev, u16 func_vhca_id, + bool ec_function) +{ + if (mlx5_page_mgt_mode_is_vhca_id(dev)) + return mlx5_esw_vhca_id_to_func_type(dev, func_vhca_id); + + return func_id_to_type(dev, func_vhca_id, ec_function); +} + static u32 mlx5_get_ec_function(u32 function) { return function >> 16; } -static u32 mlx5_get_func_id(u32 function) +static u32 mlx5_get_func_vhca_id(u32 function) { return function & 0xffff; } @@ -123,7 +149,8 @@ static struct rb_root *page_root_per_function(struct mlx5_core_dev *dev, u32 fun return root; } -static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u32 function) +static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, + u32 function, u16 func_type) { struct rb_node *parent = NULL; struct rb_root *root; @@ -156,6 +183,7 @@ static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u nfp->addr = addr; nfp->page = page; nfp->function = function; + nfp->func_type = func_type; nfp->free_count = MLX5_NUM_4K_IN_PAGE; for (i = 0; i < MLX5_NUM_4K_IN_PAGE; i++) set_bit(i, &nfp->bitmask); @@ -196,7 +224,7 @@ static struct fw_page *find_fw_page(struct mlx5_core_dev *dev, u64 addr, return result; } -static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id, +static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_vhca_id, s32 *npages, int boot) { u32 out[MLX5_ST_SZ_DW(query_pages_out)] = {}; @@ -207,14 +235,20 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id, MLX5_SET(query_pages_in, in, op_mod, boot ? MLX5_QUERY_PAGES_IN_OP_MOD_BOOT_PAGES : MLX5_QUERY_PAGES_IN_OP_MOD_INIT_PAGES); - MLX5_SET(query_pages_in, in, embedded_cpu_function, mlx5_core_is_ecpf(dev)); + + if (mlx5_page_mgt_mode_is_vhca_id(dev)) + MLX5_SET(query_pages_in, in, function_id, + MLX5_CAP_GEN(dev, vhca_id)); + else + MLX5_SET(query_pages_in, in, embedded_cpu_function, + mlx5_core_is_ecpf(dev)); err = mlx5_cmd_exec_inout(dev, query_pages, in, out); if (err) return err; *npages = MLX5_GET(query_pages_out, out, num_pages); - *func_id = MLX5_GET(query_pages_out, out, function_id); + *func_vhca_id = MLX5_GET(query_pages_out, out, function_id); return err; } @@ -245,6 +279,10 @@ static int alloc_4k(struct mlx5_core_dev *dev, u64 *addr, u32 function) if (!fp->free_count) list_del(&fp->list); + if (fp->func_type != MLX5_FUNC_TYPE_NONE) + dev->priv.page_counters[fp->func_type]++; + dev->priv.fw_pages++; + *addr = fp->addr + n * MLX5_ADAPTER_PAGE_SIZE; return 0; @@ -280,6 +318,11 @@ static void free_4k(struct mlx5_core_dev *dev, u64 addr, u32 function) mlx5_core_warn_rl(dev, "page not found\n"); return; } + + if (fwp->func_type != MLX5_FUNC_TYPE_NONE) + dev->priv.page_counters[fwp->func_type]--; + dev->priv.fw_pages--; + n = (addr & ~MLX5_U64_4K_PAGE_MASK) >> MLX5_ADAPTER_PAGE_SHIFT; fwp->free_count++; set_bit(n, &fwp->bitmask); @@ -289,7 +332,8 @@ static void free_4k(struct mlx5_core_dev *dev, u64 addr, u32 function) list_add(&fwp->list, &dev->priv.free_list); } -static int alloc_system_page(struct mlx5_core_dev *dev, u32 function) +static int alloc_system_page(struct mlx5_core_dev *dev, u32 function, + u16 func_type) { struct device *device = mlx5_core_dma_dev(dev); int nid = dev->priv.numa_node; @@ -317,7 +361,7 @@ static int alloc_system_page(struct mlx5_core_dev *dev, u32 function) goto map; } - err = insert_page(dev, addr, page, function); + err = insert_page(dev, addr, page, function, func_type); if (err) { mlx5_core_err(dev, "failed to track allocated page\n"); dma_unmap_page(device, addr, PAGE_SIZE, DMA_BIDIRECTIONAL); @@ -334,7 +378,7 @@ static int alloc_system_page(struct mlx5_core_dev *dev, u32 function) return err; } -static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id, +static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_vhca_id, bool ec_function) { u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {}; @@ -342,19 +386,23 @@ static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id, MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES); MLX5_SET(manage_pages_in, in, op_mod, MLX5_PAGES_CANT_GIVE); - MLX5_SET(manage_pages_in, in, function_id, func_id); - MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function); + MLX5_SET(manage_pages_in, in, function_id, func_vhca_id); + + if (!mlx5_page_mgt_mode_is_vhca_id(dev)) + MLX5_SET(manage_pages_in, in, embedded_cpu_function, + ec_function); err = mlx5_cmd_exec_in(dev, manage_pages, in); if (err) - mlx5_core_warn(dev, "page notify failed func_id(%d) err(%d)\n", - func_id, err); + mlx5_core_warn(dev, + "page notify failed func_vhca_id(%d) err(%d)\n", + func_vhca_id, err); } -static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, +static int give_pages(struct mlx5_core_dev *dev, u16 func_vhca_id, int npages, int event, bool ec_function) { - u32 function = get_function(func_id, ec_function); + u32 function = get_function_key(dev, func_vhca_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; @@ -364,6 +412,8 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, u32 *in; int i; + func_type = func_vhca_id_to_type(dev, func_vhca_id, ec_function); + inlen += npages * MLX5_FLD_SZ_BYTES(manage_pages_in, pas[0]); in = kvzalloc(inlen, GFP_KERNEL); if (!in) { @@ -377,7 +427,8 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, err = alloc_4k(dev, &addr, function); if (err) { if (err == -ENOMEM) - err = alloc_system_page(dev, function); + err = alloc_system_page(dev, function, + func_type); if (err) { dev->priv.fw_pages_alloc_failed += (npages - i); goto out_4k; @@ -390,9 +441,12 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES); MLX5_SET(manage_pages_in, in, op_mod, MLX5_PAGES_GIVE); - MLX5_SET(manage_pages_in, in, function_id, func_id); + MLX5_SET(manage_pages_in, in, function_id, func_vhca_id); MLX5_SET(manage_pages_in, in, input_num_entries, npages); - MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function); + + if (!mlx5_page_mgt_mode_is_vhca_id(dev)) + MLX5_SET(manage_pages_in, in, embedded_cpu_function, + ec_function); err = mlx5_cmd_do(dev, in, inlen, out, sizeof(out)); if (err == -EREMOTEIO) { @@ -405,17 +459,15 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, } err = mlx5_cmd_check(dev, err, in, out); if (err) { - mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", - func_id, npages, err); + mlx5_core_warn(dev, "func_vhca_id 0x%x, npages %d, err %d\n", + func_vhca_id, npages, err); goto out_dropped; } - func_type = func_id_to_type(dev, func_id, ec_function); - dev->priv.page_counters[func_type] += npages; - dev->priv.fw_pages += npages; - - mlx5_core_dbg(dev, "npages %d, ec_function %d, func_id 0x%x, err %d\n", - npages, ec_function, func_id, err); + mlx5_core_dbg(dev, + "npages %d, ec_function %d, func 0x%x, mode %d, err %d\n", + npages, ec_function, func_vhca_id, + mlx5_page_mgt_mode_is_vhca_id(dev), err); kvfree(in); return 0; @@ -428,18 +480,17 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, out_free: kvfree(in); if (notify_fail) - page_notify_fail(dev, func_id, ec_function); + page_notify_fail(dev, func_vhca_id, ec_function); return err; } -static void release_all_pages(struct mlx5_core_dev *dev, u16 func_id, +static void release_all_pages(struct mlx5_core_dev *dev, u16 func_vhca_id, bool ec_function) { - u32 function = get_function(func_id, ec_function); + u32 function = get_function_key(dev, func_vhca_id, ec_function); struct rb_root *root; struct rb_node *p; int npages = 0; - u16 func_type; root = xa_load(&dev->priv.page_root_xa, function); if (WARN_ON_ONCE(!root)) @@ -448,18 +499,20 @@ static void release_all_pages(struct mlx5_core_dev *dev, u16 func_id, p = rb_first(root); while (p) { struct fw_page *fwp = rb_entry(p, struct fw_page, rb_node); + int used = MLX5_NUM_4K_IN_PAGE - fwp->free_count; p = rb_next(p); - npages += (MLX5_NUM_4K_IN_PAGE - fwp->free_count); + npages += used; + if (fwp->func_type != MLX5_FUNC_TYPE_NONE) + dev->priv.page_counters[fwp->func_type] -= used; free_fwp(dev, fwp, fwp->free_count); } - func_type = func_id_to_type(dev, func_id, ec_function); - dev->priv.page_counters[func_type] -= npages; dev->priv.fw_pages -= npages; - mlx5_core_dbg(dev, "npages %d, ec_function %d, func_id 0x%x\n", - npages, ec_function, func_id); + mlx5_core_dbg(dev, "npages %d, ec_function %d, func 0x%x, mode %d\n", + npages, ec_function, func_vhca_id, + mlx5_page_mgt_mode_is_vhca_id(dev)); } static u32 fwp_fill_manage_pages_out(struct fw_page *fwp, u32 *out, u32 index, @@ -487,7 +540,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, struct fw_page *fwp; struct rb_node *p; bool ec_function; - u32 func_id; + u32 func_vhca_id; u32 npages; u32 i = 0; int err; @@ -499,10 +552,11 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, /* No hard feelings, we want our pages back! */ npages = MLX5_GET(manage_pages_in, in, input_num_entries); - func_id = MLX5_GET(manage_pages_in, in, function_id); + func_vhca_id = MLX5_GET(manage_pages_in, in, function_id); ec_function = MLX5_GET(manage_pages_in, in, embedded_cpu_function); - root = xa_load(&dev->priv.page_root_xa, get_function(func_id, ec_function)); + root = xa_load(&dev->priv.page_root_xa, + get_function_key(dev, func_vhca_id, ec_function)); if (WARN_ON_ONCE(!root)) return -EEXIST; @@ -518,14 +572,14 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, return 0; } -static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, - int *nclaimed, bool event, bool ec_function) +static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_vhca_id, + int npages, int *nclaimed, bool event, + bool ec_function) { - u32 function = get_function(func_id, ec_function); + u32 function = get_function_key(dev, func_vhca_id, ec_function); int outlen = MLX5_ST_SZ_BYTES(manage_pages_out); u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {}; int num_claimed; - u16 func_type; u32 *out; int err; int i; @@ -540,12 +594,16 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES); MLX5_SET(manage_pages_in, in, op_mod, MLX5_PAGES_TAKE); - MLX5_SET(manage_pages_in, in, function_id, func_id); + MLX5_SET(manage_pages_in, in, function_id, func_vhca_id); MLX5_SET(manage_pages_in, in, input_num_entries, npages); - MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function); - mlx5_core_dbg(dev, "func 0x%x, npages %d, outlen %d\n", - func_id, npages, outlen); + if (!mlx5_page_mgt_mode_is_vhca_id(dev)) + MLX5_SET(manage_pages_in, in, embedded_cpu_function, + ec_function); + + mlx5_core_dbg(dev, "func 0x%x, npages %d, outlen %d mode %d\n", + func_vhca_id, npages, outlen, + mlx5_page_mgt_mode_is_vhca_id(dev)); err = reclaim_pages_cmd(dev, in, sizeof(in), out, outlen); if (err) { npages = MLX5_GET(manage_pages_in, in, input_num_entries); @@ -577,10 +635,6 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, if (nclaimed) *nclaimed = num_claimed; - func_type = func_id_to_type(dev, func_id, ec_function); - dev->priv.page_counters[func_type] -= num_claimed; - dev->priv.fw_pages -= num_claimed; - out_free: kvfree(out); return err; @@ -658,30 +712,102 @@ static int req_pages_handler(struct notifier_block *nb, * req->npages (and not min ()). */ req->npages = max_t(s32, npages, MAX_RECLAIM_NPAGES); - req->ec_function = ec_function; + if (!mlx5_page_mgt_mode_is_vhca_id(dev)) + req->ec_function = ec_function; req->release_all = release_all; INIT_WORK(&req->work, pages_work_handler); queue_work(dev->priv.pg_wq, &req->work); return NOTIFY_OK; } +/* + * After set_hca_cap(), the second satisfy_startup_pages(dev, 0) may see + * VHCA_ID mode. If page_root_xa already has the PF entry from the first + * (boot) call under FUNC_ID keys 0 or (ec_function << 16), migrate that + * entry to the device vhca_id key so lookups use VHCA_ID semantics. + */ +static int mlx5_pagealloc_migrate_pf_to_vhca_id(struct mlx5_core_dev *dev) +{ + u32 vhca_id_key, old_key; + struct rb_root *root; + struct fw_page *fwp; + struct rb_node *p; + bool ec_function; + int err; + + if (xa_empty(&dev->priv.page_root_xa)) + return 0; + + vhca_id_key = MLX5_CAP_GEN(dev, vhca_id); + ec_function = mlx5_core_is_ecpf(dev); + + old_key = ec_function ? (1U << 16) : 0; + root = xa_load(&dev->priv.page_root_xa, old_key); + if (!root) + return 0; + + if (old_key == vhca_id_key) + return 0; + + err = xa_insert(&dev->priv.page_root_xa, vhca_id_key, root, GFP_KERNEL); + if (err) { + mlx5_core_warn(dev, + "failed to migrate page root key 0x%x to vhca_id 0x%x\n", + old_key, vhca_id_key); + return err; + } + + for (p = rb_first(root); p; p = rb_next(p)) { + fwp = rb_entry(p, struct fw_page, rb_node); + fwp->function = vhca_id_key; + } + + xa_erase(&dev->priv.page_root_xa, old_key); + + return 0; +} + int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot) { - u16 func_id; + bool ec_function = false; + u16 func_vhca_id; s32 npages; int err; - err = mlx5_cmd_query_pages(dev, &func_id, &npages, boot); + /* Boot pages are requested before set_hca_cap(), so the capability + * is not negotiated yet; use FUNC_ID mode for backward compatibility. + * Init pages are requested after set_hca_cap(), which unconditionally + * enables CAP_GEN_MAX. Current caps are not re-queried at this point, + * so check CAP_GEN_MAX directly. + */ + if (boot) { + mlx5_page_mgt_mode_set(dev, MLX5_PAGE_MGT_MODE_FUNC_ID); + } else { + if (MLX5_CAP_GEN_MAX(dev, icm_mng_function_id_mode) == + MLX5_ID_MODE_FUNCTION_VHCA_ID) { + err = mlx5_pagealloc_migrate_pf_to_vhca_id(dev); + if (err) + return err; + mlx5_page_mgt_mode_set(dev, MLX5_PAGE_MGT_MODE_VHCA_ID); + } + } + + err = mlx5_cmd_query_pages(dev, &func_vhca_id, &npages, boot); if (err) return err; - mlx5_core_dbg(dev, "requested %d %s pages for func_id 0x%x\n", - npages, boot ? "boot" : "init", func_id); + mlx5_core_dbg(dev, + "requested %d %s pages for func_vhca_id 0x%x\n", + npages, boot ? "boot" : "init", func_vhca_id); if (!npages) return 0; - return give_pages(dev, func_id, npages, 0, mlx5_core_is_ecpf(dev)); + /* In VHCA_ID mode, ec_function remains false (not used). */ + if (!mlx5_page_mgt_mode_is_vhca_id(dev)) + ec_function = mlx5_core_is_ecpf(dev); + + return give_pages(dev, func_vhca_id, npages, 0, ec_function); } enum { @@ -709,15 +835,17 @@ static int mlx5_reclaim_root_pages(struct mlx5_core_dev *dev, while (!RB_EMPTY_ROOT(root)) { u32 ec_function = mlx5_get_ec_function(function); - u32 function_id = mlx5_get_func_id(function); + u32 func_vhca_id = mlx5_get_func_vhca_id(function); int nclaimed; int err; - err = reclaim_pages(dev, function_id, optimal_reclaimed_pages(), + err = reclaim_pages(dev, func_vhca_id, + optimal_reclaimed_pages(), &nclaimed, false, ec_function); if (err) { - mlx5_core_warn(dev, "reclaim_pages err (%d) func_id=0x%x ec_func=0x%x\n", - err, function_id, ec_function); + mlx5_core_warn(dev, + "reclaim_pages err (%d) func_vhca_id=0x%x ec_func=0x%x\n", + err, func_vhca_id, ec_function); return err; } @@ -757,6 +885,9 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev) WARN(dev->priv.page_counters[MLX5_HOST_PF], "External host PF FW pages counter is %d after reclaiming all pages\n", dev->priv.page_counters[MLX5_HOST_PF]); + WARN(dev->priv.page_counters[MLX5_SPF], + "SPFs FW pages counter is %d after reclaiming all pages\n", + dev->priv.page_counters[MLX5_SPF]); WARN(dev->priv.page_counters[MLX5_EC_VF], "EC VFs FW pages counter is %d after reclaiming all pages\n", dev->priv.page_counters[MLX5_EC_VF]); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c index e051b9a939ee..0f5b8bc7861e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c @@ -87,9 +87,8 @@ int mlx5_set_msix_vec_count(struct mlx5_core_dev *dev, int function_id, int msix_vec_count) { int query_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out); - int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in); - void *hca_cap = NULL, *query_cap = NULL, *cap; int num_vf_msix, min_msix, max_msix; + void *query_cap, *hca_caps; bool ec_vf_function; int vport; int ret; @@ -111,11 +110,8 @@ int mlx5_set_msix_vec_count(struct mlx5_core_dev *dev, int function_id, return -EOVERFLOW; query_cap = kvzalloc(query_sz, GFP_KERNEL); - hca_cap = kvzalloc(set_sz, GFP_KERNEL); - if (!hca_cap || !query_cap) { - ret = -ENOMEM; - goto out; - } + if (!query_cap) + return -ENOMEM; ec_vf_function = mlx5_core_ec_sriov_enabled(dev); vport = mlx5_core_func_to_vport(dev, function_id, ec_vf_function); @@ -123,21 +119,12 @@ int mlx5_set_msix_vec_count(struct mlx5_core_dev *dev, int function_id, if (ret) goto out; - cap = MLX5_ADDR_OF(set_hca_cap_in, hca_cap, capability); - memcpy(cap, MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability), - MLX5_UN_SZ_BYTES(hca_cap_union)); - MLX5_SET(cmd_hca_cap, cap, dynamic_msix_table_size, msix_vec_count); + hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_cap, capability); + MLX5_SET(cmd_hca_cap, hca_caps, dynamic_msix_table_size, + msix_vec_count); - MLX5_SET(set_hca_cap_in, hca_cap, opcode, MLX5_CMD_OP_SET_HCA_CAP); - MLX5_SET(set_hca_cap_in, hca_cap, other_function, 1); - MLX5_SET(set_hca_cap_in, hca_cap, ec_vf_function, ec_vf_function); - MLX5_SET(set_hca_cap_in, hca_cap, function_id, function_id); - - MLX5_SET(set_hca_cap_in, hca_cap, op_mod, - MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1); - ret = mlx5_cmd_exec_in(dev, set_hca_cap, hca_cap); + ret = mlx5_vport_set_other_func_general_cap(dev, hca_caps, vport); out: - kvfree(hca_cap); kvfree(query_cap); return ret; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c index 8503e532f423..b6cecbcc392d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c @@ -245,8 +245,10 @@ static int mlx5_sf_add(struct mlx5_core_dev *dev, struct mlx5_sf_table *table, if (IS_ERR(sf)) return PTR_ERR(sf); + mlx5_esw_reps_block(esw); err = mlx5_eswitch_load_sf_vport(esw, sf->hw_fn_id, MLX5_VPORT_UC_ADDR_CHANGE, &sf->dl_port, new_attr->controller, new_attr->sfnum); + mlx5_esw_reps_unblock(esw); if (err) goto esw_err; *dl_port = &sf->dl_port.dl_port; @@ -263,6 +265,8 @@ static int mlx5_sf_new_check_attr(struct mlx5_core_dev *dev, const struct devlink_port_new_attrs *new_attr, struct netlink_ext_ack *extack) { + u32 controller; + if (new_attr->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) { NL_SET_ERR_MSG_MOD(extack, "Driver supports only SF port addition"); return -EOPNOTSUPP; @@ -282,7 +286,9 @@ mlx5_sf_new_check_attr(struct mlx5_core_dev *dev, const struct devlink_port_new_ NL_SET_ERR_MSG_MOD(extack, "External controller is unsupported"); return -EOPNOTSUPP; } - if (new_attr->pfnum != PCI_FUNC(dev->pdev->devfn)) { + controller = new_attr->controller_valid ? new_attr->controller : 0; + if (new_attr->pfnum != + mlx5_esw_sf_controller_to_pfnum(dev, controller)) { NL_SET_ERR_MSG_MOD(extack, "Invalid pfnum supplied"); return -EOPNOTSUPP; } @@ -304,10 +310,6 @@ int mlx5_devlink_sf_port_new(struct devlink *devlink, struct mlx5_sf_table *table = dev->priv.sf_table; int err; - err = mlx5_sf_new_check_attr(dev, new_attr, extack); - if (err) - return err; - if (!mlx5_sf_table_supported(dev)) { NL_SET_ERR_MSG_MOD(extack, "SF ports are not supported."); return -EOPNOTSUPP; @@ -319,6 +321,10 @@ int mlx5_devlink_sf_port_new(struct devlink *devlink, return -EOPNOTSUPP; } + err = mlx5_sf_new_check_attr(dev, new_attr, extack); + if (err) + return err; + return mlx5_sf_add(dev, table, new_attr, extack, dl_port); } @@ -367,7 +373,10 @@ int mlx5_devlink_sf_port_del(struct devlink *devlink, struct mlx5_sf_table *table = dev->priv.sf_table; struct mlx5_sf *sf = mlx5_sf_by_dl_port(dl_port); + mlx5_esw_reps_block(dev->priv.eswitch); mlx5_sf_del(table, sf); + mlx5_esw_reps_unblock(dev->priv.eswitch); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c index 049dfd431618..0bc9146a3598 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c @@ -21,25 +21,33 @@ struct mlx5_sf_hwc_table { struct mlx5_sf_hw *sfs; int max_fn; u16 start_fn_id; + u32 controller; }; -enum mlx5_sf_hwc_index { +enum { MLX5_SF_HWC_LOCAL, - MLX5_SF_HWC_EXTERNAL, - MLX5_SF_HWC_MAX, + MLX5_SF_HWC_EXT_HOST, + MLX5_SF_HWC_FIRST_SPF, }; struct mlx5_sf_hw_table { struct mutex table_lock; /* Serializes sf deletion and vhca state change handler. */ - struct mlx5_sf_hwc_table hwc[MLX5_SF_HWC_MAX]; + struct mlx5_sf_hwc_table *hwc; + int num_hwc; }; static struct mlx5_sf_hwc_table * mlx5_sf_controller_to_hwc(struct mlx5_core_dev *dev, u32 controller) { - int idx = !!controller; + struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + int i; - return &dev->priv.sf_hw_table->hwc[idx]; + for (i = MLX5_SF_HWC_FIRST_SPF; i < table->num_hwc; i++) { + if (table->hwc[i].controller == controller) + return &table->hwc[i]; + } + + return &table->hwc[!!controller]; } u16 mlx5_sf_sw_to_hw_id(struct mlx5_core_dev *dev, u32 controller, u16 sw_id) @@ -60,7 +68,7 @@ mlx5_sf_table_fn_to_hwc(struct mlx5_sf_hw_table *table, u16 fn_id) { int i; - for (i = 0; i < ARRAY_SIZE(table->hwc); i++) { + for (i = 0; i < table->num_hwc; i++) { if (table->hwc[i].max_fn && fn_id >= table->hwc[i].start_fn_id && fn_id < (table->hwc[i].start_fn_id + table->hwc[i].max_fn)) @@ -221,9 +229,10 @@ static void mlx5_sf_hw_table_hwc_dealloc_all(struct mlx5_core_dev *dev, static void mlx5_sf_hw_table_dealloc_all(struct mlx5_core_dev *dev, struct mlx5_sf_hw_table *table) { - mlx5_sf_hw_table_hwc_dealloc_all(dev, - &table->hwc[MLX5_SF_HWC_EXTERNAL]); - mlx5_sf_hw_table_hwc_dealloc_all(dev, &table->hwc[MLX5_SF_HWC_LOCAL]); + int i; + + for (i = 0; i < table->num_hwc; i++) + mlx5_sf_hw_table_hwc_dealloc_all(dev, &table->hwc[i]); } static int mlx5_sf_hw_table_hwc_init(struct mlx5_sf_hwc_table *hwc, u16 max_fn, u16 base_id) @@ -277,11 +286,13 @@ static int mlx5_sf_hw_table_res_register(struct mlx5_core_dev *dev, u16 max_fn, int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) { struct mlx5_sf_hw_table *table; + int num_spfs, num_hwc; u16 max_ext_fn = 0; u16 ext_base_id = 0; u16 base_id; u16 max_fn; int err; + int i; if (!mlx5_vhca_event_supported(dev)) return 0; @@ -295,7 +306,7 @@ int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) if (mlx5_sf_hw_table_res_register(dev, max_fn, max_ext_fn)) mlx5_core_dbg(dev, "failed to register max SFs resources"); - if (!max_fn && !max_ext_fn) + if (!max_fn && !max_ext_fn && !mlx5_esw_has_spf_sfs(dev)) return 0; table = kzalloc_obj(*table); @@ -304,26 +315,62 @@ int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) goto alloc_err; } + num_spfs = mlx5_esw_get_num_spfs(dev); + num_hwc = MLX5_SF_HWC_FIRST_SPF + num_spfs; + table->hwc = kcalloc(num_hwc, sizeof(*table->hwc), GFP_KERNEL); + if (!table->hwc) { + err = -ENOMEM; + goto hwc_alloc_err; + } + table->num_hwc = num_hwc; + mutex_init(&table->table_lock); dev->priv.sf_hw_table = table; + table->hwc[MLX5_SF_HWC_LOCAL].controller = 0; base_id = mlx5_sf_start_function_id(dev); err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_LOCAL], max_fn, base_id); if (err) - goto table_err; + goto hwc_init_err; - err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_EXTERNAL], + table->hwc[MLX5_SF_HWC_EXT_HOST].controller = + mlx5_esw_get_hpf_host_number(dev) + 1; + err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_EXT_HOST], max_ext_fn, ext_base_id); if (err) - goto ext_err; + goto hwc_init_err; - mlx5_core_dbg(dev, "SF HW table: max sfs = %d, ext sfs = %d\n", max_fn, max_ext_fn); + for (i = 0; i < num_spfs; i++) { + u16 spf_max_sfs, spf_base_id, host_number; + int hwc_idx = MLX5_SF_HWC_FIRST_SPF + i; + + err = mlx5_esw_spf_get_host_number(dev, i, &host_number); + if (err) + goto hwc_init_err; + + err = mlx5_esw_sf_max_spf_functions(dev, i, &spf_max_sfs, + &spf_base_id); + if (err) + goto hwc_init_err; + + table->hwc[hwc_idx].controller = host_number + 1; + err = mlx5_sf_hw_table_hwc_init(&table->hwc[hwc_idx], + spf_max_sfs, spf_base_id); + if (err) + goto hwc_init_err; + } + + mlx5_core_dbg(dev, "SF HW table: max sfs = %d, ext sfs = %d, num spfs = %d\n", + max_fn, max_ext_fn, num_spfs); return 0; -ext_err: - mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]); -table_err: +hwc_init_err: + dev->priv.sf_hw_table = NULL; + for (i = 0; i < num_hwc; i++) + mlx5_sf_hw_table_hwc_cleanup(&table->hwc[i]); mutex_destroy(&table->table_lock); + kfree(table->hwc); +hwc_alloc_err: kfree(table); alloc_err: mlx5_sf_hw_table_res_unregister(dev); @@ -333,13 +380,15 @@ int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev) { struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table; + int i; if (!table) goto res_unregister; - mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_EXTERNAL]); - mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]); + for (i = 0; i < table->num_hwc; i++) + mlx5_sf_hw_table_hwc_cleanup(&table->hwc[i]); mutex_destroy(&table->table_lock); + kfree(table->hwc); kfree(table); dev->priv.sf_hw_table = NULL; res_unregister: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h index 4fc870140d71..487c94b56203 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h @@ -4,14 +4,6 @@ #ifndef __MLX5_IFC_VHCA_EVENT_H__ #define __MLX5_IFC_VHCA_EVENT_H__ -enum mlx5_ifc_vhca_state { - MLX5_VHCA_STATE_INVALID = 0x0, - MLX5_VHCA_STATE_ALLOCATED = 0x1, - MLX5_VHCA_STATE_ACTIVE = 0x2, - MLX5_VHCA_STATE_IN_USE = 0x3, - MLX5_VHCA_STATE_TEARDOWN_REQUEST = 0x4, -}; - struct mlx5_ifc_vhca_state_context_bits { u8 arm_change_event[0x1]; u8 reserved_at_1[0xb]; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index bf6f631cf2ce..0770b5d99c5d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -273,6 +273,7 @@ void mlx5_sriov_detach(struct mlx5_core_dev *dev) static u16 mlx5_get_max_vfs(struct mlx5_core_dev *dev) { + struct mlx5_esw_pf_info host_pf_info; u16 host_total_vfs; const u32 *out; @@ -284,8 +285,8 @@ static u16 mlx5_get_max_vfs(struct mlx5_core_dev *dev) */ if (IS_ERR(out)) goto done; - host_total_vfs = MLX5_GET(query_esw_functions_out, out, - host_params_context.host_total_vfs); + host_pf_info = mlx5_esw_get_host_pf_info(dev, out); + host_total_vfs = host_pf_info.total_vfs; kvfree(out); return host_total_vfs; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c index 6dcd9c2a78aa..eae02bc74221 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c @@ -422,6 +422,18 @@ int mlx5hws_bwc_queue_poll(struct mlx5hws_context *ctx, if (!got_comp && !drain) return 0; + if (unlikely(ctx->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)) { + /* If the device is down for any reason (e.g. FLR), the HW will + * no longer generate completions. + * Note that ETIMEDOUT is returned here because the BWC layer + * already has a special handling for timeouts - it breaks the + * rehash / resize / shrink loops to avoid chain of timeouts. + */ + mlx5_core_warn_once(ctx->mdev, + "BWC poll: device is down, polling for completion aborted\n"); + return -ETIMEDOUT; + } + queue_full = mlx5hws_send_engine_full(&ctx->send_queue[queue_id]); while (queue_full || ((got_comp || drain) && *pending_rules)) { ret = mlx5hws_send_queue_poll(ctx, queue_id, comp, burst_th); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h index b92d55b2d147..20cdacd8f12e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.h @@ -116,5 +116,5 @@ static inline const struct mlx5_flow_cmds *mlx5_fs_cmd_get_hws_cmds(void) return NULL; } -#endif /* CONFIG_MLX5_HWS_STEERING */ +#endif /* CONFIG_MLX5_HW_STEERING */ #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c index bd292485a25b..dd7927983ab2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c @@ -282,6 +282,9 @@ int mlx5hws_table_destroy(struct mlx5hws_table *tbl) goto unlock_err; } + if (tbl->default_miss.miss_tbl) + list_del_init(&tbl->default_miss.next); + list_del_init(&tbl->tbl_list_node); mutex_unlock(&ctx->ctrl_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/vport.c index d8e382b9fa61..6dc3b11b7926 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/vport.c @@ -50,7 +50,7 @@ static int hws_vport_add_gvmi(struct mlx5hws_context *ctx, u16 vport) static bool hws_vport_is_esw_mgr_vport(struct mlx5hws_context *ctx, u16 vport) { return ctx->caps->is_ecpf ? vport == MLX5_VPORT_ECPF : - vport == MLX5_VPORT_PF; + vport == MLX5_VPORT_HOST_PF; } int mlx5hws_vport_get_gvmi(struct mlx5hws_context *ctx, u16 vport, u16 *vport_gvmi) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c index 7a0a15822392..fa4d24b3dfaa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c @@ -239,7 +239,7 @@ static int dr_icm_buddy_init_ste_cache(struct mlx5dr_icm_buddy_mem *buddy) if (!buddy->hw_ste_arr) goto free_ste_arr; - buddy->miss_list = kvmalloc(num_of_entries * sizeof(struct list_head), GFP_KERNEL); + buddy->miss_list = kvmalloc_array(num_of_entries, sizeof(struct list_head), GFP_KERNEL); if (!buddy->miss_list) goto free_hw_ste_arr; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_types.h index cc328292bf84..e0344707f522 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_types.h @@ -986,7 +986,6 @@ struct mlx5dr_matcher_rx_tx { [DR_RULE_MAX_STES]; u8 num_of_builders; u8 num_of_builders_arr[DR_RULE_IPV_MAX][DR_RULE_IPV_MAX]; - u64 default_icm_addr; struct mlx5dr_table_rx_tx *nic_tbl; u32 prio; struct list_head list_node; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index d63b0e8806b5..3676e26ac6b0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -1319,8 +1319,8 @@ void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf, buf[(*len)++] = MLX5_CAP_GEN_2(mdev, load_balance_id); } -static bool mlx5_vport_use_vhca_id_as_func_id(struct mlx5_core_dev *dev, - u16 vport_num, u16 *vhca_id) +bool mlx5_vport_use_vhca_id_as_func_id(struct mlx5_core_dev *dev, + u16 vport_num, u16 *vhca_id) { if (!MLX5_CAP_GEN_2(dev, function_id_type_vhca_id)) return false; @@ -1372,7 +1372,7 @@ int mlx5_vport_get_vhca_id(struct mlx5_core_dev *dev, u16 vport, u16 *vhca_id) if (mlx5_esw_vport_vhca_id(dev->priv.eswitch, vport, vhca_id)) return 0; - query_ctx = kzalloc(query_out_sz, GFP_KERNEL); + query_ctx = kvzalloc(query_out_sz, GFP_KERNEL); if (!query_ctx) return -ENOMEM; @@ -1384,7 +1384,7 @@ int mlx5_vport_get_vhca_id(struct mlx5_core_dev *dev, u16 vport, u16 *vhca_id) *vhca_id = MLX5_GET(cmd_hca_cap, hca_caps, vhca_id); out_free: - kfree(query_ctx); + kvfree(query_ctx); return err; } EXPORT_SYMBOL_GPL(mlx5_vport_get_vhca_id); @@ -1399,7 +1399,7 @@ int mlx5_vport_set_other_func_cap(struct mlx5_core_dev *dev, const void *hca_cap void *set_ctx; int ret; - set_ctx = kzalloc(set_sz, GFP_KERNEL); + set_ctx = kvzalloc(set_sz, GFP_KERNEL); if (!set_ctx) return -ENOMEM; @@ -1428,6 +1428,6 @@ int mlx5_vport_set_other_func_cap(struct mlx5_core_dev *dev, const void *hca_cap MLX5_SET(set_hca_cap_in, set_ctx, function_id, function_id); ret = mlx5_cmd_exec_in(dev, set_hca_cap, set_ctx); - kfree(set_ctx); + kvfree(set_ctx); return ret; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index d76246301f67..308d8a94865f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -1306,11 +1306,11 @@ static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core, } static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { - if (val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER && - val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) { + if (val->vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER && + val->vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) { NL_SET_ERR_MSG_MOD(extack, "'fw_load_policy' must be 'driver' or 'flash'"); return -EINVAL; } @@ -1337,7 +1337,7 @@ static int mlxsw_core_fw_params_register(struct mlxsw_core *mlxsw_core) value.vu8 = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY, - value); + &value); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 512933dbab9a..1fee57054b20 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -713,7 +713,7 @@ static struct mlxsw_driver mlxsw_m_driver = { }; static const struct i2c_device_id mlxsw_m_i2c_id[] = { - { "mlxsw_minimal" }, + { .name = "mlxsw_minimal" }, { } }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 3bb89045eaf5..01fcbd1c8d01 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5651,6 +5651,14 @@ MLXSW_ITEM64(reg, ppcnt, tx_pause_duration, MLXSW_ITEM64(reg, ppcnt, tx_pause_transition, MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x70, 0, 64); +/* reg_ppcnt_rx_discards + * Per-PG (ingress priority group) drop count. + * index slot is the PG, switch priority. + * Access: RO + */ +MLXSW_ITEM64(reg, ppcnt, rx_discards, + MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x78, 0, 64); + /* Ethernet Per Traffic Class Counters */ /* reg_ppcnt_tc_transmit_queue diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3a65420fa1ad..82569162d2e5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4360,7 +4360,7 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, lag_id = lag->lag_id; err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index); if (err) - return err; + goto err_lag_uppers_bridge_join; err = mlxsw_sp_lag_uppers_bridge_join(mlxsw_sp_port, lag_dev, extack); @@ -5277,8 +5277,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *nb, } static const struct pci_device_id mlxsw_sp1_pci_id_table[] = { - {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0}, - {0, }, + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM) }, + { }, }; static struct pci_driver mlxsw_sp1_pci_driver = { @@ -5287,8 +5287,8 @@ static struct pci_driver mlxsw_sp1_pci_driver = { }; static const struct pci_device_id mlxsw_sp2_pci_id_table[] = { - {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM2), 0}, - {0, }, + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM2) }, + { }, }; static struct pci_driver mlxsw_sp2_pci_driver = { @@ -5297,8 +5297,8 @@ static struct pci_driver mlxsw_sp2_pci_driver = { }; static const struct pci_device_id mlxsw_sp3_pci_id_table[] = { - {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM3), 0}, - {0, }, + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM3) }, + { }, }; static struct pci_driver mlxsw_sp3_pci_driver = { @@ -5307,8 +5307,8 @@ static struct pci_driver mlxsw_sp3_pci_driver = { }; static const struct pci_device_id mlxsw_sp4_pci_id_table[] = { - {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM4), 0}, - {0, }, + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM4) }, + { }, }; static struct pci_driver mlxsw_sp4_pci_driver = { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 0a8fb9c842d3..7f78b1ef61cc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -542,6 +542,10 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = { .str = "tx_pause_duration_prio", .getter = mlxsw_reg_ppcnt_tx_pause_duration_get, }, + { + .str = "rx_discards_pg", + .getter = mlxsw_reg_ppcnt_rx_discards_get, + }, }; #define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c index 4f921bbc1e77..8d6cea43be61 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c @@ -1021,6 +1021,7 @@ mlxsw_sp_fid_port_vid_list_add(struct mlxsw_sp_fid *fid, u16 local_port, u16 vid) { struct mlxsw_sp_fid_port_vid *port_vid, *tmp_port_vid; + struct list_head *insert_before = &fid->port_vid_list; port_vid = kzalloc_obj(*port_vid); if (!port_vid) @@ -1030,11 +1031,13 @@ mlxsw_sp_fid_port_vid_list_add(struct mlxsw_sp_fid *fid, u16 local_port, port_vid->vid = vid; list_for_each_entry(tmp_port_vid, &fid->port_vid_list, list) { - if (tmp_port_vid->local_port > local_port) + if (tmp_port_vid->local_port > local_port) { + insert_before = &tmp_port_vid->list; break; + } } - list_add_tail(&port_vid->list, &tmp_port_vid->list); + list_add_tail(&port_vid->list, insert_before); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 7bd87d0547d8..3d6fdbab05e0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1018,6 +1018,7 @@ static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, err_tree_replace: for (i--; i >= 0; i--) { + vr = &mlxsw_sp->router->vrs[i]; if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id)) continue; mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h index 81794bd326e1..64b958df7774 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h @@ -805,6 +805,7 @@ enum { #define FBNIC_CSR_END_PCS 0x10668 /* CSR section delimiter */ #define FBNIC_CSR_START_RSFEC 0x10800 /* CSR section delimiter */ +#define FBNIC_RSFEC_CONTROL(n) (0x10800 + 8 * (n)) /* 0x42000 + 32*n */ /* We have 4 RSFEC engines present in our part, however we are only using 1. * As such only CCW(0) and NCCW(0) will never be non-zero and the other diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c index f14de2366854..cb34fc166ef9 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c @@ -2020,6 +2020,11 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | ETHTOOL_RING_USE_HDS_THRS, .rxfh_max_num_contexts = FBNIC_RPC_RSS_TBL_COUNT, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS | + ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM | + ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM | + ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM, .get_drvinfo = fbnic_get_drvinfo, .get_regs_len = fbnic_get_regs_len, .get_regs = fbnic_get_regs, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c index 709041f7fc43..fe3a4ce88413 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c @@ -7,10 +7,49 @@ #include "fbnic.h" #include "fbnic_netdev.h" +/* fbnic MDIO Interface Layout + * + * +-------------------+ + * | MAC | + * +-------------------+ + * | | | | <-- 25GMII, 50GMII, or CGMII + * +-------------------+ + * MMD 3 | PCS | + * +-------------------+ + * | FEC | + * +-------------------+ + * MMD 8 | Separated PMA | + * +-------------------+ + * | | <-- PMD Service Interface + * +-------------------+ + * MMD 1 | PMD | + * +-------------------+ + */ + #define DW_VENDOR BIT(15) #define FBNIC_PCS_VENDOR BIT(9) #define FBNIC_PCS_ZERO_MASK (DW_VENDOR - FBNIC_PCS_VENDOR) +static int +fbnic_mdio_ids(int id, int regnum) +{ + /* return correct IDs */ + switch (regnum) { + case MDIO_DEVID1: + return id >> 16; + case MDIO_DEVID2: + return id & 0xffff; + case MDIO_DEVS1: + return MDIO_DEVS_SEP_PMA1 | MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; + case MDIO_DEVS2: + return 0; + case MDIO_STAT2: + return MDIO_STAT2_DEVPRST_VAL; + } + + return 0; +} + static int fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) { @@ -29,18 +68,6 @@ fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) } switch (regnum) { - case MDIO_DEVID1: - ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16; - break; - case MDIO_DEVID2: - ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff; - break; - case MDIO_DEVS1: - ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; - break; - case MDIO_STAT2: - ret = MDIO_STAT2_DEVPRST_VAL; - break; case MDIO_PMA_RXDET: /* If training isn't complete default to 0 */ if (fbd->pmd_state != FBNIC_PMD_SEND_DATA) @@ -51,6 +78,7 @@ fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2)); break; default: + ret = fbnic_mdio_ids(MP_FBNIC_XPCS_PMA_100G_ID, regnum); break; } @@ -64,7 +92,7 @@ fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) static int fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) { - int ret, offset = 0; + int ret, offset = 0, overrides = 0; /* We will need access to both PCS instances to get config info */ if (addr >= 2) @@ -75,18 +103,25 @@ fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) return 0; /* Intercept and return correct ID for PCS */ - if (regnum == MDIO_DEVID1) - return DW_XPCS_ID >> 16; - if (regnum == MDIO_DEVID2) - return DW_XPCS_ID & 0xffff; - if (regnum == MDIO_DEVS1) - return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; + switch (regnum) { + case MDIO_DEVID1 ... MDIO_DEVID2: + ret = fbnic_mdio_ids(DW_XPCS_ID, regnum); + break; + case MDIO_DEVS1: + /* DW IP returns MDIO_DEVS_SEP_PMA1, MDIO_DEVS_PMAPMD, + * and MDIO_DEVS_PCS as 0 + */ + overrides = fbnic_mdio_ids(DW_XPCS_ID, regnum); + fallthrough; + default: + /* Swap vendor page bit for FBNIC PCS vendor page bit */ + if (regnum & DW_VENDOR) + offset ^= DW_VENDOR | FBNIC_PCS_VENDOR; - /* Swap vendor page bit for FBNIC PCS vendor page bit */ - if (regnum & DW_VENDOR) - offset ^= DW_VENDOR | FBNIC_PCS_VENDOR; - - ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset)); + ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset)); + ret |= overrides; + break; + } dev_dbg(fbd->dev, "SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n", @@ -95,6 +130,32 @@ fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) return ret; } +static int +fbnic_mdio_read_pma(struct fbnic_dev *fbd, int addr, int regnum) +{ + int ret = 0; + + /* We will need access to both PMA instances to get config info */ + if (addr >= 2) + return 0; + + switch (regnum) { + case MDIO_PMA_RSFEC_CTRL ... MDIO_PMA_RSFEC_LANE_MAP: + ret = fbnic_rd32(fbd, FBNIC_RSFEC_CONTROL(addr) + + regnum - MDIO_PMA_RSFEC_CTRL); + break; + default: + ret = fbnic_mdio_ids(MP_FBNIC_XPCS_PMA_100G_ID, regnum); + break; + } + + dev_dbg(fbd->dev, + "SWMII PMA Rd: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, ret); + + return ret; +} + static int fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) { @@ -106,6 +167,9 @@ fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) if (devnum == MDIO_MMD_PCS) return fbnic_mdio_read_pcs(fbd, addr, regnum); + if (devnum == MDIO_MMD_SEP_PMA1) + return fbnic_mdio_read_pma(fbd, addr, regnum); + return 0; } @@ -125,7 +189,7 @@ fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val) addr, regnum, val); /* Allow access to both halves of PCS for 50R2 config */ - if (addr > 2) + if (addr >= 2) return; /* Skip write for reserved registers */ @@ -139,6 +203,26 @@ fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val) fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val); } +static void +fbnic_mdio_write_pma(struct fbnic_dev *fbd, int addr, int regnum, u16 val) +{ + dev_dbg(fbd->dev, + "SWMII PMA Wr: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, val); + + if (addr >= 2) + return; + + switch (regnum) { + case MDIO_PMA_RSFEC_CTRL ... MDIO_PMA_RSFEC_LANE_MAP: + fbnic_wr32(fbd, FBNIC_RSFEC_CONTROL(addr) + + regnum - MDIO_PMA_RSFEC_CTRL, val); + break; + default: + break; + } +} + static int fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val) @@ -151,6 +235,9 @@ fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, if (devnum == MDIO_MMD_PCS) fbnic_mdio_write_pcs(fbd, addr, regnum, val); + if (devnum == MDIO_MMD_SEP_PMA1) + fbnic_mdio_write_pma(fbd, addr, regnum, val); + return 0; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index 4dea2bb58d2f..dd77ab6052c8 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -240,9 +240,9 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd, fbnic_write_tce_tcam(fbd); } -static void fbnic_set_rx_mode(struct net_device *netdev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int fbnic_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; @@ -250,6 +250,8 @@ static void fbnic_set_rx_mode(struct net_device *netdev, /* No need to update the hardware if we are not running */ if (netif_running(netdev)) __fbnic_set_rx_mode(fbd, uc, mc); + + return 0; } static int fbnic_set_mac(struct net_device *netdev, void *p) @@ -752,7 +754,7 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netdev->netdev_ops = &fbnic_netdev_ops; netdev->stat_ops = &fbnic_stat_ops; netdev->queue_mgmt_ops = &fbnic_queue_mgmt_ops; - netdev->netmem_tx = true; + netdev->netmem_tx = NETMEM_TX_DMA; fbnic_set_ethtool_ops(netdev); diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 26557cd32008..773fdd215a15 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -6839,11 +6839,9 @@ static int __maybe_unused pcidev_suspend(struct device *dev_d) static char pcidev_name[] = "ksz884xp"; static const struct pci_device_id pcidev_table[] = { - { PCI_VENDOR_ID_MICREL_KS, 0x8841, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_MICREL_KS, 0x8842, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE(MICREL_KS, 0x8841) }, + { PCI_VDEVICE(MICREL_KS, 0x8842) }, + { } }; MODULE_DEVICE_TABLE(pci, pcidev_table); diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index ffac22883e49..1cdce35e1423 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -108,9 +108,9 @@ static int lan743x_pci_init(struct lan743x_adapter *adapter, if (ret) goto return_error; - netif_info(adapter, probe, adapter->netdev, - "PCI: Vendor ID = 0x%04X, Device ID = 0x%04X\n", - pdev->vendor, pdev->device); + dev_dbg(&adapter->pdev->dev, + "PCI: Vendor ID = 0x%04X, Device ID = 0x%04X\n", + pdev->vendor, pdev->device); bars = pci_select_bars(pdev, IORESOURCE_MEM); if (!test_bit(0, &bars)) goto disable_device; @@ -192,10 +192,10 @@ static int lan743x_csr_init(struct lan743x_adapter *adapter) csr->id_rev = lan743x_csr_read(adapter, ID_REV); csr->fpga_rev = lan743x_csr_read(adapter, FPGA_REV); - netif_info(adapter, probe, adapter->netdev, - "ID_REV = 0x%08X, FPGA_REV = %d.%d\n", - csr->id_rev, FPGA_REV_GET_MAJOR_(csr->fpga_rev), - FPGA_REV_GET_MINOR_(csr->fpga_rev)); + dev_dbg(&adapter->pdev->dev, + "ID_REV = 0x%08X, FPGA_REV = %d.%d\n", + csr->id_rev, FPGA_REV_GET_MAJOR_(csr->fpga_rev), + FPGA_REV_GET_MINOR_(csr->fpga_rev)); if (!ID_REV_IS_VALID_CHIP_ID_(csr->id_rev)) return -ENODEV; @@ -953,8 +953,8 @@ int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr) u32 val; if (mmd > 31) { - netif_err(adapter, probe, adapter->netdev, - "%s mmd should <= 31\n", __func__); + dev_err(&adapter->pdev->dev, + "%s mmd should <= 31\n", __func__); return -EINVAL; } @@ -983,8 +983,8 @@ static int lan743x_sgmii_write(struct lan743x_adapter *adapter, int ret; if (mmd > 31) { - netif_err(adapter, probe, adapter->netdev, - "%s mmd should <= 31\n", __func__); + dev_err(&adapter->pdev->dev, + "%s mmd should <= 31\n", __func__); return -EINVAL; } mutex_lock(&adapter->sgmii_rw_lock); @@ -1215,8 +1215,7 @@ static void lan743x_mac_set_address(struct lan743x_adapter *adapter, lan743x_csr_write(adapter, MAC_RX_ADDRH, addr_hi); ether_addr_copy(adapter->mac_address, addr); - netif_info(adapter, drv, adapter->netdev, - "MAC address set to %pM\n", addr); + dev_dbg(&adapter->pdev->dev, "MAC address set to %pM\n", addr); } static void lan743x_mac_rx_enable_fse(struct lan743x_adapter *adapter) @@ -1402,8 +1401,8 @@ static void lan743x_phy_interface_select(struct lan743x_adapter *adapter) else adapter->phy_interface = PHY_INTERFACE_MODE_RGMII; - netif_dbg(adapter, drv, adapter->netdev, - "selected phy interface: 0x%X\n", adapter->phy_interface); + dev_dbg(&adapter->pdev->dev, + "selected phy interface: 0x%X\n", adapter->phy_interface); } static void lan743x_rfe_open(struct lan743x_adapter *adapter) @@ -3200,7 +3199,7 @@ static int lan743x_phylink_create(struct lan743x_adapter *adapter) } adapter->phylink = pl; - netdev_dbg(netdev, "lan743x phylink created"); + dev_dbg(&adapter->pdev->dev, "lan743x phylink created"); return 0; } @@ -3613,30 +3612,26 @@ static int lan743x_mdiobus_init(struct lan743x_adapter *adapter) adapter->mdiobus->priv = (void *)adapter; if (adapter->is_pci11x1x) { if (adapter->is_sgmii_en) { - netif_dbg(adapter, drv, adapter->netdev, - "SGMII operation\n"); + dev_dbg(&adapter->pdev->dev, "SGMII operation\n"); adapter->mdiobus->read = lan743x_mdiobus_read_c22; adapter->mdiobus->write = lan743x_mdiobus_write_c22; adapter->mdiobus->read_c45 = lan743x_mdiobus_read_c45; adapter->mdiobus->write_c45 = lan743x_mdiobus_write_c45; adapter->mdiobus->name = "lan743x-mdiobus-c45"; - netif_dbg(adapter, drv, adapter->netdev, - "lan743x-mdiobus-c45\n"); + dev_dbg(&adapter->pdev->dev, "lan743x-mdiobus-c45\n"); } else { - netif_dbg(adapter, drv, adapter->netdev, - "RGMII operation\n"); + dev_dbg(&adapter->pdev->dev, "RGMII operation\n"); // Only C22 support when RGMII I/F adapter->mdiobus->read = lan743x_mdiobus_read_c22; adapter->mdiobus->write = lan743x_mdiobus_write_c22; adapter->mdiobus->name = "lan743x-mdiobus"; - netif_dbg(adapter, drv, adapter->netdev, - "lan743x-mdiobus\n"); + dev_dbg(&adapter->pdev->dev, "lan743x-mdiobus\n"); } } else { adapter->mdiobus->read = lan743x_mdiobus_read_c22; adapter->mdiobus->write = lan743x_mdiobus_write_c22; adapter->mdiobus->name = "lan743x-mdiobus"; - netif_dbg(adapter, drv, adapter->netdev, "lan743x-mdiobus\n"); + dev_dbg(&adapter->pdev->dev, "lan743x-mdiobus\n"); } snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE, @@ -3728,8 +3723,7 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, ret = lan743x_phylink_create(adapter); if (ret < 0) { - netif_err(adapter, probe, netdev, - "failed to setup phylink (%d)\n", ret); + dev_err(&pdev->dev, "failed to setup phylink (%d)\n", ret); goto cleanup_mdiobus; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index f8ce735a7fc0..41d4ec7f2f57 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -91,7 +91,7 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) pp_params.dma_dir = DMA_BIDIRECTIONAL; rx->page_pool = page_pool_create(&pp_params); - if (unlikely(IS_ERR(rx->page_pool))) + if (IS_ERR(rx->page_pool)) return PTR_ERR(rx->page_pool); for (int i = 0; i < lan966x->num_phys_ports; ++i) { @@ -106,7 +106,7 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) rx->page_pool); } - return PTR_ERR_OR_ZERO(rx->page_pool); + return 0; } static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) @@ -115,8 +115,9 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) struct fdma *fdma = &rx->fdma; int err; - if (lan966x_fdma_rx_alloc_page_pool(rx)) - return PTR_ERR(rx->page_pool); + err = lan966x_fdma_rx_alloc_page_pool(rx); + if (err) + return err; err = fdma_alloc_coherent(lan966x->dev, fdma); if (err) { @@ -815,6 +816,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) struct page *(*old_pages)[FDMA_RX_DCB_MAX_DBS]; struct page_pool *page_pool; struct fdma fdma_rx_old; + int page_order, max_mtu; int err, i, j; old_pages = kmemdup(lan966x->rx.page, sizeof(lan966x->rx.page), @@ -825,6 +827,8 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) /* Store these for later to free them */ memcpy(&fdma_rx_old, &lan966x->rx.fdma, sizeof(struct fdma)); page_pool = lan966x->rx.page_pool; + page_order = lan966x->rx.page_order; + max_mtu = lan966x->rx.max_mtu; napi_synchronize(&lan966x->napi); napi_disable(&lan966x->napi); @@ -854,7 +858,24 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) return 0; restore: lan966x->rx.page_pool = page_pool; + lan966x->rx.page_order = page_order; + lan966x->rx.max_mtu = max_mtu; memcpy(&lan966x->rx.fdma, &fdma_rx_old, sizeof(struct fdma)); + /* + * lan966x_fdma_rx_alloc_page_pool() registered the new pool with + * each port's XDP RXQ before the allocation failed. The new pool is + * destroyed by lan966x_fdma_rx_alloc(), so restore the old pool's + * registration before restarting RX. + */ + for (i = 0; i < lan966x->num_phys_ports; i++) { + if (!lan966x->ports[i]) + continue; + + xdp_rxq_info_unreg_mem_model(&lan966x->ports[i]->xdp_rxq); + xdp_rxq_info_reg_mem_model(&lan966x->ports[i]->xdp_rxq, + MEM_TYPE_PAGE_POOL, page_pool); + } + lan966x_fdma_rx_start(&lan966x->rx); lan966x_fdma_wakeup_netdev(lan966x); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c index cd4f42c3f7eb..83b37f95ee46 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c @@ -277,6 +277,9 @@ int sparx5_psfp_fm_add(struct sparx5 *sparx5, u32 uidx, ret = sparx5_psfp_fm_get(sparx5, uidx, &fm->pol.idx); if (ret < 0) return ret; + + *id = fm->pol.idx; + /* Was already in use, no need to reconfigure */ if (ret > 1) return 0; @@ -291,8 +294,6 @@ int sparx5_psfp_fm_add(struct sparx5 *sparx5, u32 uidx, if (ret < 0) return ret; - *id = fm->pol.idx; - return 0; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c index f779a5c00803..a17143953502 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -807,7 +807,7 @@ static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5, /* Add new flow-meter */ ret = sparx5_psfp_fm_add(sparx5, pol_idx, fm, &psfp_fmid); if (ret < 0) - return ret; + goto err_sg_del; } /* Map stream filter to stream gate */ @@ -816,7 +816,7 @@ static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5, /* Add new stream-filter and map it to a steam gate */ ret = sparx5_psfp_sf_add(sparx5, sf, &psfp_sfid); if (ret < 0) - return ret; + goto err_fm_del; /* Streams are classified by ISDX - map ISDX 1:1 to sfid for now. */ sparx5_isdx_conf_set(sparx5, psfp_sfid, psfp_sfid, psfp_fmid); @@ -824,13 +824,23 @@ static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5, ret = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_ADD_REPLACE_SEL, VCAP_BIT_1); if (ret) - return ret; + goto err_sf_del; ret = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_VAL, psfp_sfid); if (ret) - return ret; + goto err_sf_del; return 0; + +err_sf_del: + sparx5_isdx_conf_set(sparx5, psfp_sfid, 0, 0); + sparx5_psfp_sf_del(sparx5, psfp_sfid); +err_fm_del: + if (pol_idx >= 0) + sparx5_psfp_fm_del(sparx5, psfp_fmid); +err_sg_del: + sparx5_psfp_sg_del(sparx5, psfp_sgid); + return ret; } /* Handle the action trap for a VCAP rule */ diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index d8e816882f02..a0fdd052d7f1 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -150,7 +150,7 @@ static int mana_gd_init_registers(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); - if (gc->is_pf) + if (gc->is_pf && !gc->is_pf2) return mana_gd_init_pf_regs(pdev); else return mana_gd_init_vf_regs(pdev); @@ -179,8 +179,21 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev) struct gdma_context *gc = pci_get_drvdata(pdev); struct gdma_query_max_resources_resp resp = {}; struct gdma_general_req req = {}; + unsigned int max_num_queues; + u8 bm_hostmode; + u16 num_ports; int err; + /* Reset msi_sharing so it is recomputed from current hardware + * state. On resume, num_online_cpus() or num_msix_usable may + * have changed, making dedicated MSI-X feasible where it was + * not before. Only reset on platforms that support dynamic + * MSI-X allocation; on non-dyn platforms msi_sharing is + * unconditionally true (set in mana_gd_setup_hwc_irqs). + */ + if (pci_msix_can_alloc_dyn(to_pci_dev(gc->dev))) + gc->msi_sharing = false; + mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_MAX_RESOURCES, sizeof(req), sizeof(resp)); @@ -224,6 +237,60 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev) if (gc->max_num_queues > gc->num_msix_usable - 1) gc->max_num_queues = gc->num_msix_usable - 1; + if (gc->max_num_queues == 0) + return -ENOSPC; + + debugfs_create_u32("num_msix_usable", 0400, gc->mana_pci_debugfs, + &gc->num_msix_usable); + debugfs_create_u32("max_num_queues", 0400, gc->mana_pci_debugfs, + &gc->max_num_queues); + + err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION, + MANA_MINOR_VERSION, + MANA_MICRO_VERSION, + &num_ports, &bm_hostmode); + if (err) + return err; + + if (!num_ports) { + dev_err(gc->dev, "Failed to detect any vPort\n"); + return -EINVAL; + } + + /* Cap to the same limit used by mana_probe() for port instantiation, + * so MSI-X and queue budgeting matches the actual port count. + */ + if (num_ports > MAX_PORTS_IN_MANA_DEV) + num_ports = MAX_PORTS_IN_MANA_DEV; + + /* + * Adjust the per-vPort max queue count to allow dedicated + * MSIx for each vPort. Prefer at least MANA_DEF_NUM_QUEUES, + * but the hardware max (gc->max_num_queues) takes precedence. + */ + max_num_queues = (gc->num_msix_usable - 1) / num_ports; + max_num_queues = rounddown_pow_of_two(max(max_num_queues, 1U)); + if (max_num_queues < MANA_DEF_NUM_QUEUES) + max_num_queues = MANA_DEF_NUM_QUEUES; + + /* + * Use dedicated MSIx for EQs whenever possible, use MSIx sharing for + * Ethernet EQs when (max_num_queues * num_ports > num_msix_usable - 1). + */ + max_num_queues = min(gc->max_num_queues, max_num_queues); + if (max_num_queues * num_ports > gc->num_msix_usable - 1) + gc->msi_sharing = true; + + /* If MSI is shared, use max allowed value */ + if (gc->msi_sharing) + gc->max_num_queues_vport = min(gc->num_msix_usable - 1, + gc->max_num_queues); + else + gc->max_num_queues_vport = max_num_queues; + + dev_info(gc->dev, "MSI sharing mode %u max queues %u\n", + gc->msi_sharing, gc->max_num_queues_vport); + return 0; } @@ -804,6 +871,10 @@ static int mana_gd_register_irq(struct gdma_queue *queue, } queue->eq.msix_index = msi_index; + /* The caller acquired a GIC reference via mana_gd_get_gic(). + * That refcount prevents mana_gd_put_gic() from erasing this + * irq_contexts entry concurrently. + */ gic = xa_load(&gc->irq_contexts, msi_index); if (WARN_ON(!gic)) return -EINVAL; @@ -831,6 +902,10 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue) if (WARN_ON(msix_index >= gc->num_msix_usable)) return; + /* The caller releases the GIC reference via mana_gd_put_gic() + * after this function returns. The refcount guarantees this + * irq_contexts entry is still valid. + */ gic = xa_load(&gc->irq_contexts, msix_index); if (WARN_ON(!gic)) return; @@ -844,7 +919,6 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue) } spin_unlock_irqrestore(&gic->lock, flags); - queue->eq.msix_index = INVALID_PCI_MSIX_INDEX; synchronize_rcu(); } @@ -959,6 +1033,7 @@ static int mana_gd_create_eq(struct gdma_dev *gd, out: dev_err(dev, "Failed to create EQ: %d\n", err); mana_gd_destroy_eq(gc, false, queue); + queue->eq.msix_index = INVALID_PCI_MSIX_INDEX; return err; } @@ -1192,6 +1267,8 @@ int mana_gd_create_mana_wq_cq(struct gdma_dev *gd, if (!queue) return -ENOMEM; + queue->id = INVALID_QUEUE_ID; + gmi = &queue->mem_info; err = mana_gd_alloc_memory(gc, spec->queue_size, gmi); if (err) { @@ -1294,6 +1371,13 @@ int mana_gd_verify_vf_version(struct pci_dev *pdev) return err ? err : -EPROTO; } gc->pf_cap_flags1 = resp.pf_cap_flags1; + gc->gdma_protocol_ver = resp.gdma_protocol_ver; + + debugfs_create_x64("gdma_protocol_ver", 0400, gc->mana_pci_debugfs, + &gc->gdma_protocol_ver); + debugfs_create_x64("pf_cap_flags1", 0400, gc->mana_pci_debugfs, + &gc->pf_cap_flags1); + if (resp.pf_cap_flags1 & GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG) { err = mana_gd_query_hwc_timeout(pdev, &hwc->hwc_timeout); if (err) { @@ -1605,6 +1689,166 @@ static irqreturn_t mana_gd_intr(int irq, void *arg) return IRQ_HANDLED; } +void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi) +{ + struct pci_dev *dev = to_pci_dev(gc->dev); + struct gdma_irq_context *gic; + struct msi_map irq_map; + int irq; + + mutex_lock(&gc->gic_mutex); + + gic = xa_load(&gc->irq_contexts, msi); + if (WARN_ON(!gic)) { + mutex_unlock(&gc->gic_mutex); + return; + } + + if (use_msi_bitmap) + gic->bitmap_refs--; + + if (use_msi_bitmap && gic->bitmap_refs == 0) + clear_bit(msi, gc->msi_bitmap); + + if (!refcount_dec_and_test(&gic->refcount)) + goto out; + + irq = gic->irq; + + irq_update_affinity_hint(irq, NULL); + free_irq(irq, gic); + + if (gic->dyn_msix) { + irq_map.virq = irq; + irq_map.index = msi; + pci_msix_free_irq(dev, irq_map); + } + + xa_erase(&gc->irq_contexts, msi); + kfree(gic); + +out: + mutex_unlock(&gc->gic_mutex); +} +EXPORT_SYMBOL_NS(mana_gd_put_gic, "NET_MANA"); + +/* + * Get a GIC (GDMA IRQ Context) on a MSI vector + * a MSI can be shared between different EQs, this function supports setting + * up separate MSIs using a bitmap, or directly using the MSI index + * + * @use_msi_bitmap: + * True if MSI is assigned by this function on available slots from bitmap. + * False if MSI is passed from *msi_requested + */ +struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc, + bool use_msi_bitmap, + int *msi_requested) +{ + struct pci_dev *dev = to_pci_dev(gc->dev); + struct gdma_irq_context *gic; + struct msi_map irq_map = { }; + int irq; + int msi; + int err; + + mutex_lock(&gc->gic_mutex); + + if (use_msi_bitmap) { + msi = find_first_zero_bit(gc->msi_bitmap, gc->num_msix_usable); + if (msi >= gc->num_msix_usable) { + dev_err(gc->dev, "No free MSI vectors available\n"); + gic = ERR_PTR(-ENOSPC); + goto out; + } + *msi_requested = msi; + } else { + msi = *msi_requested; + } + + gic = xa_load(&gc->irq_contexts, msi); + if (gic) { + refcount_inc(&gic->refcount); + if (use_msi_bitmap) { + gic->bitmap_refs++; + set_bit(msi, gc->msi_bitmap); + } + goto out; + } + + irq = pci_irq_vector(dev, msi); + if (irq == -EINVAL) { + irq_map = pci_msix_alloc_irq_at(dev, msi, NULL); + if (!irq_map.virq) { + err = irq_map.index; + dev_err(gc->dev, + "Failed to alloc irq_map msi %d err %d\n", + msi, err); + gic = ERR_PTR(err); + goto out; + } + irq = irq_map.virq; + msi = irq_map.index; + *msi_requested = msi; + } + + gic = kzalloc(sizeof(*gic), GFP_KERNEL); + if (!gic) { + gic = ERR_PTR(-ENOMEM); + if (irq_map.virq) + pci_msix_free_irq(dev, irq_map); + goto out; + } + + gic->handler = mana_gd_process_eq_events; + gic->msi = msi; + gic->irq = irq; + INIT_LIST_HEAD(&gic->eq_list); + spin_lock_init(&gic->lock); + + if (!gic->msi) + snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s", + pci_name(dev)); + else + snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_msi%d@pci:%s", + gic->msi, pci_name(dev)); + + err = request_irq(irq, mana_gd_intr, 0, gic->name, gic); + if (err) { + dev_err(gc->dev, "Failed to request irq %d %s\n", + irq, gic->name); + kfree(gic); + gic = ERR_PTR(err); + if (irq_map.virq) + pci_msix_free_irq(dev, irq_map); + goto out; + } + + gic->dyn_msix = !!irq_map.virq; + refcount_set(&gic->refcount, 1); + gic->bitmap_refs = use_msi_bitmap ? 1 : 0; + + err = xa_err(xa_store(&gc->irq_contexts, msi, gic, GFP_KERNEL)); + if (err) { + dev_err(gc->dev, "Failed to store irq context for msi %d: %d\n", + msi, err); + free_irq(irq, gic); + kfree(gic); + gic = ERR_PTR(err); + if (irq_map.virq) + pci_msix_free_irq(dev, irq_map); + goto out; + } + + if (use_msi_bitmap) + set_bit(msi, gc->msi_bitmap); + +out: + mutex_unlock(&gc->gic_mutex); + return gic; +} +EXPORT_SYMBOL_NS(mana_gd_get_gic, "NET_MANA"); + int mana_gd_alloc_res_map(u32 res_avail, struct gdma_resource *r) { r->map = bitmap_zalloc(res_avail, GFP_KERNEL); @@ -1707,7 +1951,7 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec) struct gdma_context *gc = pci_get_drvdata(pdev); struct gdma_irq_context *gic; bool skip_first_cpu = false; - int *irqs, irq, err, i; + int *irqs, err, i, msi; irqs = kmalloc_objs(int, nvec); if (!irqs) @@ -1720,30 +1964,14 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec) * further used in irq_setup() */ for (i = 1; i <= nvec; i++) { - gic = kzalloc_obj(*gic); - if (!gic) { - err = -ENOMEM; + msi = i; + gic = mana_gd_get_gic(gc, false, &msi); + if (IS_ERR(gic)) { + err = PTR_ERR(gic); goto free_irq; } - gic->handler = mana_gd_process_eq_events; - INIT_LIST_HEAD(&gic->eq_list); - spin_lock_init(&gic->lock); - snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s", - i - 1, pci_name(pdev)); - - /* one pci vector is already allocated for HWC */ - irqs[i - 1] = pci_irq_vector(pdev, i); - if (irqs[i - 1] < 0) { - err = irqs[i - 1]; - goto free_current_gic; - } - - err = request_irq(irqs[i - 1], mana_gd_intr, 0, gic->name, gic); - if (err) - goto free_current_gic; - - xa_store(&gc->irq_contexts, i, gic, GFP_KERNEL); + irqs[i - 1] = gic->irq; } /* @@ -1765,20 +1993,9 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec) kfree(irqs); return 0; -free_current_gic: - kfree(gic); free_irq: - for (i -= 1; i > 0; i--) { - irq = pci_irq_vector(pdev, i); - gic = xa_load(&gc->irq_contexts, i); - if (WARN_ON(!gic)) - continue; - - irq_update_affinity_hint(irq, NULL); - free_irq(irq, gic); - xa_erase(&gc->irq_contexts, i); - kfree(gic); - } + for (i -= 1; i > 0; i--) + mana_gd_put_gic(gc, false, i); kfree(irqs); return err; } @@ -1787,9 +2004,9 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec) { struct gdma_context *gc = pci_get_drvdata(pdev); struct gdma_irq_context *gic; - int *irqs, *start_irqs, irq; + int *irqs, *start_irqs; unsigned int cpu; - int err, i; + int err, i, msi; irqs = kmalloc_objs(int, nvec); if (!irqs) @@ -1798,34 +2015,14 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec) start_irqs = irqs; for (i = 0; i < nvec; i++) { - gic = kzalloc_obj(*gic); - if (!gic) { - err = -ENOMEM; + msi = i; + gic = mana_gd_get_gic(gc, false, &msi); + if (IS_ERR(gic)) { + err = PTR_ERR(gic); goto free_irq; } - gic->handler = mana_gd_process_eq_events; - INIT_LIST_HEAD(&gic->eq_list); - spin_lock_init(&gic->lock); - - if (!i) - snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s", - pci_name(pdev)); - else - snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s", - i - 1, pci_name(pdev)); - - irqs[i] = pci_irq_vector(pdev, i); - if (irqs[i] < 0) { - err = irqs[i]; - goto free_current_gic; - } - - err = request_irq(irqs[i], mana_gd_intr, 0, gic->name, gic); - if (err) - goto free_current_gic; - - xa_store(&gc->irq_contexts, i, gic, GFP_KERNEL); + irqs[i] = gic->irq; } /* If number of IRQ is one extra than number of online CPUs, @@ -1854,20 +2051,9 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec) kfree(start_irqs); return 0; -free_current_gic: - kfree(gic); free_irq: - for (i -= 1; i >= 0; i--) { - irq = pci_irq_vector(pdev, i); - gic = xa_load(&gc->irq_contexts, i); - if (WARN_ON(!gic)) - continue; - - irq_update_affinity_hint(irq, NULL); - free_irq(irq, gic); - xa_erase(&gc->irq_contexts, i); - kfree(gic); - } + for (i -= 1; i >= 0; i--) + mana_gd_put_gic(gc, false, i); kfree(start_irqs); return err; @@ -1886,6 +2072,7 @@ static int mana_gd_setup_hwc_irqs(struct pci_dev *pdev) /* Need 1 interrupt for HWC */ max_irqs = min(num_online_cpus(), MANA_MAX_NUM_QUEUES) + 1; min_irqs = 2; + gc->msi_sharing = true; } nvec = pci_alloc_irq_vectors(pdev, min_irqs, max_irqs, PCI_IRQ_MSIX); @@ -1940,30 +2127,24 @@ static int mana_gd_setup_remaining_irqs(struct pci_dev *pdev) static void mana_gd_remove_irqs(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); - struct gdma_irq_context *gic; - int irq, i; + int i; if (gc->max_num_msix < 1) return; for (i = 0; i < gc->max_num_msix; i++) { - irq = pci_irq_vector(pdev, i); - if (irq < 0) + if (!xa_load(&gc->irq_contexts, i)) continue; - gic = xa_load(&gc->irq_contexts, i); - if (WARN_ON(!gic)) - continue; - - /* Need to clear the hint before free_irq */ - irq_update_affinity_hint(irq, NULL); - free_irq(irq, gic); - xa_erase(&gc->irq_contexts, i); - kfree(gic); + mana_gd_put_gic(gc, false, i); } + WARN_ON(!xa_empty(&gc->irq_contexts)); + pci_free_irq_vectors(pdev); + bitmap_free(gc->msi_bitmap); + gc->msi_bitmap = NULL; gc->max_num_msix = 0; gc->num_msix_usable = 0; } @@ -1973,15 +2154,20 @@ static int mana_gd_setup(struct pci_dev *pdev) struct gdma_context *gc = pci_get_drvdata(pdev); int err; + gc->mana_pci_debugfs = debugfs_create_dir(pci_name(pdev), + mana_debugfs_root); + err = mana_gd_init_registers(pdev); if (err) - return err; + goto remove_debugfs; mana_smc_init(&gc->shm_channel, gc->dev, gc->shm_base); gc->service_wq = alloc_ordered_workqueue("gdma_service_wq", 0); - if (!gc->service_wq) - return -ENOMEM; + if (!gc->service_wq) { + err = -ENOMEM; + goto remove_debugfs; + } err = mana_gd_setup_hwc_irqs(pdev); if (err) { @@ -1998,6 +2184,10 @@ static int mana_gd_setup(struct pci_dev *pdev) if (err) goto destroy_hwc; + err = mana_gd_detect_devices(pdev); + if (err) + goto destroy_hwc; + err = mana_gd_query_max_resources(pdev); if (err) goto destroy_hwc; @@ -2008,9 +2198,15 @@ static int mana_gd_setup(struct pci_dev *pdev) goto destroy_hwc; } - err = mana_gd_detect_devices(pdev); - if (err) - goto destroy_hwc; + if (!gc->msi_sharing) { + gc->msi_bitmap = bitmap_zalloc(gc->num_msix_usable, GFP_KERNEL); + if (!gc->msi_bitmap) { + err = -ENOMEM; + goto destroy_hwc; + } + /* Set bit for HWC */ + set_bit(0, gc->msi_bitmap); + } dev_dbg(&pdev->dev, "mana gdma setup successful\n"); return 0; @@ -2022,11 +2218,14 @@ static int mana_gd_setup(struct pci_dev *pdev) free_workqueue: destroy_workqueue(gc->service_wq); gc->service_wq = NULL; +remove_debugfs: + debugfs_remove_recursive(gc->mana_pci_debugfs); + gc->mana_pci_debugfs = NULL; dev_err(&pdev->dev, "%s failed (error %d)\n", __func__, err); return err; } -static void mana_gd_cleanup(struct pci_dev *pdev) +static void mana_gd_cleanup_device(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); @@ -2038,12 +2237,16 @@ static void mana_gd_cleanup(struct pci_dev *pdev) destroy_workqueue(gc->service_wq); gc->service_wq = NULL; } + + debugfs_remove_recursive(gc->mana_pci_debugfs); + gc->mana_pci_debugfs = NULL; + dev_dbg(&pdev->dev, "mana gdma cleanup successful\n"); } static bool mana_is_pf(unsigned short dev_id) { - return dev_id == MANA_PF_DEVICE_ID; + return dev_id == MANA_PF_DEVICE_ID || dev_id == MANA_PF2_DEVICE_ID; } static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -2081,6 +2284,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto release_region; mutex_init(&gc->eq_test_event_mutex); + mutex_init(&gc->gic_mutex); pci_set_drvdata(pdev, gc); gc->bar0_pa = pci_resource_start(pdev, 0); gc->bar0_size = pci_resource_len(pdev, 0); @@ -2091,13 +2295,12 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) gc->numa_node = dev_to_node(&pdev->dev); gc->is_pf = mana_is_pf(pdev->device); + gc->is_pf2 = (pdev->device == MANA_PF2_DEVICE_ID); + gc->bar0_va = bar0_va; gc->dev = &pdev->dev; xa_init(&gc->irq_contexts); - gc->mana_pci_debugfs = debugfs_create_dir(pci_name(pdev), - mana_debugfs_root); - err = mana_gd_setup(pdev); if (err) goto unmap_bar; @@ -2126,16 +2329,8 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) cleanup_mana: mana_remove(&gc->mana, false); cleanup_gd: - mana_gd_cleanup(pdev); + mana_gd_cleanup_device(pdev); unmap_bar: - /* - * at this point we know that the other debugfs child dir/files - * are either not yet created or are already cleaned up. - * The pci debugfs folder clean-up now, will only be cleaning up - * adapter-MTU file and apc->mana_pci_debugfs folder. - */ - debugfs_remove_recursive(gc->mana_pci_debugfs); - gc->mana_pci_debugfs = NULL; xa_destroy(&gc->irq_contexts); pci_iounmap(pdev, bar0_va); free_gc: @@ -2185,11 +2380,7 @@ static void mana_gd_remove(struct pci_dev *pdev) mana_rdma_remove(&gc->mana_ib); mana_remove(&gc->mana, false); - mana_gd_cleanup(pdev); - - debugfs_remove_recursive(gc->mana_pci_debugfs); - - gc->mana_pci_debugfs = NULL; + mana_gd_cleanup_device(pdev); xa_destroy(&gc->irq_contexts); @@ -2211,15 +2402,11 @@ int mana_gd_suspend(struct pci_dev *pdev, pm_message_t state) mana_rdma_remove(&gc->mana_ib); mana_remove(&gc->mana, true); - mana_gd_cleanup(pdev); + mana_gd_cleanup_device(pdev); return 0; } -/* In case the NIC hardware stops working, the suspend and resume callbacks will - * fail -- if this happens, it's safer to just report an error than try to undo - * what has been done. - */ int mana_gd_resume(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); @@ -2231,13 +2418,17 @@ int mana_gd_resume(struct pci_dev *pdev) err = mana_probe(&gc->mana, true); if (err) - return err; + goto cleanup_gd; err = mana_rdma_probe(&gc->mana_ib); if (err) - return err; + mana_rdma_remove(&gc->mana_ib); - return 0; + return err; + +cleanup_gd: + mana_gd_cleanup_device(pdev); + return err; } /* Quiesce the device for kexec. This is also called upon reboot/shutdown. */ @@ -2250,17 +2441,14 @@ static void mana_gd_shutdown(struct pci_dev *pdev) mana_rdma_remove(&gc->mana_ib); mana_remove(&gc->mana, true); - mana_gd_cleanup(pdev); - - debugfs_remove_recursive(gc->mana_pci_debugfs); - - gc->mana_pci_debugfs = NULL; + mana_gd_cleanup_device(pdev); pci_disable_device(pdev); } static const struct pci_device_id mana_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF_DEVICE_ID) }, + { PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_PF2_DEVICE_ID) }, { PCI_DEVICE(PCI_VENDOR_ID_MICROSOFT, MANA_VF_DEVICE_ID) }, { } }; diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c index 7697c9b52ed3..b5e9bb184a1d 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c +++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c @@ -68,7 +68,7 @@ int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames, count++; } - tx_stats = &apc->tx_qp[q_idx].txq.stats; + tx_stats = &apc->tx_qp[q_idx]->txq.stats; u64_stats_update_begin(&tx_stats->syncp); tx_stats->xdp_xmit += count; diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index c9b1df1ed109..87862b0434c7 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -309,11 +309,18 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work) rtnl_lock(); + /* Block RDMA from grabbing the vport during the detach/attach + * window, same as mana_set_channels(). + */ + mutex_lock(&apc->vport_mutex); + apc->channel_changing = true; + mutex_unlock(&apc->vport_mutex); + /* Pre-allocate buffers to prevent failure in mana_attach later */ err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues); if (err) { netdev_err(ndev, "Insufficient memory for reset post tx stall detection\n"); - goto out; + goto clear_flag; } err = mana_detach(ndev, false); @@ -328,7 +335,11 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work) dealloc_pre_rxbufs: mana_pre_dealloc_rxbufs(apc); -out: +clear_flag: + mutex_lock(&apc->vport_mutex); + apc->channel_changing = false; + mutex_unlock(&apc->vport_mutex); + rtnl_unlock(); } @@ -355,9 +366,9 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (skb_cow_head(skb, MANA_HEADROOM)) goto tx_drop_count; - txq = &apc->tx_qp[txq_idx].txq; + txq = &apc->tx_qp[txq_idx]->txq; gdma_sq = txq->gdma_sq; - cq = &apc->tx_qp[txq_idx].tx_cq; + cq = &apc->tx_qp[txq_idx]->tx_cq; tx_stats = &txq->stats; BUILD_BUG_ON(MAX_TX_WQE_SGL_ENTRIES != MANA_MAX_TX_WQE_SGL_ENTRIES); @@ -614,7 +625,7 @@ static void mana_get_stats64(struct net_device *ndev, } for (q = 0; q < num_queues; q++) { - tx_stats = &apc->tx_qp[q].txq.stats; + tx_stats = &apc->tx_qp[q]->txq.stats; do { start = u64_stats_fetch_begin(&tx_stats->syncp); @@ -685,11 +696,11 @@ void mana_pre_dealloc_rxbufs(struct mana_port_context *mpc) put_page(virt_to_head_page(mpc->rxbufs_pre[i])); } - kfree(mpc->das_pre); + kvfree(mpc->das_pre); mpc->das_pre = NULL; out2: - kfree(mpc->rxbufs_pre); + kvfree(mpc->rxbufs_pre); mpc->rxbufs_pre = NULL; out1: @@ -806,11 +817,11 @@ int mana_pre_alloc_rxbufs(struct mana_port_context *mpc, int new_mtu, int num_qu num_rxb = num_queues * mpc->rx_queue_size; WARN(mpc->rxbufs_pre, "mana rxbufs_pre exists\n"); - mpc->rxbufs_pre = kmalloc_array(num_rxb, sizeof(void *), GFP_KERNEL); + mpc->rxbufs_pre = kvmalloc_array(num_rxb, sizeof(void *), GFP_KERNEL); if (!mpc->rxbufs_pre) goto error; - mpc->das_pre = kmalloc_objs(dma_addr_t, num_rxb); + mpc->das_pre = kvmalloc_objs(dma_addr_t, num_rxb); if (!mpc->das_pre) goto error; @@ -1007,10 +1018,9 @@ static int mana_init_port_context(struct mana_port_context *apc) return !apc->rxqs ? -ENOMEM : 0; } -static int mana_send_request(struct mana_context *ac, void *in_buf, - u32 in_len, void *out_buf, u32 out_len) +static int gdma_mana_send_request(struct gdma_context *gc, void *in_buf, + u32 in_len, void *out_buf, u32 out_len) { - struct gdma_context *gc = ac->gdma_dev->gdma_context; struct gdma_resp_hdr *resp = out_buf; struct gdma_req_hdr *req = in_buf; struct device *dev = gc->dev; @@ -1044,6 +1054,14 @@ static int mana_send_request(struct mana_context *ac, void *in_buf, return 0; } +static int mana_send_request(struct mana_context *ac, void *in_buf, + u32 in_len, void *out_buf, u32 out_len) +{ + struct gdma_context *gc = ac->gdma_dev->gdma_context; + + return gdma_mana_send_request(gc, in_buf, in_len, out_buf, out_len); +} + static int mana_verify_resp_hdr(const struct gdma_resp_hdr *resp_hdr, const enum mana_command_code expected_code, const u32 min_size) @@ -1177,11 +1195,10 @@ static void mana_pf_deregister_filter(struct mana_port_context *apc) err, resp.hdr.status); } -static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver, - u32 proto_minor_ver, u32 proto_micro_ver, - u16 *max_num_vports, u8 *bm_hostmode) +int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver, + u32 proto_minor_ver, u32 proto_micro_ver, + u16 *max_num_vports, u8 *bm_hostmode) { - struct gdma_context *gc = ac->gdma_dev->gdma_context; struct mana_query_device_cfg_resp resp = {}; struct mana_query_device_cfg_req req = {}; struct device *dev = gc->dev; @@ -1196,7 +1213,8 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver, req.proto_minor_ver = proto_minor_ver; req.proto_micro_ver = proto_micro_ver; - err = mana_send_request(ac, &req, sizeof(req), &resp, sizeof(resp)); + err = gdma_mana_send_request(gc, &req, sizeof(req), + &resp, sizeof(resp)); if (err) { dev_err(dev, "Failed to query config: %d", err); return err; @@ -1230,8 +1248,6 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver, else *bm_hostmode = 0; - debugfs_create_u16("adapter-MTU", 0400, gc->mana_pci_debugfs, &gc->adapter_mtu); - return 0; } @@ -1262,6 +1278,12 @@ static int mana_query_vport_cfg(struct mana_port_context *apc, u32 vport_index, *max_sq = resp.max_num_sq; *max_rq = resp.max_num_rq; + + if (*max_sq == 0 || *max_rq == 0) { + netdev_err(apc->ndev, "Invalid max queues from vPort config\n"); + return -EPROTO; + } + if (resp.num_indirection_ent > 0 && resp.num_indirection_ent <= MANA_INDIRECT_TABLE_MAX_SIZE && is_power_of_2(resp.num_indirection_ent)) { @@ -1276,6 +1298,9 @@ static int mana_query_vport_cfg(struct mana_port_context *apc, u32 vport_index, apc->port_handle = resp.vport; ether_addr_copy(apc->mac_addr, resp.mac_addr); + apc->vport_max_sq = *max_sq; + apc->vport_max_rq = *max_rq; + return 0; } @@ -1289,7 +1314,7 @@ void mana_uncfg_vport(struct mana_port_context *apc) EXPORT_SYMBOL_NS(mana_uncfg_vport, "NET_MANA"); int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, - u32 doorbell_pg_id) + u32 doorbell_pg_id, bool check_channel_changing) { struct mana_config_vport_resp resp = {}; struct mana_config_vport_req req = {}; @@ -1314,7 +1339,8 @@ int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, * Ethernet usage on the same port. */ mutex_lock(&apc->vport_mutex); - if (apc->vport_use_count > 0) { + if (apc->vport_use_count > 0 || + (check_channel_changing && apc->channel_changing)) { mutex_unlock(&apc->vport_mutex); return -EBUSY; } @@ -1430,6 +1456,11 @@ static int mana_cfg_vport_steering(struct mana_port_context *apc, netdev_info(ndev, "Configured steering vPort %llu entries %u\n", apc->port_handle, apc->indir_table_sz); + + apc->steer_rx = rx; + apc->steer_rss = apc->rss_state; + apc->steer_update_tab = update_tab; + apc->steer_cqe_coalescing = req->cqe_coalescing_enable; out: kfree(req); return err; @@ -1442,6 +1473,12 @@ int mana_query_link_cfg(struct mana_port_context *apc) struct mana_query_link_config_req req = {}; int err; + netdev_assert_locked(ndev); + + err = apc->link_cfg_error; + if (err <= 0) + return err; + mana_gd_init_req_hdr(&req.hdr, MANA_QUERY_LINK_CONFIG, sizeof(req), sizeof(resp)); @@ -1454,6 +1491,7 @@ int mana_query_link_cfg(struct mana_port_context *apc) if (err) { if (err == -EOPNOTSUPP) { netdev_info_once(ndev, "MANA_QUERY_LINK_CONFIG not supported\n"); + apc->link_cfg_error = err; return err; } netdev_err(ndev, "Failed to query link config: %d\n", err); @@ -1471,12 +1509,12 @@ int mana_query_link_cfg(struct mana_port_context *apc) return err; } - if (resp.qos_unconfigured) { - err = -EINVAL; - return err; - } + if (resp.qos_unconfigured) + return -EINVAL; + apc->speed = resp.link_speed_mbps; apc->max_speed = resp.qos_speed_mbps; + apc->link_cfg_error = 0; return 0; } @@ -1488,6 +1526,8 @@ int mana_set_bw_clamp(struct mana_port_context *apc, u32 speed, struct net_device *ndev = apc->ndev; int err; + netdev_assert_locked(ndev); + mana_gd_init_req_hdr(&req.hdr, MANA_SET_BW_CLAMP, sizeof(req), sizeof(resp)); req.vport = apc->port_handle; @@ -1521,6 +1561,8 @@ int mana_set_bw_clamp(struct mana_port_context *apc, u32 speed, if (resp.qos_unconfigured) netdev_info(ndev, "QoS is unconfigured\n"); + /* Invalidate the cache; next query will re-fetch from firmware. */ + apc->link_cfg_error = 1; return 0; } @@ -1609,78 +1651,100 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, } EXPORT_SYMBOL_NS(mana_destroy_wq_obj, "NET_MANA"); -static void mana_destroy_eq(struct mana_context *ac) +void mana_destroy_eq(struct mana_port_context *apc) { + struct mana_context *ac = apc->ac; struct gdma_context *gc = ac->gdma_dev->gdma_context; struct gdma_queue *eq; + unsigned int msi; int i; - if (!ac->eqs) + if (!apc->eqs) return; - debugfs_remove_recursive(ac->mana_eqs_debugfs); - ac->mana_eqs_debugfs = NULL; + debugfs_remove_recursive(apc->mana_eqs_debugfs); + apc->mana_eqs_debugfs = NULL; - for (i = 0; i < gc->max_num_queues; i++) { - eq = ac->eqs[i].eq; + for (i = 0; i < apc->num_queues; i++) { + eq = apc->eqs[i].eq; if (!eq) continue; + msi = eq->eq.msix_index; mana_gd_destroy_queue(gc, eq); + mana_gd_put_gic(gc, !gc->msi_sharing, msi); } - kfree(ac->eqs); - ac->eqs = NULL; + kfree(apc->eqs); + apc->eqs = NULL; } +EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA"); -static void mana_create_eq_debugfs(struct mana_context *ac, int i) +static void mana_create_eq_debugfs(struct mana_port_context *apc, int i) { - struct mana_eq eq = ac->eqs[i]; + struct mana_eq eq = apc->eqs[i]; char eqnum[32]; sprintf(eqnum, "eq%d", i); - eq.mana_eq_debugfs = debugfs_create_dir(eqnum, ac->mana_eqs_debugfs); + eq.mana_eq_debugfs = debugfs_create_dir(eqnum, apc->mana_eqs_debugfs); debugfs_create_u32("head", 0400, eq.mana_eq_debugfs, &eq.eq->head); debugfs_create_u32("tail", 0400, eq.mana_eq_debugfs, &eq.eq->tail); + debugfs_create_u32("irq", 0400, eq.mana_eq_debugfs, &eq.eq->eq.irq); debugfs_create_file("eq_dump", 0400, eq.mana_eq_debugfs, eq.eq, &mana_dbg_q_fops); } -static int mana_create_eq(struct mana_context *ac) +int mana_create_eq(struct mana_port_context *apc) { - struct gdma_dev *gd = ac->gdma_dev; + struct gdma_dev *gd = apc->ac->gdma_dev; struct gdma_context *gc = gd->gdma_context; struct gdma_queue_spec spec = {}; + struct gdma_irq_context *gic; int err; + int msi; int i; - ac->eqs = kzalloc_objs(struct mana_eq, gc->max_num_queues); - if (!ac->eqs) + if (WARN_ON(apc->eqs)) + return -EEXIST; + apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues); + if (!apc->eqs) return -ENOMEM; spec.type = GDMA_EQ; spec.monitor_avl_buf = false; spec.queue_size = EQ_SIZE; spec.eq.callback = NULL; - spec.eq.context = ac->eqs; + spec.eq.context = apc->eqs; spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE; - ac->mana_eqs_debugfs = debugfs_create_dir("EQs", gc->mana_pci_debugfs); + apc->mana_eqs_debugfs = + debugfs_create_dir("EQs", apc->mana_port_debugfs); - for (i = 0; i < gc->max_num_queues; i++) { - spec.eq.msix_index = (i + 1) % gc->num_msix_usable; - err = mana_gd_create_mana_eq(gd, &spec, &ac->eqs[i].eq); - if (err) { - dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err); + for (i = 0; i < apc->num_queues; i++) { + msi = (i + 1) % gc->num_msix_usable; + + gic = mana_gd_get_gic(gc, !gc->msi_sharing, &msi); + if (IS_ERR(gic)) { + err = PTR_ERR(gic); goto out; } - mana_create_eq_debugfs(ac, i); + spec.eq.msix_index = msi; + + err = mana_gd_create_mana_eq(gd, &spec, &apc->eqs[i].eq); + if (err) { + dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err); + mana_gd_put_gic(gc, !gc->msi_sharing, msi); + goto out; + } + apc->eqs[i].eq->eq.irq = gic->irq; + mana_create_eq_debugfs(apc, i); } return 0; out: - mana_destroy_eq(ac); + mana_destroy_eq(apc); return err; } +EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA"); static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq) { @@ -2324,21 +2388,28 @@ static void mana_destroy_txq(struct mana_port_context *apc) return; for (i = 0; i < apc->num_queues; i++) { - debugfs_remove_recursive(apc->tx_qp[i].mana_tx_debugfs); - apc->tx_qp[i].mana_tx_debugfs = NULL; + if (!apc->tx_qp[i]) + continue; - napi = &apc->tx_qp[i].tx_cq.napi; - if (apc->tx_qp[i].txq.napi_initialized) { + debugfs_remove_recursive(apc->tx_qp[i]->mana_tx_debugfs); + apc->tx_qp[i]->mana_tx_debugfs = NULL; + + napi = &apc->tx_qp[i]->tx_cq.napi; + if (apc->tx_qp[i]->txq.napi_initialized) { napi_synchronize(napi); napi_disable_locked(napi); netif_napi_del_locked(napi); - apc->tx_qp[i].txq.napi_initialized = false; + apc->tx_qp[i]->txq.napi_initialized = false; } - mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object); - mana_deinit_cq(apc, &apc->tx_qp[i].tx_cq); + if (apc->tx_qp[i]->tx_object != INVALID_MANA_HANDLE) + mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i]->tx_object); - mana_deinit_txq(apc, &apc->tx_qp[i].txq); + mana_deinit_cq(apc, &apc->tx_qp[i]->tx_cq); + + mana_deinit_txq(apc, &apc->tx_qp[i]->txq); + + kvfree(apc->tx_qp[i]); } kfree(apc->tx_qp); @@ -2347,7 +2418,7 @@ static void mana_destroy_txq(struct mana_port_context *apc) static void mana_create_txq_debugfs(struct mana_port_context *apc, int idx) { - struct mana_tx_qp *tx_qp = &apc->tx_qp[idx]; + struct mana_tx_qp *tx_qp = apc->tx_qp[idx]; char qnum[32]; sprintf(qnum, "TX-%d", idx); @@ -2386,7 +2457,7 @@ static int mana_create_txq(struct mana_port_context *apc, int err; int i; - apc->tx_qp = kzalloc_objs(struct mana_tx_qp, apc->num_queues); + apc->tx_qp = kzalloc_objs(struct mana_tx_qp *, apc->num_queues); if (!apc->tx_qp) return -ENOMEM; @@ -2406,10 +2477,16 @@ static int mana_create_txq(struct mana_port_context *apc, gc = gd->gdma_context; for (i = 0; i < apc->num_queues; i++) { - apc->tx_qp[i].tx_object = INVALID_MANA_HANDLE; + apc->tx_qp[i] = kvzalloc_obj(*apc->tx_qp[i]); + if (!apc->tx_qp[i]) { + err = -ENOMEM; + goto out; + } + + apc->tx_qp[i]->tx_object = INVALID_MANA_HANDLE; /* Create SQ */ - txq = &apc->tx_qp[i].txq; + txq = &apc->tx_qp[i]->txq; u64_stats_init(&txq->stats.syncp); txq->ndev = net; @@ -2427,7 +2504,7 @@ static int mana_create_txq(struct mana_port_context *apc, goto out; /* Create SQ's CQ */ - cq = &apc->tx_qp[i].tx_cq; + cq = &apc->tx_qp[i]->tx_cq; cq->type = MANA_CQ_TYPE_TX; cq->txq = txq; @@ -2437,7 +2514,7 @@ static int mana_create_txq(struct mana_port_context *apc, spec.monitor_avl_buf = false; spec.queue_size = cq_size; spec.cq.callback = mana_schedule_napi; - spec.cq.parent_eq = ac->eqs[i].eq; + spec.cq.parent_eq = apc->eqs[i].eq; spec.cq.context = cq; err = mana_gd_create_mana_wq_cq(gd, &spec, &cq->gdma_cq); if (err) @@ -2456,7 +2533,7 @@ static int mana_create_txq(struct mana_port_context *apc, err = mana_create_wq_obj(apc, apc->port_handle, GDMA_SQ, &wq_spec, &cq_spec, - &apc->tx_qp[i].tx_object); + &apc->tx_qp[i]->tx_object); if (err) goto out; @@ -2559,7 +2636,7 @@ static void mana_destroy_rxq(struct mana_port_context *apc, if (rxq->gdma_rq) mana_gd_destroy_queue(gc, rxq->gdma_rq); - kfree(rxq); + kvfree(rxq); } static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, @@ -2699,7 +2776,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, gc = gd->gdma_context; - rxq = kzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size); + rxq = kvzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size); if (!rxq) return NULL; @@ -2830,13 +2907,12 @@ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx) static int mana_add_rx_queues(struct mana_port_context *apc, struct net_device *ndev) { - struct mana_context *ac = apc->ac; struct mana_rxq *rxq; int err = 0; int i; for (i = 0; i < apc->num_queues; i++) { - rxq = mana_create_rxq(apc, i, &ac->eqs[i], ndev); + rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev); if (!rxq) { err = -ENOMEM; netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err); @@ -2855,9 +2931,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc, return err; } -static void mana_destroy_vport(struct mana_port_context *apc) +static void mana_destroy_rxqs(struct mana_port_context *apc) { - struct gdma_dev *gd = apc->ac->gdma_dev; struct mana_rxq *rxq; u32 rxq_idx; @@ -2872,8 +2947,12 @@ static void mana_destroy_vport(struct mana_port_context *apc) apc->rxqs[rxq_idx] = NULL; } } +} + +static void mana_destroy_vport(struct mana_port_context *apc) +{ + struct gdma_dev *gd = apc->ac->gdma_dev; - mana_destroy_txq(apc); mana_uncfg_vport(apc); if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) @@ -2894,11 +2973,14 @@ static int mana_create_vport(struct mana_port_context *apc, return err; } - err = mana_cfg_vport(apc, gd->pdid, gd->doorbell); - if (err) + err = mana_cfg_vport(apc, gd->pdid, gd->doorbell, false); + if (err) { + if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) + mana_pf_deregister_hw_vport(apc); return err; + } - return mana_create_txq(apc, net); + return 0; } static int mana_rss_table_alloc(struct mana_port_context *apc) @@ -3160,6 +3242,8 @@ static int mana_init_port(struct net_device *ndev) max_queues = min_t(u32, max_txq, max_rxq); if (apc->max_queues > max_queues) apc->max_queues = max_queues; + if (apc->max_queues > gc->max_num_queues_vport) + apc->max_queues = gc->max_num_queues_vport; if (apc->num_queues > apc->max_queues) apc->num_queues = apc->max_queues; @@ -3167,6 +3251,23 @@ static int mana_init_port(struct net_device *ndev) eth_hw_addr_set(ndev, apc->mac_addr); sprintf(vport, "vport%d", port_idx); apc->mana_port_debugfs = debugfs_create_dir(vport, gc->mana_pci_debugfs); + + debugfs_create_u64("port_handle", 0400, apc->mana_port_debugfs, + &apc->port_handle); + debugfs_create_u32("max_sq", 0400, apc->mana_port_debugfs, + &apc->vport_max_sq); + debugfs_create_u32("max_rq", 0400, apc->mana_port_debugfs, + &apc->vport_max_rq); + debugfs_create_u32("indir_table_sz", 0400, apc->mana_port_debugfs, + &apc->indir_table_sz); + debugfs_create_u32("steer_rx", 0400, apc->mana_port_debugfs, + &apc->steer_rx); + debugfs_create_u32("steer_rss", 0400, apc->mana_port_debugfs, + &apc->steer_rss); + debugfs_create_bool("steer_update_tab", 0400, apc->mana_port_debugfs, + &apc->steer_update_tab); + debugfs_create_u32("steer_cqe_coalescing", 0400, apc->mana_port_debugfs, + &apc->steer_cqe_coalescing); debugfs_create_u32("current_speed", 0400, apc->mana_port_debugfs, &apc->speed); return 0; @@ -3184,21 +3285,36 @@ int mana_alloc_queues(struct net_device *ndev) err = mana_create_vport(apc, ndev); if (err) { - netdev_err(ndev, "Failed to create vPort %u : %d\n", apc->port_idx, err); + netdev_err(ndev, "Failed to create vPort %u : %d\n", + apc->port_idx, err); return err; } + err = mana_create_eq(apc); + if (err) { + netdev_err(ndev, "Failed to create EQ on vPort %u: %d\n", + apc->port_idx, err); + goto destroy_vport; + } + + err = mana_create_txq(apc, ndev); + if (err) { + netdev_err(ndev, "Failed to create TXQ on vPort %u: %d\n", + apc->port_idx, err); + goto destroy_eq; + } + err = netif_set_real_num_tx_queues(ndev, apc->num_queues); if (err) { netdev_err(ndev, "netif_set_real_num_tx_queues () failed for ndev with num_queues %u : %d\n", apc->num_queues, err); - goto destroy_vport; + goto destroy_txq; } err = mana_add_rx_queues(apc, ndev); if (err) - goto destroy_vport; + goto destroy_rxq; apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE; @@ -3207,7 +3323,7 @@ int mana_alloc_queues(struct net_device *ndev) netdev_err(ndev, "netif_set_real_num_rx_queues () failed for ndev with num_queues %u : %d\n", apc->num_queues, err); - goto destroy_vport; + goto destroy_rxq; } mana_rss_table_init(apc); @@ -3215,19 +3331,25 @@ int mana_alloc_queues(struct net_device *ndev) err = mana_config_rss(apc, TRI_STATE_TRUE, true, true); if (err) { netdev_err(ndev, "Failed to configure RSS table: %d\n", err); - goto destroy_vport; + goto destroy_rxq; } if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) { err = mana_pf_register_filter(apc); if (err) - goto destroy_vport; + goto destroy_rxq; } mana_chn_setxdp(apc, mana_xdp_get(apc)); return 0; +destroy_rxq: + mana_destroy_rxqs(apc); +destroy_txq: + mana_destroy_txq(apc); +destroy_eq: + mana_destroy_eq(apc); destroy_vport: mana_destroy_vport(apc); return err; @@ -3296,7 +3418,7 @@ static int mana_dealloc_queues(struct net_device *ndev) if (apc->tx_qp) { for (i = 0; i < apc->num_queues; i++) { - txq = &apc->tx_qp[i].txq; + txq = &apc->tx_qp[i]->txq; tsleep = 1000; while (atomic_read(&txq->pending_sends) > 0 && time_before(jiffies, timeout)) { @@ -3317,7 +3439,7 @@ static int mana_dealloc_queues(struct net_device *ndev) } for (i = 0; i < apc->num_queues; i++) { - txq = &apc->tx_qp[i].txq; + txq = &apc->tx_qp[i]->txq; while ((skb = skb_dequeue(&txq->pending_skbs))) { mana_unmap_skb(skb, apc); dev_kfree_skb_any(skb); @@ -3338,6 +3460,9 @@ static int mana_dealloc_queues(struct net_device *ndev) mana_fence_rqs(apc); /* Even in err case, still need to cleanup the vPort */ + mana_destroy_rxqs(apc); + mana_destroy_txq(apc); + mana_destroy_eq(apc); mana_destroy_vport(apc); return 0; @@ -3389,7 +3514,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, int err; ndev = alloc_etherdev_mq(sizeof(struct mana_port_context), - gc->max_num_queues); + gc->max_num_queues_vport); if (!ndev) return -ENOMEM; @@ -3398,14 +3523,15 @@ static int mana_probe_port(struct mana_context *ac, int port_idx, apc = netdev_priv(ndev); apc->ac = ac; apc->ndev = ndev; - apc->max_queues = gc->max_num_queues; + apc->max_queues = gc->max_num_queues_vport; /* Use MANA_DEF_NUM_QUEUES as default, still honoring the HW limit */ - apc->num_queues = min(gc->max_num_queues, MANA_DEF_NUM_QUEUES); + apc->num_queues = min(gc->max_num_queues_vport, MANA_DEF_NUM_QUEUES); apc->tx_queue_size = DEF_TX_BUFFERS_PER_QUEUE; apc->rx_queue_size = DEF_RX_BUFFERS_PER_QUEUE; apc->port_handle = INVALID_MANA_HANDLE; apc->pf_filter_handle = INVALID_MANA_HANDLE; apc->port_idx = port_idx; + apc->link_cfg_error = 1; apc->cqe_coalescing_enable = 0; mutex_init(&apc->vport_mutex); @@ -3664,19 +3790,18 @@ int mana_probe(struct gdma_dev *gd, bool resuming) INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler); - err = mana_create_eq(ac); - if (err) { - dev_err(dev, "Failed to create EQs: %d\n", err); - goto out; - } - - err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION, - MANA_MICRO_VERSION, &num_ports, &bm_hostmode); + err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION, + MANA_MINOR_VERSION, + MANA_MICRO_VERSION, + &num_ports, &bm_hostmode); if (err) goto out; ac->bm_hostmode = bm_hostmode; + debugfs_create_u16("adapter-MTU", 0400, + gc->mana_pci_debugfs, &gc->adapter_mtu); + if (!resuming) { ac->num_ports = num_ports; } else { @@ -3690,12 +3815,14 @@ int mana_probe(struct gdma_dev *gd, bool resuming) enable_work(&ac->link_change_work); } - if (ac->num_ports == 0) - dev_err(dev, "Failed to detect any vPort\n"); - if (ac->num_ports > MAX_PORTS_IN_MANA_DEV) ac->num_ports = MAX_PORTS_IN_MANA_DEV; + debugfs_create_u16("num_vports", 0400, gc->mana_pci_debugfs, + &ac->num_ports); + debugfs_create_u8("bm_hostmode", 0400, gc->mana_pci_debugfs, + &ac->bm_hostmode); + ac->per_port_queue_reset_wq = create_singlethread_workqueue("mana_per_port_queue_reset_wq"); if (!ac->per_port_queue_reset_wq) { @@ -3721,6 +3848,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) rtnl_lock(); apc = netdev_priv(ac->ports[i]); enable_work(&apc->queue_reset_work); + netdev_lock(ac->ports[i]); + apc->link_cfg_error = 1; + netdev_unlock(ac->ports[i]); err = mana_attach(ac->ports[i]); rtnl_unlock(); /* Log the port for which the attach failed, stop @@ -3809,8 +3939,6 @@ void mana_remove(struct gdma_dev *gd, bool suspending) free_netdev(ndev); } - mana_destroy_eq(ac); - if (ac->per_port_queue_reset_wq) { destroy_workqueue(ac->per_port_queue_reset_wq); ac->per_port_queue_reset_wq = NULL; @@ -3818,6 +3946,11 @@ void mana_remove(struct gdma_dev *gd, bool suspending) mana_gd_deregister_device(gd); + if (gc->mana_pci_debugfs) { + debugfs_lookup_and_remove("bm_hostmode", gc->mana_pci_debugfs); + debugfs_lookup_and_remove("num_vports", gc->mana_pci_debugfs); + } + if (suspending) return; diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c index 6a4b42fe0944..94e658d07a27 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c @@ -260,7 +260,7 @@ static void mana_get_ethtool_stats(struct net_device *ndev, } for (q = 0; q < num_queues; q++) { - tx_stats = &apc->tx_qp[q].txq.stats; + tx_stats = &apc->tx_qp[q]->txq.stats; do { start = u64_stats_fetch_begin(&tx_stats->syncp); @@ -454,6 +454,11 @@ static int mana_set_coalesce(struct net_device *ndev, return err; } +/* mana_set_channels - change the number of queues on a port + * + * Returns -EBUSY if RDMA holds the vport with EQs sized to the + * current num_queues. + */ static int mana_set_channels(struct net_device *ndev, struct ethtool_channels *channels) { @@ -462,10 +467,22 @@ static int mana_set_channels(struct net_device *ndev, unsigned int old_count = apc->num_queues; int err; + /* Set channel_changing to block RDMA from grabbing the vport + * during the detach/attach window. mana_cfg_vport() checks + * this flag under vport_mutex and returns -EBUSY if set. + */ + mutex_lock(&apc->vport_mutex); + if (!apc->port_is_up && apc->vport_use_count) { + mutex_unlock(&apc->vport_mutex); + return -EBUSY; + } + apc->channel_changing = true; + mutex_unlock(&apc->vport_mutex); + err = mana_pre_alloc_rxbufs(apc, ndev->mtu, new_count); if (err) { netdev_err(ndev, "Insufficient memory for new allocations"); - return err; + goto clear_flag; } err = mana_detach(ndev, false); @@ -483,6 +500,10 @@ static int mana_set_channels(struct net_device *ndev, out: mana_pre_dealloc_rxbufs(apc); +clear_flag: + mutex_lock(&apc->vport_mutex); + apc->channel_changing = false; + mutex_unlock(&apc->vport_mutex); return err; } @@ -575,6 +596,8 @@ static int mana_get_link_ksettings(struct net_device *ndev, const struct ethtool_ops mana_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM, .get_ethtool_stats = mana_get_ethtool_stats, .get_sset_count = mana_get_sset_count, .get_strings = mana_get_strings, diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index e50be508c166..42d2c456f712 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -92,7 +92,7 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port, int ocelot_mact_forget(struct ocelot *ocelot, const unsigned char mac[ETH_ALEN], unsigned int vid); struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port); -int ocelot_netdev_to_port(struct net_device *dev); +int ocelot_netdev_to_port(struct ocelot *ocelot, struct net_device *dev); int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, struct device_node *portnp); diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 844292eb7422..c5983c56a544 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -233,8 +233,8 @@ ocelot_flower_parse_egress_port(struct ocelot *ocelot, struct flow_cls_offload * const struct flow_action_entry *a, bool mirror, struct netlink_ext_ack *extack) { + int egress_port = ocelot->ops->netdev_to_port(ocelot, a->dev); const char *act_string = mirror ? "mirror" : "redirect"; - int egress_port = ocelot->ops->netdev_to_port(a->dev); enum flow_action_id offloadable_act_id; offloadable_act_id = mirror ? FLOW_ACTION_MIRRED : FLOW_ACTION_REDIRECT; @@ -580,7 +580,7 @@ static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port, return -ENOENT; } - ingress_port = ocelot->ops->netdev_to_port(indev); + ingress_port = ocelot->ops->netdev_to_port(ocelot, indev); if (ingress_port < 0) { NL_SET_ERR_MSG_MOD(extack, "Can only offload an ocelot ingress port"); diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 1b8269320464..4d45e136f08f 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -984,7 +984,7 @@ static bool ocelot_netdevice_dev_check(const struct net_device *dev) return dev->netdev_ops == &ocelot_port_netdev_ops; } -int ocelot_netdev_to_port(struct net_device *dev) +int ocelot_netdev_to_port(struct ocelot *ocelot, struct net_device *dev) { struct ocelot_port_private *priv; @@ -992,6 +992,8 @@ int ocelot_netdev_to_port(struct net_device *dev) return -EINVAL; priv = netdev_priv(dev); + if (priv->port.ocelot != ocelot) + return -EINVAL; return priv->port.index; } diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c index 316f941629d4..70a2b0082ba8 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -17,12 +17,12 @@ static const char rnpgbe_driver_name[] = "rnpgbe"; * private_data (used for different hw chip) } */ static struct pci_device_id rnpgbe_pci_tbl[] = { - { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210), board_n210 }, - { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210L), board_n210 }, - { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_DUAL_PORT), board_n500 }, - { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_QUAD_PORT), board_n500 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210), .driver_data = board_n210 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210L), .driver_data = board_n210 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_DUAL_PORT), .driver_data = board_n500 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_QUAD_PORT), .driver_data = board_n500 }, /* required last entry */ - {0, }, + { }, }; /** diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c index b253734dbc80..35a63b78880b 100644 --- a/drivers/net/ethernet/natsemi/natsemi.c +++ b/drivers/net/ethernet/natsemi/natsemi.c @@ -248,8 +248,8 @@ static struct { }; static const struct pci_device_id natsemi_pci_tbl[] = { - { PCI_VENDOR_ID_NS, 0x0020, 0x12d9, 0x000c, 0, 0, 0 }, - { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VDEVICE_SUB(NS, 0x0020, 0x12d9, 0x000c), .driver_data = 0 }, + { PCI_VDEVICE(NS, 0x0020), .driver_data = 1 }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl); diff --git a/drivers/net/ethernet/netronome/nfp/devlink_param.c b/drivers/net/ethernet/netronome/nfp/devlink_param.c index 85e3b19e6165..826527992e4a 100644 --- a/drivers/net/ethernet/netronome/nfp/devlink_param.c +++ b/drivers/net/ethernet/netronome/nfp/devlink_param.c @@ -170,7 +170,7 @@ nfp_devlink_param_u8_set(struct devlink *devlink, u32 id, static int nfp_devlink_param_u8_validate(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack) { const struct nfp_devlink_param_u8_arg *arg; @@ -180,12 +180,12 @@ nfp_devlink_param_u8_validate(struct devlink *devlink, u32 id, arg = &nfp_devlink_u8_args[id]; - if (val.vu8 > arg->max_dl_val) { + if (val->vu8 > arg->max_dl_val) { NL_SET_ERR_MSG_MOD(extack, "parameter out of range"); return -EINVAL; } - if (val.vu8 == arg->invalid_dl_val) { + if (val->vu8 == arg->invalid_dl_val) { NL_SET_ERR_MSG_MOD(extack, "unknown/invalid value specified"); return -EINVAL; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 48390b2fd44d..5a9fb5bad405 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -33,39 +33,40 @@ static const char nfp_driver_name[] = "nfp"; static const struct pci_device_id nfp_pci_device_ids[] = { - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP3800, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP3800, + { + PCI_VDEVICE_SUB(NETRONOME, PCI_DEVICE_ID_NFP3800, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP3800, + }, { + PCI_VDEVICE_SUB(NETRONOME, PCI_DEVICE_ID_NFP4000, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000, + }, { + PCI_VDEVICE_SUB(NETRONOME, PCI_DEVICE_ID_NFP5000, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000, + }, { + PCI_VDEVICE_SUB(NETRONOME, PCI_DEVICE_ID_NFP6000, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000, + }, { + PCI_VDEVICE_SUB(CORIGINE, PCI_DEVICE_ID_NFP3800, + PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP3800, + }, { + PCI_VDEVICE_SUB(CORIGINE, PCI_DEVICE_ID_NFP4000, + PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000, + }, { + PCI_VDEVICE_SUB(CORIGINE, PCI_DEVICE_ID_NFP5000, + PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000, + }, { + PCI_VDEVICE_SUB(CORIGINE, PCI_DEVICE_ID_NFP6000, + PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000, }, - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP4000, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000, - }, - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP5000, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000, - }, - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP6000, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000, - }, - { PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP3800, - PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP3800, - }, - { PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP4000, - PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000, - }, - { PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP5000, - PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000, - }, - { PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP6000, - PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000, - }, - { 0, } /* Required last entry. */ + { } /* Required last entry. */ }; MODULE_DEVICE_TABLE(pci, nfp_pci_device_ids); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c index b5646c884371..6c8f3321048a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -38,23 +38,24 @@ struct nfp_net_vf { static const char nfp_net_driver_name[] = "nfp_netvf"; static const struct pci_device_id nfp_netvf_pci_device_ids[] = { - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP3800_VF, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP3800_VF, + { + PCI_VDEVICE_SUB(NETRONOME, PCI_DEVICE_ID_NFP3800_VF, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP3800_VF, + }, { + PCI_VDEVICE_SUB(NETRONOME, PCI_DEVICE_ID_NFP6000_VF, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000_VF, + }, { + PCI_VDEVICE_SUB(CORIGINE, PCI_DEVICE_ID_NFP3800_VF, + PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP3800_VF, + }, { + PCI_VDEVICE_SUB(CORIGINE, PCI_DEVICE_ID_NFP6000_VF, + PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID), + .driver_data = NFP_DEV_NFP6000_VF, }, - { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NFP6000_VF, - PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000_VF, - }, - { PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP3800_VF, - PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP3800_VF, - }, - { PCI_VENDOR_ID_CORIGINE, PCI_DEVICE_ID_NFP6000_VF, - PCI_VENDOR_ID_CORIGINE, PCI_ANY_ID, - PCI_ANY_ID, 0, NFP_DEV_NFP6000_VF, - }, - { 0, } /* Required last entry. */ + { } /* Required last entry. */ }; MODULE_DEVICE_TABLE(pci, nfp_netvf_pci_device_ids); diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 91a906a7918a..0727d53345a3 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,8 @@ #define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) #define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) #define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) +#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ + GENMASK(12, 7)) /* PHY Clause 22 registers base address and mask */ #define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00 @@ -107,7 +110,6 @@ /* Internal structure for MAC-PHY drivers */ struct oa_tc6 { - struct device *dev; struct net_device *netdev; struct phy_device *phydev; struct mii_bus *mdiobus; @@ -121,14 +123,13 @@ struct oa_tc6 { struct sk_buff *ongoing_tx_skb; struct sk_buff *waiting_tx_skb; struct sk_buff *rx_skb; - struct task_struct *spi_thread; - wait_queue_head_t spi_wq; u16 tx_skb_offset; u16 spi_data_tx_buf_offset; u16 tx_credits; u8 rx_chunks_available; bool rx_buf_overflow; bool int_flag; + bool disable_traffic; }; enum oa_tc6_header_type { @@ -518,7 +519,7 @@ static int oa_tc6_mdiobus_register(struct oa_tc6 *tc6) tc6->mdiobus->read_c45 = oa_tc6_mdiobus_read_c45; tc6->mdiobus->write_c45 = oa_tc6_mdiobus_write_c45; tc6->mdiobus->name = "oa-tc6-mdiobus"; - tc6->mdiobus->parent = tc6->dev; + tc6->mdiobus->parent = &tc6->spi->dev; snprintf(tc6->mdiobus->id, ARRAY_SIZE(tc6->mdiobus->id), "%s", dev_name(&tc6->spi->dev)); @@ -669,6 +670,38 @@ static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6) } } +static void oa_tc6_cleanup_waiting_tx_skb(struct oa_tc6 *tc6) +{ + if (tc6->waiting_tx_skb) { + tc6->netdev->stats.tx_dropped++; + kfree_skb(tc6->waiting_tx_skb); + tc6->waiting_tx_skb = NULL; + } +} + +static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6) +{ + oa_tc6_cleanup_ongoing_tx_skb(tc6); + oa_tc6_cleanup_ongoing_rx_skb(tc6); + oa_tc6_cleanup_waiting_tx_skb(tc6); +} + +/* If the failure is at SPI interface level, masking and clearing + * the interrupt of the device won't work. Since SPI interrupt is + * disabled, it should stop the repeated interrupts. + */ +static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) +{ + u32 regval = INT_MASK0_ALL_INTERRUPTS; + + tc6->disable_traffic = true; + oa_tc6_free_pending_skbs(tc6); + oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval); + oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, ®val); + oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval); + dev_err(&tc6->spi->dev, "Device interrupt disabled to avoid interrupt storm"); +} + static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) { u32 value; @@ -752,6 +785,17 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) { + /* MAC-PHY delivers each frame with its Ethernet FCS attached. + * Strip it before handing over to the stack, unless the user + * has asked to keep it via NETIF_F_RXFCS. Keeping the FCS + * in the frame is harmless for IP traffic, but is parsed as + * a (malformed) suffix TLV by PTP, which makes ptp4l reject + * every message with "bad message" error. + */ + if (!(tc6->netdev->features & NETIF_F_RXFCS) && + tc6->rx_skb->len > ETH_FCS_LEN) + skb_trim(tc6->rx_skb, tc6->rx_skb->len - ETH_FCS_LEN); + tc6->rx_skb->protocol = eth_type_trans(tc6->rx_skb, tc6->netdev); tc6->netdev->stats.rx_packets++; tc6->netdev->stats.rx_bytes += tc6->rx_skb->len; @@ -1105,29 +1149,29 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6) return 0; } -static int oa_tc6_spi_thread_handler(void *data) +static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data) { struct oa_tc6 *tc6 = data; - int ret; + int ret = 0; - while (likely(!kthread_should_stop())) { - /* This kthread will be waken up if there is a tx skb or mac-phy - * interrupt to perform spi transfer with tx chunks. - */ - wait_event_interruptible(tc6->spi_wq, tc6->int_flag || - (tc6->waiting_tx_skb && - tc6->tx_credits) || - kthread_should_stop()); - - if (kthread_should_stop()) - break; - - ret = oa_tc6_try_spi_transfer(tc6); - if (ret) - return ret; + /* It is possible that interrupt woke the thread before it is + * disabled. Until we come up with good recovery mechanism, + * no need to attempt spi transfer, once it fails. Pending skbs + * are already freed. + */ + if (!tc6->disable_traffic) { + while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret = oa_tc6_try_spi_transfer(tc6); + if (ret) { + disable_irq_nosync(tc6->spi->irq); + oa_tc6_disable_traffic(tc6); + break; + } + } } - return 0; + return IRQ_HANDLED; } static int oa_tc6_update_buffer_status_from_register(struct oa_tc6 *tc6) @@ -1161,11 +1205,15 @@ static irqreturn_t oa_tc6_macphy_isr(int irq, void *data) * the previous rx footer. * - extended status event not reported in the previous rx footer. */ - tc6->int_flag = true; - /* Wake spi kthread to perform spi transfer */ - wake_up_interruptible(&tc6->spi_wq); - - return IRQ_HANDLED; + if (tc6->disable_traffic) + disable_irq_nosync(tc6->spi->irq); + else + tc6->int_flag = true; + /* Wake IRQ thread to perform spi transfer . In case + * disable_traffic is set, threaded irq may run again + * one more time. + */ + return IRQ_WAKE_THREAD; } /** @@ -1202,7 +1250,7 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable); */ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) { - if (tc6->waiting_tx_skb) { + if (tc6->disable_traffic || tc6->waiting_tx_skb) { netif_stop_queue(tc6->netdev); return NETDEV_TX_BUSY; } @@ -1217,8 +1265,8 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) tc6->waiting_tx_skb = skb; spin_unlock_bh(&tc6->tx_skb_lock); - /* Wake spi kthread to perform spi transfer */ - wake_up_interruptible(&tc6->spi_wq); + /* Wake the threaded IRQ to perform spi transfer. */ + irq_wake_thread(tc6->spi->irq, tc6); return NETDEV_TX_OK; } @@ -1311,24 +1359,15 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) goto phy_exit; } - init_waitqueue_head(&tc6->spi_wq); - - tc6->spi_thread = kthread_run(oa_tc6_spi_thread_handler, tc6, - "oa-tc6-spi-thread"); - if (IS_ERR(tc6->spi_thread)) { - dev_err(&tc6->spi->dev, "Failed to create SPI thread\n"); - goto phy_exit; - } - - sched_set_fifo(tc6->spi_thread); - - ret = devm_request_irq(&tc6->spi->dev, tc6->spi->irq, oa_tc6_macphy_isr, - IRQF_TRIGGER_FALLING, dev_name(&tc6->spi->dev), - tc6); + ret = devm_request_threaded_irq(&tc6->spi->dev, tc6->spi->irq, + oa_tc6_macphy_isr, + oa_tc6_macphy_threaded_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + dev_name(&tc6->spi->dev), tc6); if (ret) { dev_err(&tc6->spi->dev, "Failed to request macphy isr %d\n", ret); - goto kthread_stop; + goto phy_exit; } /* oa_tc6_sw_reset_macphy() function resets and clears the MAC-PHY reset @@ -1338,12 +1377,10 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) * 7.7 and 9.2.8.8 in the OPEN Alliance specification for more details. */ tc6->int_flag = true; - wake_up_interruptible(&tc6->spi_wq); + irq_wake_thread(tc6->spi->irq, tc6); return tc6; -kthread_stop: - kthread_stop(tc6->spi_thread); phy_exit: oa_tc6_phy_exit(tc6); return NULL; @@ -1356,11 +1393,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init); */ void oa_tc6_exit(struct oa_tc6 *tc6) { + tc6->disable_traffic = true; + disable_irq(tc6->spi->irq); oa_tc6_phy_exit(tc6); - kthread_stop(tc6->spi_thread); - dev_kfree_skb_any(tc6->ongoing_tx_skb); - dev_kfree_skb_any(tc6->waiting_tx_skb); - dev_kfree_skb_any(tc6->rx_skb); + oa_tc6_free_pending_skbs(tc6); } EXPORT_SYMBOL_GPL(oa_tc6_exit); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index 3838c4a70766..648d9d24be85 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -1076,3 +1076,13 @@ bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos) return ((pos - tail) & mask) < ((head - tail) & mask); } + +void ionic_reset_link_down_count(struct ionic_dev *idev) +{ + if (!READ_ONCE(idev->link_down_count_init)) { + idev->link_down_count_total = 0; + idev->link_down_count_last = + le16_to_cpu(idev->port_info->status.link_down_count); + WRITE_ONCE(idev->link_down_count_init, true); + } +} diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index 35566f97eaea..db90e39a1442 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -184,6 +184,10 @@ struct ionic_dev { u32 port_info_sz; struct ionic_port_info *port_info; dma_addr_t port_info_pa; + struct ionic_port_extra_stats port_extra_stats_cache; + bool link_down_count_init; + u16 link_down_count_last; + u32 link_down_count_total; struct ionic_devinfo dev_info; }; @@ -394,4 +398,6 @@ bool ionic_adminq_poke_doorbell(struct ionic_queue *q); bool ionic_txq_poke_doorbell(struct ionic_queue *q); bool ionic_rxq_poke_doorbell(struct ionic_queue *q); +void ionic_reset_link_down_count(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 78a802eb159f..c4ab4b5caa0a 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -115,9 +115,32 @@ static void ionic_get_link_ext_stats(struct net_device *netdev, struct ethtool_link_ext_stats *stats) { struct ionic_lif *lif = netdev_priv(netdev); + struct ionic *ionic = lif->ionic; + u64 link_down_count_total; + u16 link_down_count_fw; - if (lif->ionic->pdev->is_physfn) - stats->link_down_events = lif->link_down_count; + if (ionic->pdev->is_virtfn) + return; + + if (!ionic->idev.port_info) { + netdev_err_once(netdev, "port_info not initialized\n"); + return; + } + + link_down_count_fw = + le16_to_cpu(ionic->idev.port_info->status.link_down_count); + link_down_count_total = ionic->idev.link_down_count_total + + link_down_count_fw - + ionic->idev.link_down_count_last; + + /* The firmware counter is only 16 bits and can wraparound */ + if (link_down_count_fw < ionic->idev.link_down_count_last) + link_down_count_total += BIT(16); + + ionic->idev.link_down_count_last = link_down_count_fw; + ionic->idev.link_down_count_total = link_down_count_total; + + stats->link_down_events = link_down_count_total; } static int ionic_get_link_ksettings(struct net_device *netdev, diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h index 23d6e2b4791e..0a201422d0c5 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_if.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h @@ -273,10 +273,12 @@ union ionic_drv_identity { * enum ionic_dev_capability - Device capabilities * @IONIC_DEV_CAP_VF_CTRL: Device supports VF ctrl operations * @IONIC_DEV_CAP_DISC_CMB: Device supports CMB discovery operations + * @IONIC_DEV_CAP_EXTRA_STATS: Device supports extra stats schema */ enum ionic_dev_capability { IONIC_DEV_CAP_VF_CTRL = BIT(0), IONIC_DEV_CAP_DISC_CMB = BIT(1), + IONIC_DEV_CAP_EXTRA_STATS = BIT(4), }; /** @@ -2329,7 +2331,7 @@ struct ionic_qos_identify_comp { /* Capri max supported, should be renamed. */ #define IONIC_QOS_CLASS_MAX 7 #define IONIC_QOS_PCP_MAX 8 -#define IONIC_QOS_CLASS_NAME_SZ 32 +#define IONIC_QOS_CLASS_NAME_SZ 32 #define IONIC_QOS_DSCP_MAX 64 #define IONIC_QOS_ALL_PCP 0xFF #define IONIC_DSCP_BLOCK_SIZE 8 @@ -2855,54 +2857,12 @@ struct ionic_mgmt_port_stats { __le64 frames_tx_pause; }; -enum ionic_pb_buffer_drop_stats { - IONIC_BUFFER_INTRINSIC_DROP = 0, - IONIC_BUFFER_DISCARDED, - IONIC_BUFFER_ADMITTED, - IONIC_BUFFER_OUT_OF_CELLS_DROP, - IONIC_BUFFER_OUT_OF_CELLS_DROP_2, - IONIC_BUFFER_OUT_OF_CREDIT_DROP, - IONIC_BUFFER_TRUNCATION_DROP, - IONIC_BUFFER_PORT_DISABLED_DROP, - IONIC_BUFFER_COPY_TO_CPU_TAIL_DROP, - IONIC_BUFFER_SPAN_TAIL_DROP, - IONIC_BUFFER_MIN_SIZE_VIOLATION_DROP, - IONIC_BUFFER_ENQUEUE_ERROR_DROP, - IONIC_BUFFER_INVALID_PORT_DROP, - IONIC_BUFFER_INVALID_OUTPUT_QUEUE_DROP, - IONIC_BUFFER_DROP_MAX, -}; - -enum ionic_oflow_drop_stats { - IONIC_OFLOW_OCCUPANCY_DROP, - IONIC_OFLOW_EMERGENCY_STOP_DROP, - IONIC_OFLOW_WRITE_BUFFER_ACK_FILL_UP_DROP, - IONIC_OFLOW_WRITE_BUFFER_ACK_FULL_DROP, - IONIC_OFLOW_WRITE_BUFFER_FULL_DROP, - IONIC_OFLOW_CONTROL_FIFO_FULL_DROP, - IONIC_OFLOW_DROP_MAX, -}; - -/* struct ionic_port_pb_stats - packet buffers system stats - * uses ionic_pb_buffer_drop_stats for drop_counts[] - */ -struct ionic_port_pb_stats { - __le64 sop_count_in; - __le64 eop_count_in; - __le64 sop_count_out; - __le64 eop_count_out; - __le64 drop_counts[IONIC_BUFFER_DROP_MAX]; - __le64 input_queue_buffer_occupancy[IONIC_QOS_TC_MAX]; - __le64 input_queue_port_monitor[IONIC_QOS_TC_MAX]; - __le64 output_queue_port_monitor[IONIC_QOS_TC_MAX]; - __le64 oflow_drop_counts[IONIC_OFLOW_DROP_MAX]; - __le64 input_queue_good_pkts_in[IONIC_QOS_TC_MAX]; - __le64 input_queue_good_pkts_out[IONIC_QOS_TC_MAX]; - __le64 input_queue_err_pkts_in[IONIC_QOS_TC_MAX]; - __le64 input_queue_fifo_depth[IONIC_QOS_TC_MAX]; - __le64 input_queue_max_fifo_depth[IONIC_QOS_TC_MAX]; - __le64 input_queue_peak_occupancy[IONIC_QOS_TC_MAX]; - __le64 output_queue_buffer_occupancy[IONIC_QOS_TC_MAX]; +struct ionic_port_extra_stats { + __le64 rsfec_correctable_blocks; + __le64 rsfec_uncorrectable_blocks; + __le64 fec_corrected_bits_total; + __le64 rx_bits_phy; + __le64 fec_codeword_error_bin[16]; }; /** @@ -2950,7 +2910,7 @@ union ionic_port_identity { * @sprom_page2: Extended Transceiver sprom, page 2 * @sprom_page17: Extended Transceiver sprom, page 17 * @rsvd: reserved byte(s) - * @pb_stats: uplink pb drop stats + * @extra_stats: Extra port statistics data */ struct ionic_port_info { union ionic_port_config config; @@ -2968,9 +2928,7 @@ struct ionic_port_info { }; }; u8 rsvd[376]; - - /* pb_stats must start at 2k offset */ - struct ionic_port_pb_stats pb_stats; + struct ionic_port_extra_stats extra_stats; }; /* diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 637e635bbf03..fd3ee9820531 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -140,6 +140,7 @@ void ionic_lif_deferred_enqueue(struct ionic_lif *lif, static void ionic_link_status_check(struct ionic_lif *lif) { + struct ionic_dev *idev = &lif->ionic->idev; struct net_device *netdev = lif->netdev; u16 link_status; bool link_up; @@ -153,6 +154,8 @@ static void ionic_link_status_check(struct ionic_lif *lif) return; } + ionic_reset_link_down_count(idev); + link_status = le16_to_cpu(lif->info->status.link_status); link_up = link_status == IONIC_PORT_OPER_STATUS_UP; @@ -179,7 +182,6 @@ static void ionic_link_status_check(struct ionic_lif *lif) } } else { if (netif_carrier_ok(netdev)) { - lif->link_down_count++; netdev_info(netdev, "Link down\n"); netif_carrier_off(netdev); } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index 8e10f66dc50e..d34692462036 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -214,7 +214,6 @@ struct ionic_lif { bool registered; bool doorbell_wa; u16 lif_type; - unsigned int link_down_count; unsigned int nmcast; unsigned int nucast; unsigned int nvlans; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index 3c5200e2fdb7..6e6f3ed07271 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -731,6 +731,14 @@ int ionic_port_init(struct ionic *ionic) return -ENOMEM; } + /* If the driver knows about more "extra stats" than the firmware, + * make sure these stats are marked as invalid. + */ + memset(&idev->port_info->extra_stats, 0xff, + sizeof(idev->port_info->extra_stats)); + + WRITE_ONCE(idev->link_down_count_init, false); + sz = min(sizeof(ident->port.config), sizeof(idev->dev_cmd_regs->data)); mutex_lock(&ionic->dev_cmd_lock); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c index 0107599a9dd4..428d5cca930f 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c @@ -167,6 +167,7 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = { #define IONIC_NUM_PORT_STATS ARRAY_SIZE(ionic_port_stats_desc) #define IONIC_NUM_TX_STATS ARRAY_SIZE(ionic_tx_stats_desc) #define IONIC_NUM_RX_STATS ARRAY_SIZE(ionic_rx_stats_desc) +#define IONIC_NUM_EXTRA_PORT_STATS 1 #define MAX_Q(lif) ((lif)->netdev->real_num_tx_queues) @@ -232,6 +233,33 @@ static void ionic_get_lif_stats(struct ionic_lif *lif, stats->hw_tx_aborted_errors = ns.tx_aborted_errors; } +static u32 ionic_extra_port_stats_get_count(struct ionic_lif *lif) +{ + struct ionic_dev *idev = &lif->ionic->idev; + struct ionic_port_extra_stats *pes_cache; + u32 count = 0; + + if (!(lif->ionic->ident.dev.capabilities & + cpu_to_le64(IONIC_DEV_CAP_EXTRA_STATS))) + return count; + + pes_cache = &idev->port_extra_stats_cache; + /* Treat all of the extra port stats as invalid in subsequent calls if + * port_info isn't set; otherwise cache a valid snapshot for them. + */ + if (!idev->port_info) { + memset(pes_cache, 0xff, sizeof(*pes_cache)); + return count; + } + + *pes_cache = idev->port_info->extra_stats; + + if (pes_cache->rx_bits_phy != IONIC_STAT_INVALID) + count++; + + return count; +} + static u64 ionic_sw_stats_get_count(struct ionic_lif *lif) { u64 total = 0, tx_queues = MAX_Q(lif), rx_queues = MAX_Q(lif); @@ -243,7 +271,7 @@ static u64 ionic_sw_stats_get_count(struct ionic_lif *lif) rx_queues += 1; total += IONIC_NUM_LIF_STATS; - total += IONIC_NUM_PORT_STATS; + total += IONIC_NUM_PORT_STATS + ionic_extra_port_stats_get_count(lif); total += tx_queues * IONIC_NUM_TX_STATS; total += rx_queues * IONIC_NUM_RX_STATS; @@ -271,6 +299,20 @@ static void ionic_sw_stats_get_rx_strings(struct ionic_lif *lif, u8 **buf, ionic_rx_stats_desc[i].name); } +static void ionic_extra_port_stats_get_strings(struct ionic_lif *lif, u8 **buf) +{ + struct ionic_port_extra_stats *pes_cache; + + if (!(lif->ionic->ident.dev.capabilities & + cpu_to_le64(IONIC_DEV_CAP_EXTRA_STATS))) + return; + + pes_cache = &lif->ionic->idev.port_extra_stats_cache; + + if (pes_cache->rx_bits_phy != IONIC_STAT_INVALID) + ethtool_puts(buf, "rx_bits_phy"); +} + static void ionic_sw_stats_get_strings(struct ionic_lif *lif, u8 **buf) { int i, q_num; @@ -280,6 +322,7 @@ static void ionic_sw_stats_get_strings(struct ionic_lif *lif, u8 **buf) for (i = 0; i < IONIC_NUM_PORT_STATS; i++) ethtool_puts(buf, ionic_port_stats_desc[i].name); + ionic_extra_port_stats_get_strings(lif, buf); for (q_num = 0; q_num < MAX_Q(lif); q_num++) ionic_sw_stats_get_tx_strings(lif, buf, q_num); @@ -322,6 +365,25 @@ static void ionic_sw_stats_get_rxq_values(struct ionic_lif *lif, u64 **buf, } } +static void ionic_extra_port_stats_get_values(struct ionic_lif *lif, u64 **buf) +{ + struct ionic_port_extra_stats *pes_cache; + + if (!(lif->ionic->ident.dev.capabilities & + cpu_to_le64(IONIC_DEV_CAP_EXTRA_STATS))) + return; + + /* The number of statistics added to @buf here must equal + * ionic_extra_port_stats_get_count(). + */ + pes_cache = &lif->ionic->idev.port_extra_stats_cache; + + if (pes_cache->rx_bits_phy != IONIC_STAT_INVALID) { + **buf = le64_to_cpu(pes_cache->rx_bits_phy); + (*buf)++; + } +} + static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf) { struct ionic_port_stats *port_stats; @@ -341,6 +403,7 @@ static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf) &ionic_port_stats_desc[i]); (*buf)++; } + ionic_extra_port_stats_get_values(lif, buf); for (q_num = 0; q_num < MAX_Q(lif); q_num++) ionic_sw_stats_get_txq_values(lif, buf, q_num); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.h b/drivers/net/ethernet/pensando/ionic/ionic_stats.h index 2a725834f792..7ed935868e84 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.h @@ -4,6 +4,8 @@ #ifndef _IONIC_STATS_H_ #define _IONIC_STATS_H_ +#define IONIC_STAT_INVALID (cpu_to_le64(~0ULL)) + #define IONIC_STAT_TO_OFFSET(type, stat_name) (offsetof(type, stat_name)) #define IONIC_STAT_DESC(type, stat_name) { \ diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 39842eb73bc3..cb0ae0650905 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -76,20 +76,20 @@ enum qede_pci_private { }; static const struct pci_device_id qede_pci_tbl[] = { - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_40), QEDE_PRIVATE_PF}, - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_10), QEDE_PRIVATE_PF}, - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_MF), QEDE_PRIVATE_PF}, - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_100), QEDE_PRIVATE_PF}, - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_50), QEDE_PRIVATE_PF}, - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), QEDE_PRIVATE_PF}, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_40), .driver_data = QEDE_PRIVATE_PF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_10), .driver_data = QEDE_PRIVATE_PF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_MF), .driver_data = QEDE_PRIVATE_PF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_100), .driver_data = QEDE_PRIVATE_PF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_50), .driver_data = QEDE_PRIVATE_PF }, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), .driver_data = QEDE_PRIVATE_PF }, #ifdef CONFIG_QED_SRIOV - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_IOV), QEDE_PRIVATE_VF}, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_IOV), .driver_data = QEDE_PRIVATE_VF }, #endif - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_AH), QEDE_PRIVATE_PF}, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_AH), .driver_data = QEDE_PRIVATE_PF }, #ifdef CONFIG_QED_SRIOV - {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_AH_IOV), QEDE_PRIVATE_VF}, + { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_AH_IOV), .driver_data = QEDE_PRIVATE_VF }, #endif - { 0 } + { } }; MODULE_DEVICE_TABLE(pci, qede_pci_tbl); diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index ba7efb108637..57e1984d20ee 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -48,7 +48,7 @@ config QCA7000_UART will be called qcauart. config QCOM_EMAC - tristate "Qualcomm Technologies, Inc. EMAC Gigabit Ethernet support" + tristate "Qualcomm EMAC Gigabit Ethernet support" depends on HAS_DMA && HAS_IOMEM select CRC32 select PHYLIB @@ -61,7 +61,7 @@ config QCOM_EMAC Precision Clock Synchronization Protocol. config QCOM_PPE - tristate "Qualcomm Technologies, Inc. PPE Ethernet support" + tristate "Qualcomm PPE Ethernet support" depends on COMMON_CLK && HAS_IOMEM && OF depends on ARCH_QCOM || COMPILE_TEST select REGMAP_MMIO diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index a8532ebd42ec..8241bcf76664 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -235,44 +235,44 @@ static const struct { static const struct pci_device_id rtl8139_pci_tbl[] = { - {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, - {0x16ec, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + { PCI_DEVICE(0x10ec, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE(0x10ec, 0x8138), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1113, 0x1211), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1500, 0x1360), .driver_data = RTL8139 }, + { PCI_DEVICE(0x4033, 0x1360), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1186, 0x1300), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1186, 0x1340), .driver_data = RTL8139 }, + { PCI_DEVICE(0x13d1, 0xab06), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1259, 0xa117), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1259, 0xa11e), .driver_data = RTL8139 }, + { PCI_DEVICE(0x14ea, 0xab06), .driver_data = RTL8139 }, + { PCI_DEVICE(0x14ea, 0xab07), .driver_data = RTL8139 }, + { PCI_DEVICE(0x11db, 0x1234), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1432, 0x9130), .driver_data = RTL8139 }, + { PCI_DEVICE(0x02ac, 0x1012), .driver_data = RTL8139 }, + { PCI_DEVICE(0x018a, 0x0106), .driver_data = RTL8139 }, + { PCI_DEVICE(0x126c, 0x1211), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1743, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE(0x021b, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE(0x16ec, 0xab06), .driver_data = RTL8139 }, #ifdef CONFIG_SH_SECUREEDGE5410 /* Bogus 8139 silicon reports 8129 without external PROM :-( */ - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, + { PCI_DEVICE(0x10ec, 0x8129), .driver_data = RTL8139 }, #endif #ifdef CONFIG_8139TOO_8129 - {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 }, + { PCI_DEVICE(0x10ec, 0x8129), .driver_data = RTL8129 }, #endif /* some crazy cards report invalid vendor ids like * 0x0001 here. The other ids are valid and constant, * so we simply don't match on the main vendor id. */ - {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 }, - {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 }, + { PCI_DEVICE_SUB(PCI_ANY_ID, 0x8139, 0x10ec, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE_SUB(PCI_ANY_ID, 0x8139, 0x1186, 0x1300), .driver_data = RTL8139 }, + { PCI_DEVICE_SUB(PCI_ANY_ID, 0x8139, 0x13d1, 0xab06), .driver_data = RTL8139 }, - {0,} + { } }; MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 791277e750ba..ec4fc21fa21f 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -229,7 +229,7 @@ static const struct pci_device_id rtl8169_pci_tbl[] = { { PCI_VDEVICE(REALTEK, 0x2502) }, { PCI_VDEVICE(REALTEK, 0x2600) }, { PCI_VDEVICE(REALTEK, 0x8129) }, - { PCI_VDEVICE(REALTEK, 0x8136), RTL_CFG_NO_GBIT }, + { PCI_VDEVICE(REALTEK, 0x8136), .driver_data = RTL_CFG_NO_GBIT }, { PCI_VDEVICE(REALTEK, 0x8161) }, { PCI_VDEVICE(REALTEK, 0x8162) }, { PCI_VDEVICE(REALTEK, 0x8167) }, @@ -240,15 +240,15 @@ static const struct pci_device_id rtl8169_pci_tbl[] = { { PCI_VDEVICE(DLINK, 0x4302) }, { PCI_VDEVICE(AT, 0xc107) }, { PCI_VDEVICE(USR, 0x0116) }, - { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 }, - { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 }, + { PCI_VDEVICE_SUB(LINKSYS, 0x1032, PCI_ANY_ID, 0x0024) }, + { PCI_DEVICE_SUB(0x0001, 0x8168, PCI_ANY_ID, 0x2410) }, { PCI_VDEVICE(REALTEK, 0x8125) }, { PCI_VDEVICE(REALTEK, 0x8126) }, { PCI_VDEVICE(REALTEK, 0x8127) }, { PCI_VDEVICE(REALTEK, 0x3000) }, { PCI_VDEVICE(REALTEK, 0x5000) }, { PCI_VDEVICE(REALTEK, 0x0e10) }, - {} + { } }; MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h index b9209eb6ea73..9bd6872474c1 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase.h +++ b/drivers/net/ethernet/realtek/rtase/rtase.h @@ -153,6 +153,10 @@ enum rtase_registers { #define RTASE_FORCE_TXFLOW_EN BIT(10) #define RTASE_RX_CHKSUM BIT(5) + RTASE_GPHY_STD_00 = 0x6024, +#define RTASE_RXFLOW_EN BIT(7) +#define RTASE_TXFLOW_EN BIT(6) + RTASE_Q0_RX_DESC_ADDR0 = 0x00E4, RTASE_Q0_RX_DESC_ADDR4 = 0x00E8, RTASE_Q1_RX_DESC_ADDR0 = 0x4000, diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 55105d34bc79..255667775f0e 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -976,6 +976,9 @@ static void rtase_hw_config(struct net_device *dev) rtase_hw_set_features(dev, dev->features); /* enable flow control */ + reg_data16 = rtase_r16(tp, RTASE_GPHY_STD_00); + reg_data16 &= ~(RTASE_TXFLOW_EN | RTASE_RXFLOW_EN); + rtase_w16(tp, RTASE_GPHY_STD_00, reg_data16); reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD); reg_data16 |= (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN); rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index ff6ca7b2cd4f..84a55f2b48ff 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -40,8 +40,8 @@ static const char rocker_driver_name[] = "rocker"; static const struct pci_device_id rocker_pci_id_table[] = { - {PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER), 0}, - {0, } + { PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER) }, + { } }; struct rocker_wait { diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index 12ddea514910..ed5276d492b9 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -331,9 +331,9 @@ static const struct { }; static const struct pci_device_id sis190_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0190), 0, 0, 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x0191), 0, 0, 1 }, - { 0, }, + { PCI_VDEVICE(SI, 0x0190), .driver_data = 0 }, + { PCI_VDEVICE(SI, 0x0191), .driver_data = 1 }, + { }, }; MODULE_DEVICE_TABLE(pci, sis190_pci_tbl); diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c index 1349ef50cd26..6e9afd9042f7 100644 --- a/drivers/net/ethernet/sis/sis900.c +++ b/drivers/net/ethernet/sis/sis900.c @@ -104,11 +104,11 @@ static const char * card_names[] = { }; static const struct pci_device_id sis900_pci_tbl[] = { - {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_900}, - {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7016}, - {0,} + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_900), + .driver_data = SIS_900 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_7016), + .driver_data = SIS_7016 }, + { } }; MODULE_DEVICE_TABLE (pci, sis900_pci_tbl); diff --git a/drivers/net/ethernet/sis/sis900.h b/drivers/net/ethernet/sis/sis900.h index 66e4bf38770f..b20665fc37f9 100644 --- a/drivers/net/ethernet/sis/sis900.h +++ b/drivers/net/ethernet/sis/sis900.h @@ -315,7 +315,7 @@ enum sis630_revision_id { #define MAX_FRAME_SIZE (1518 + 4) #else #define MAX_FRAME_SIZE 1518 -#endif /* CONFIG_VLAN_802_1Q */ +#endif /* CONFIG_VLAN_8021Q */ #define TX_BUF_SIZE (MAX_FRAME_SIZE+18) #define RX_BUF_SIZE (MAX_FRAME_SIZE+18) diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c index 389659db06a8..60538b7fe684 100644 --- a/drivers/net/ethernet/smsc/epic100.c +++ b/drivers/net/ethernet/smsc/epic100.c @@ -166,11 +166,19 @@ static const struct epic_chip_info pci_id_tbl[] = { static const struct pci_device_id epic_pci_tbl[] = { - { 0x10B8, 0x0005, 0x1092, 0x0AB4, 0, 0, SMSC_83C170_0 }, - { 0x10B8, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C170 }, - { 0x10B8, 0x0006, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, SMSC_83C175 }, - { 0,} + { + PCI_DEVICE_SUB(0x10B8, 0x0005, 0x1092, 0x0AB4), + .driver_data = SMSC_83C170_0, + }, { + PCI_DEVICE(0x10B8, 0x0005), + .driver_data = SMSC_83C170, + }, { + PCI_DEVICE(0x10B8, 0x0006), + .class = PCI_CLASS_NETWORK_ETHERNET << 8, + .class_mask = 0xffff00, + .driver_data = SMSC_83C175, + }, + { } }; MODULE_DEVICE_TABLE (pci, epic_pci_tbl); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c index 16b955a6d77b..b1ea248e3311 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c @@ -26,6 +26,7 @@ struct starfive_dwmac_data { struct starfive_dwmac { struct device *dev; const struct starfive_dwmac_data *data; + struct clk *sgmii_rx; }; static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) @@ -68,6 +69,25 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) return 0; } +static int stmmac_starfive_sgmii_set_clk_rate(void *bsp_priv, struct clk *clk_tx_i, + phy_interface_t __maybe_unused interface, + int speed) +{ + struct starfive_dwmac *dwmac = bsp_priv; + long rate = rgmii_clock(speed); + int ret; + + /* MAC clock rate the same as RGMII */ + if (rate < 0) + return -EINVAL; + + ret = clk_set_rate(clk_tx_i, rate); + if (ret) + return ret; + + return clk_set_rate(dwmac->sgmii_rx, rate); +} + static int starfive_dwmac_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat_dat; @@ -102,23 +122,34 @@ static int starfive_dwmac_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx), "error getting gtx clock\n"); - /* Generally, the rgmii_tx clock is provided by the internal clock, - * which needs to match the corresponding clock frequency according - * to different speeds. If the rgmii_tx clock is provided by the - * external rgmii_rxin, there is no need to configure the clock - * internally, because rgmii_rxin will be adaptively adjusted. - */ - if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) - plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate; - dwmac->dev = &pdev->dev; - plat_dat->flags |= STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP; plat_dat->bsp_priv = dwmac; - plat_dat->dma_cfg->dche = true; + /* generic sgmii, 1000_BASEX not support yet */ + if (plat_dat->phy_interface == PHY_INTERFACE_MODE_SGMII) { + dwmac->sgmii_rx = devm_clk_get_enabled(&pdev->dev, "sgmii_rx"); + if (IS_ERR(dwmac->sgmii_rx)) + return dev_err_probe(&pdev->dev, + PTR_ERR(dwmac->sgmii_rx), + "error getting sgmii rx clock\n"); + plat_dat->set_clk_tx_rate = stmmac_starfive_sgmii_set_clk_rate; + } else { + /* + * Generally, the rgmii_tx clock is provided by the internal clock, + * which needs to match the corresponding clock frequency according + * to different speeds. If the rgmii_tx clock is provided by the + * external rgmii_rxin, there is no need to configure the clock + * internally, because rgmii_rxin will be adaptively adjusted. + */ + if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) + plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate; - err = starfive_dwmac_set_mode(plat_dat); - if (err) - return err; + err = starfive_dwmac_set_mode(plat_dat); + if (err) + return err; + } + + plat_dat->flags |= (STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP | STMMAC_FLAG_SPH_DISABLE); + plat_dat->dma_cfg->dche = true; return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index ffcd036d4c02..6382836828ba 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -198,6 +198,7 @@ enum power_event { #define GMAC_HW_FEAT_AVSEL BIT(20) #define GMAC_HW_TSOEN BIT(18) #define GMAC_HW_FEAT_SPHEN BIT(17) +#define GMAC_HW_FEAT_DCBEN BIT(16) #define GMAC_HW_ADDR64 GENMASK(15, 14) #define GMAC_HW_TXFIFOSIZE GENMASK(10, 6) #define GMAC_HW_RXFIFOSIZE GENMASK(4, 0) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 28728271fbc9..829a23bdad01 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -391,6 +391,7 @@ static int dwmac4_get_hw_feature(void __iomem *ioaddr, dma_cap->av = (hw_cap & GMAC_HW_FEAT_AVSEL) >> 20; dma_cap->tsoen = (hw_cap & GMAC_HW_TSOEN) >> 18; dma_cap->sphen = (hw_cap & GMAC_HW_FEAT_SPHEN) >> 17; + dma_cap->dcben = (hw_cap & GMAC_HW_FEAT_DCBEN) >> 16; dma_cap->addr64 = (hw_cap & GMAC_HW_ADDR64) >> 14; switch (dma_cap->addr64) { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 51943705a2b0..61b6d45a02f5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -379,9 +379,9 @@ #define XGMAC_RIE BIT(6) #define XGMAC_TBUE BIT(2) #define XGMAC_TIE BIT(0) -#define XGMAC_DMA_INT_DEFAULT_EN (XGMAC_NIE | XGMAC_AIE | XGMAC_RBUE | \ +#define XGMAC_DMA_INT_DEFAULT_EN (XGMAC_NIE | XGMAC_AIE | \ XGMAC_RIE | XGMAC_TIE) -#define XGMAC_DMA_INT_DEFAULT_RX (XGMAC_RBUE | XGMAC_RIE) +#define XGMAC_DMA_INT_DEFAULT_RX (XGMAC_RIE) #define XGMAC_DMA_INT_DEFAULT_TX (XGMAC_TIE) #define XGMAC_DMA_CH_Rx_WATCHDOG(x) (0x0000313c + (0x80 * (x))) #define XGMAC_RWT GENMASK(7, 0) @@ -429,6 +429,8 @@ #define XGMAC_TDES3_VLTV BIT(16) #define XGMAC_TDES3_VT GENMASK(15, 0) #define XGMAC_TDES3_FL GENMASK(14, 0) +#define XGMAC_RDES2_L4FM BIT(28) +#define XGMAC_RDES2_L3FM BIT(27) #define XGMAC_RDES2_HL GENMASK(9, 0) #define XGMAC_RDES3_OWN BIT(31) #define XGMAC_RDES3_CTXT BIT(30) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c index b5f200a87484..6719ac6e395b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c @@ -27,6 +27,7 @@ static int dwxgmac2_get_rx_status(struct stmmac_extra_stats *x, struct dma_desc *p) { u32 rdes3 = le32_to_cpu(p->des3); + u32 rdes2 = le32_to_cpu(p->des2); if (unlikely(rdes3 & XGMAC_RDES3_OWN)) return dma_own; @@ -37,6 +38,11 @@ static int dwxgmac2_get_rx_status(struct stmmac_extra_stats *x, if (unlikely((rdes3 & XGMAC_RDES3_ES) && (rdes3 & XGMAC_RDES3_LD))) return discard_frame; + if (rdes2 & XGMAC_RDES2_L3FM) + x->l3_filter_match++; + if (rdes2 & XGMAC_RDES2_L4FM) + x->l4_filter_match++; + return good_frame; } diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c index 1b3b114e7bec..d81581dfa1a7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c @@ -479,8 +479,6 @@ static void dwxgmac_mmc_read(void __iomem *mmcaddr, struct stmmac_counters *mmc) &mmc->mmc_rx_multicastframe_g); dwxgmac_read_mmc_reg(mmcaddr, MMC_XGMAC_RX_CRC_ERR, &mmc->mmc_rx_crc_error); - dwxgmac_read_mmc_reg(mmcaddr, MMC_XGMAC_RX_CRC_ERR, - &mmc->mmc_rx_crc_error); mmc->mmc_rx_run_error += readl(mmcaddr + MMC_XGMAC_RX_RUNT_ERR); mmc->mmc_rx_jabber_error += readl(mmcaddr + MMC_XGMAC_RX_JABBER_ERR); mmc->mmc_rx_undersize_g += readl(mmcaddr + MMC_XGMAC_RX_UNDER); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3591755ea30b..2a0d7eff88d3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3341,12 +3341,14 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) * Try to cancel any timer if napi is scheduled, timer will be armed * again in the next scheduled napi. */ - if (unlikely(!napi_is_scheduled(napi))) - hrtimer_start(&tx_q->txtimer, - STMMAC_COAL_TIMER(tx_coal_timer), - HRTIMER_MODE_REL); - else + if (unlikely(!napi_is_scheduled(napi))) { + if (unlikely(!(hrtimer_active(&tx_q->txtimer)))) + hrtimer_start(&tx_q->txtimer, + STMMAC_COAL_TIMER(tx_coal_timer), + HRTIMER_MODE_REL); + } else { hrtimer_try_to_cancel(&tx_q->txtimer); + } } /** @@ -4140,7 +4142,7 @@ static int __stmmac_open(struct net_device *dev, u8 chan; int ret; - for (int i = 0; i < MTL_MAX_TX_QUEUES; i++) + for (int i = 0; i < priv->plat->tx_queues_to_use; i++) if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_EN) dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs; memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 5cae2aa72906..dc5f951a311d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -156,8 +156,8 @@ static int stmmac_mtl_setup(struct platform_device *pdev, /* Processing RX queues common config */ if (!of_property_read_u32(rx_node, "snps,rx-queues-to-use", &value)) { - if (value > U8_MAX) - value = U8_MAX; + if (value > MTL_MAX_RX_QUEUES) + value = MTL_MAX_RX_QUEUES; plat->rx_queues_to_use = value; } @@ -210,8 +210,8 @@ static int stmmac_mtl_setup(struct platform_device *pdev, /* Processing TX queues common config */ if (!of_property_read_u32(tx_node, "snps,tx-queues-to-use", &value)) { - if (value > U8_MAX) - value = U8_MAX; + if (value > MTL_MAX_TX_QUEUES) + value = MTL_MAX_TX_QUEUES; plat->tx_queues_to_use = value; } diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index fe00e7dd3fe4..9a8b5cf2d648 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -218,11 +218,9 @@ static u16 link_modes[] = { }; static const struct pci_device_id cas_pci_tbl[] = { - { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_CASSINI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SATURN, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { 0, } + { PCI_VDEVICE(SUN, PCI_DEVICE_ID_SUN_CASSINI) }, + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SATURN) }, + { } }; MODULE_DEVICE_TABLE(pci, cas_pci_tbl); @@ -1029,7 +1027,7 @@ static int cas_pcs_link_check(struct cas *cp) * point a bit earlier in the sequence. If we had * generated a reset a short time ago, we'll wait for * the link timer to check the status until a - * timer expires (link_transistion_jiffies_valid is + * timer expires (link_transition_jiffies_valid is * true when the timer is running.) Instead of using * a system timer, we just do a check whenever the * link timer is running - this clears the flag after @@ -4547,7 +4545,7 @@ static int cas_get_link_ksettings(struct net_device *dev, } if (linkstate != link_up) { /* Force these to "unknown" if the link is not up and - * autonogotiation in enabled. We can set the link + * autonegotiation is enabled. We can set the link * speed to 0, but not cmd->duplex, * because its legal values are 0 and 1. Ethtool will * print the value reported in parentheses after the @@ -4799,7 +4797,7 @@ static void cas_program_bridge(struct pci_dev *cas_pdev) */ pci_write_config_word(pdev, 0x50, (5 << 10) | 0x3ff); - /* The Read Prefecth Policy register is 16-bit and sits at + /* The Read Prefetch Policy register is 16-bit and sits at * offset 0x52. It enables a "smart" pre-fetch policy. We * enable it and max out all of the settings since only one * device is sitting underneath and thus bandwidth sharing is @@ -4906,7 +4904,7 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* * On some architectures, the default cache line size set - * by pci_try_set_mwi reduces perforamnce. We have to increase + * by pci_try_set_mwi reduces performance. We have to increase * it for this case. To start, we'll print some configuration * data. */ diff --git a/drivers/net/ethernet/sun/cassini.h b/drivers/net/ethernet/sun/cassini.h index 2d91f4936d52..0c24547a4534 100644 --- a/drivers/net/ethernet/sun/cassini.h +++ b/drivers/net/ethernet/sun/cassini.h @@ -259,7 +259,7 @@ /* output enables are provided for each device's chip select and for the rest * of the outputs from cassini to its local bus devices. two sw programmable - * bits are connected to general purpus control/status bits. + * bits are connected to general purpose control/status bits. * DEFAULT: 0x7 */ #define REG_BIM_LOCAL_DEV_EN 0x1020 /* BIM local device @@ -404,7 +404,7 @@ GMII on SERDES pins for monitoring. */ #define SATURN_PCFG_FSI 0x00000200 /* 1 = freeze serdes/gmii. all - pins configed as outputs. + pins configured as outputs. for power saving when using internal phy. */ #define SATURN_PCFG_LAD 0x00000800 /* 0 = mac core led ctrl @@ -622,7 +622,7 @@ enabled */ #define RX_CFG_SWIVEL_MASK 0x00001C00 /* byte offset of the 1st data byte of the packet - w/in 8 byte boundares. + w/in 8 byte boundaries. this swivels the data DMA'ed to header buffers, jumbo buffers @@ -1248,7 +1248,7 @@ */ #define REG_MAC_TX_STATUS 0x6010 /* TX MAC status reg */ #define MAC_TX_FRAME_XMIT 0x0001 /* successful frame - transmision */ + transmission */ #define MAC_TX_UNDERRUN 0x0002 /* terminated frame transmission due to data starvation in the @@ -1414,7 +1414,7 @@ * when passed to the host. to ensure proper operation, need to wait 3.2ms * after clearing RX_CFG_EN before writing to any other RX MAC registers * or other MAC parameters. alternatively, poll RX_CFG_EN until it clears - * to 0. similary, HASH_FILTER_EN and ADDR_FILTER_EN have the same + * to 0. Similarly, HASH_FILTER_EN and ADDR_FILTER_EN have the same * restrictions as CFG_EN. */ #define REG_MAC_RX_CFG 0x6034 /* RX MAC config reg */ @@ -1670,7 +1670,7 @@ * programmed in frame mode. load this register w/ a valid instruction * (as per IEEE 802.3u MII spec). poll this register to check for instruction * execution completion. during a read operation, this register will also - * contain the 16-bit data returned by the tranceiver. unless specified + * contain the 16-bit data returned by the transceiver. unless specified * otherwise, fields are considered "don't care" when polling for * completion. */ @@ -1734,7 +1734,7 @@ #define MIF_CFG_POLL_REG_SHIFT 3 #define MIF_CFG_MDIO_0 0x0100 /* (ro) dual purpose. when MDIO_0 is idle, - 1 -> tranceiver is + 1 -> transceiver is connected to MDIO_0. when MIF is communicating w/ MDIO_0 in bit-bang @@ -1750,7 +1750,7 @@ mode, this bit indicates the incoming bit stream during a read op */ -#define MIF_CFG_POLL_PHY_MASK 0x7C00 /* tranceiver address to +#define MIF_CFG_POLL_PHY_MASK 0x7C00 /* transceiver address to be polled */ #define MIF_CFG_POLL_PHY_SHIFT 10 diff --git a/drivers/net/ethernet/sun/sunbmac.h b/drivers/net/ethernet/sun/sunbmac.h index d379bd407eca..85778edcf8dc 100644 --- a/drivers/net/ethernet/sun/sunbmac.h +++ b/drivers/net/ethernet/sun/sunbmac.h @@ -205,7 +205,7 @@ #define FRAME_WRITE 0x50020000 #define FRAME_READ 0x60020000 -/* Tranceiver registers. */ +/* Transceiver registers. */ #define TCVR_PAL_SERIAL 0x00000001 /* Enable serial mode */ #define TCVR_PAL_EXTLBACK 0x00000002 /* Enable external loopback */ #define TCVR_PAL_MSENSE 0x00000004 /* Media sense */ diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 8e69d917d827..b56a0d4cdb12 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -85,8 +85,7 @@ MODULE_LICENSE("GPL"); #define GEM_MODULE_NAME "gem" static const struct pci_device_id gem_pci_tbl[] = { - { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_GEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VDEVICE(SUN, PCI_DEVICE_ID_SUN_GEM) }, /* These models only differ from the original GEM in * that their tx/rx fifos are of a different size and @@ -95,21 +94,14 @@ static const struct pci_device_id gem_pci_tbl[] = { * Apple's GMAC does support gigabit on machines with * the BCM54xx PHYs. -BenH */ - { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_RIO_GEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID2_GMAC, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - {0, } + { PCI_VDEVICE(SUN, PCI_DEVICE_ID_SUN_RIO_GEM) }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC) }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP) }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2) }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_K2_GMAC) }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM) }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_IPID2_GMAC) }, + { } }; MODULE_DEVICE_TABLE(pci, gem_pci_tbl); @@ -2187,7 +2179,7 @@ static void gem_do_stop(struct net_device *dev, int wol) * if we did. This is not an issue however as the reset * task is synchronized vs. us (rtnl_lock) and will do * nothing if the device is down or suspended. We do - * still clear reset_task_pending to avoid a spurrious + * still clear reset_task_pending to avoid a spurious * reset later on in case we do resume before it gets * scheduled. */ @@ -2370,7 +2362,7 @@ static int __maybe_unused gem_resume(struct device *dev_d) gem_do_start(dev); /* If we had WOL enabled, the cell clock was never turned off during - * sleep, so we end up beeing unbalanced. Fix that here + * sleep, so we end up being unbalanced. Fix that here */ if (gp->asleep_wol) gem_put_cell(gp); diff --git a/drivers/net/ethernet/sun/sungem.h b/drivers/net/ethernet/sun/sungem.h index 626302a9bc89..b921d3074017 100644 --- a/drivers/net/ethernet/sun/sungem.h +++ b/drivers/net/ethernet/sun/sungem.h @@ -352,7 +352,7 @@ #define MAC_HASH14 0x60F8UL /* Hash Table 14 Register */ #define MAC_HASH15 0x60FCUL /* Hash Table 15 Register */ #define MAC_NCOLL 0x6100UL /* Normal Collision Counter */ -#define MAC_FASUCC 0x6104UL /* First Attmpt. Succ Coll Ctr. */ +#define MAC_FASUCC 0x6104UL /* First Attempt. Succ Coll Ctr.*/ #define MAC_ECOLL 0x6108UL /* Excessive Collision Counter */ #define MAC_LCOLL 0x610CUL /* Late Collision Counter */ #define MAC_DTIMER 0x6110UL /* Defer Timer */ @@ -657,7 +657,7 @@ /* MIF Frame/Output Register. This 32-bit register allows the host to * communicate with a transceiver in frame mode (as opposed to big-bang - * mode). Writes by the host specify an instrution. After being issued + * mode). Writes by the host specify an instruction. After being issued * the host must poll this register for completion. Also, after * completion this register holds the data returned by the transceiver * if applicable. diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index 4c9d5d4dd8a0..a70f704eb9f3 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -1118,7 +1118,7 @@ static void happy_meal_transceiver_check(struct happy_meal *hp, void __iomem *tr * * We use skb_reserve() to align the data block we get in the skb. We * also program the etxregs->cfg register to use an offset of 2. This - * imperical constant plus the ethernet header size will always leave + * empirical constant plus the ethernet header size will always leave * us with a nicely aligned ip header once we pass things up to the * protocol layers. * diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 7ac75fc8cdcf..434a31080855 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -302,7 +302,7 @@ static int am65_cpsw_nuss_ndo_slave_add_vid(struct net_device *ndev, { struct am65_cpsw_common *common = am65_ndev_to_common(ndev); struct am65_cpsw_port *port = am65_ndev_to_port(ndev); - u32 port_mask, unreg_mcast = 0; + u32 port_mask; int ret; if (!common->is_emac_mode) @@ -316,11 +316,9 @@ static int am65_cpsw_nuss_ndo_slave_add_vid(struct net_device *ndev, return ret; port_mask = BIT(port->port_id) | ALE_PORT_HOST; - if (!vid) - unreg_mcast = port_mask; dev_info(common->dev, "Adding vlan %d to vlan filter\n", vid); ret = cpsw_ale_vlan_add_modify(common->ale, vid, port_mask, - unreg_mcast, port_mask, 0); + 0, port_mask, 0); pm_runtime_put(common->dev); return ret; diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c index c5be359f3c66..b6e25a4658d1 100644 --- a/drivers/net/ethernet/ti/cpsw_new.c +++ b/drivers/net/ethernet/ti/cpsw_new.c @@ -2050,7 +2050,7 @@ static int cpsw_probe(struct platform_device *pdev) ret = cpsw_register_ports(cpsw); if (ret) - goto clean_unregister_notifiers; + goto clean_unregister_devlink; dev_notice(dev, "initialized (regs %pa, pool size %d) hw_ver:%08X %d.%d (%d)\n", &ss_res->start, descs_pool_size, @@ -2062,6 +2062,8 @@ static int cpsw_probe(struct platform_device *pdev) return 0; +clean_unregister_devlink: + cpsw_unregister_devlink(cpsw); clean_unregister_notifiers: cpsw_unregister_notifiers(cpsw); clean_cpts: diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c index a28a608f9bf4..82ddef9c17d5 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_common.c +++ b/drivers/net/ethernet/ti/icssg/icssg_common.c @@ -105,6 +105,7 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac, struct xdp_desc xdp_desc; int num_tx = 0, pkt_len; int descs_avail, ret; + u32 dst_tag_id; u32 *epib; int i; @@ -137,9 +138,17 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac, epib[0] = 0; epib[1] = 0; cppi5_hdesc_set_pktlen(host_desc, pkt_len); - cppi5_desc_set_tags_ids(&host_desc->hdr, 0, - (emac->port_id | (q_idx << 8))); + dst_tag_id = emac->port_id | (q_idx << 8); + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_DUP)) + dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG; + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_TAG_INS)) + epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS; + + cppi5_desc_set_tags_ids(&host_desc->hdr, 0, dst_tag_id); k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &dma_buf); cppi5_hdesc_attach_buf(host_desc, dma_buf, pkt_len, dma_buf, pkt_len); @@ -696,6 +705,7 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac, dma_addr_t desc_dma, buf_dma; struct prueth_swdata *swdata; struct page *page; + u32 dst_tag_id; u32 *epib; int ret; @@ -737,9 +747,25 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac, /* set dst tag to indicate internal qid at the firmware which is at * bit8..bit15. bit0..bit7 indicates port num for directed - * packets in case of switch mode operation + * packets in case of switch mode operation and port num 0 + * for undirected packets in case of HSR offload mode. + * + * XDP_TX frames arrive on a slave port with the HSR tag already + * stripped by the PRU firmware. Like skb TX via hsr0, they must + * be sent as undirected so the PRU duplicates them to both ports + * and re-inserts the HSR sequence tag. */ - cppi5_desc_set_tags_ids(&first_desc->hdr, 0, (emac->port_id | (q_idx << 8))); + dst_tag_id = emac->port_id | (q_idx << 8); + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_DUP)) + dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG; + + if (emac->prueth->is_hsr_offload_mode && + (ndev->features & NETIF_F_HW_HSR_TAG_INS)) + epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS; + + cppi5_desc_set_tags_ids(&first_desc->hdr, 0, dst_tag_id); k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma); cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len); swdata = cppi5_hdesc_get_swdata(first_desc); @@ -927,6 +953,7 @@ static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id, struct cppi5_host_desc_t *desc_rx; struct prueth_swdata *swdata; dma_addr_t desc_dma, buf_dma; + int avail_desc, alloc_budget; struct xdp_buff *xdp; int xdp_status = 0; int count = 0; @@ -993,16 +1020,13 @@ static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id, if (xdp_status & ICSSG_XDP_REDIR) xdp_do_flush(); - /* Allocate xsk buffers from the pool for the "count" number of - * packets processed in order to be able to receive more packets. - */ - ret = prueth_rx_alloc_zc(emac, count); + avail_desc = k3_cppi_desc_pool_avail(rx_chn->desc_pool); + alloc_budget = min_t(int, budget, avail_desc); + + ret = prueth_rx_alloc_zc(emac, alloc_budget); if (xsk_uses_need_wakeup(rx_chn->xsk_pool)) { - /* If the user space doesn't provide enough buffers then it must - * explicitly wake up the kernel when new buffers are available - */ - if (ret < count) + if (ret < alloc_budget) xsk_set_rx_need_wakeup(rx_chn->xsk_pool); else xsk_clear_rx_need_wakeup(rx_chn->xsk_pool); diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index a55b0f951181..274a1837025c 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -119,33 +119,20 @@ static struct board { }; static const struct pci_device_id tlan_pci_tbl[] = { - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL10, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETFLEX3I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_THUNDER, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETFLEX3B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100PI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, - { PCI_VENDOR_ID_OLICOM, PCI_DEVICE_ID_OLICOM_OC2183, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { PCI_VENDOR_ID_OLICOM, PCI_DEVICE_ID_OLICOM_OC2325, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, - { PCI_VENDOR_ID_OLICOM, PCI_DEVICE_ID_OLICOM_OC2326, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_T2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, - { 0,} + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL10), .driver_data = 0 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100), .driver_data = 1 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETFLEX3I), .driver_data = 2 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_THUNDER), .driver_data = 3 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETFLEX3B), .driver_data = 4 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100PI), .driver_data = 5 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100D), .driver_data = 6 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100I), .driver_data = 7 }, + { PCI_VDEVICE(OLICOM, PCI_DEVICE_ID_OLICOM_OC2183), .driver_data = 8 }, + { PCI_VDEVICE(OLICOM, PCI_DEVICE_ID_OLICOM_OC2325), .driver_data = 9 }, + { PCI_VDEVICE(OLICOM, PCI_DEVICE_ID_OLICOM_OC2326), .driver_data = 10 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100), .driver_data = 11 }, + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_T2), .driver_data = 12 }, + { } }; MODULE_DEVICE_TABLE(pci, tlan_pci_tbl); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 2451f6b20b11..260e14d5d541 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -2520,6 +2520,7 @@ int wx_sw_init(struct wx *wx) mutex_init(&wx->reset_lock); bitmap_zero(wx->state, WX_STATE_NBITS); bitmap_zero(wx->flags, WX_PF_FLAGS_NBITS); + set_bit(WX_STATE_DOWN, wx->state); wx->misc_irq_domain = false; return 0; @@ -2875,7 +2876,7 @@ void wx_update_stats(struct wx *wx) u64 restart_queue = 0, tx_busy = 0; u32 i; - if (!netif_running(wx->netdev) || + if (test_bit(WX_STATE_DOWN, wx->state) || test_bit(WX_STATE_RESETTING, wx->state)) return; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 746623fa59b4..d042567b8128 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -876,7 +876,7 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector, if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - netif_running(tx_ring->netdev)) { + !test_bit(WX_STATE_DOWN, wx->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; @@ -964,7 +964,7 @@ static int wx_poll(struct napi_struct *napi, int budget) if (likely(napi_complete_done(napi, work_done))) { if (wx->adaptive_itr) wx_update_dim_sample(q_vector); - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) wx_intr_enable(wx, WX_INTR_Q(q_vector->v_idx)); } @@ -2341,6 +2341,8 @@ int wx_init_interrupt_scheme(struct wx *wx) wx_cache_ring_rss(wx); + set_bit(WX_STATE_DOWN, wx->state); + return 0; } EXPORT_SYMBOL(wx_init_interrupt_scheme); @@ -3314,7 +3316,8 @@ EXPORT_SYMBOL(wx_set_ring); void wx_service_event_schedule(struct wx *wx) { - if (!test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state)) + if (!test_bit(WX_STATE_DOWN, wx->state) && + !test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state)) queue_work(system_power_efficient_wq, &wx->service_task); } EXPORT_SYMBOL(wx_service_event_schedule); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c index a360b06a086a..0152004a2dd3 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c @@ -898,7 +898,7 @@ static void wx_set_vf_link_state(struct wx *wx, int vf, int state) wx->vfinfo[vf].link_state = state; switch (state) { case IFLA_VF_LINK_STATE_AUTO: - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) wx->vfinfo[vf].link_enable = true; else wx->vfinfo[vf].link_enable = false; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 0da5565ee4ff..c7befe4cdfe9 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1202,6 +1202,7 @@ struct wx_last_stats { }; enum wx_state { + WX_STATE_DOWN, WX_STATE_RESETTING, WX_STATE_SWFW_BUSY, WX_STATE_PTP_RUNNING, diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c index 94ff8f5f0b4c..0d2db8d38cd5 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c @@ -68,7 +68,7 @@ static irqreturn_t wx_msix_misc_vf(int __always_unused irq, void *data) set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags); /* Clear the interrupt */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) wr32(wx, WX_VXIMC, wx->eims_other); return IRQ_HANDLED; @@ -278,6 +278,7 @@ static void wxvf_up_complete(struct wx *wx) wx_configure_msix_vf(wx); smp_mb__before_atomic(); + clear_bit(WX_STATE_DOWN, wx->state); wx_napi_enable_all(wx); /* clear any pending interrupts, may auto mask */ @@ -327,6 +328,9 @@ static void wxvf_down(struct wx *wx) { struct net_device *netdev = wx->netdev; + if (test_and_set_bit(WX_STATE_DOWN, wx->state)) + return; + timer_delete_sync(&wx->service_timer); netif_tx_stop_all_queues(netdev); netif_tx_disable(netdev); @@ -360,7 +364,7 @@ static void wxvf_reset_subtask(struct wx *wx) rtnl_lock(); if (test_bit(WX_STATE_RESETTING, wx->state) || - !(netif_running(wx->netdev))) { + test_bit(WX_STATE_DOWN, wx->state)) { rtnl_unlock(); return; } diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index d8e3827a8b1f..8678c49b892a 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -30,20 +30,20 @@ char ngbe_driver_name[] = "ngbe"; * Class, Class Mask, private data (not used) } */ static const struct pci_device_id ngbe_pci_tbl[] = { - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL_W), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2S), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4S), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2S), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4S), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860LC), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1), 0}, - { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1L), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL_W) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2S) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4S) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2S) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4S) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860LC) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1) }, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1L) }, /* required last entry */ - { .device = 0 } + { } }; /** @@ -204,7 +204,7 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data) /* shared interrupt alert! * the interrupt that we masked before the EICR read. */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, true); return IRQ_NONE; /* Not our interrupt */ } @@ -220,7 +220,7 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data) /* would disable interrupts here but it is auto disabled */ napi_schedule_irqoff(&q_vector->napi); - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, false); return IRQ_HANDLED; @@ -235,7 +235,7 @@ static irqreturn_t __ngbe_msix_misc(struct wx *wx, u32 eicr) wx_ptp_check_pps_event(wx); /* re-enable the original interrupt state, no lsc, no queues */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, false); return IRQ_HANDLED; @@ -262,7 +262,7 @@ static irqreturn_t ngbe_misc_and_queue(int __always_unused irq, void *data) /* queue */ q_vector = wx->q_vector[0]; napi_schedule_irqoff(&q_vector->napi); - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, true); return IRQ_HANDLED; } @@ -363,6 +363,9 @@ static void ngbe_disable_device(struct wx *wx) struct net_device *netdev = wx->netdev; u32 i; + if (test_and_set_bit(WX_STATE_DOWN, wx->state)) + return; + if (wx->num_vfs) { /* Clear EITR Select mapping */ wr32(wx, WX_PX_ITRSEL, 0); @@ -401,8 +404,6 @@ static void ngbe_disable_device(struct wx *wx) wr32(wx, WX_PX_TR_CFG(reg_idx), WX_PX_TR_CFG_SWFLSH); } - - wx_update_stats(wx); } static void ngbe_reset(struct wx *wx) @@ -428,6 +429,7 @@ void ngbe_up(struct wx *wx) /* make sure to complete pre-operations */ smp_mb__before_atomic(); + clear_bit(WX_STATE_DOWN, wx->state); wx_napi_enable_all(wx); /* enable transmits */ netif_tx_start_all_queues(wx->netdev); diff --git a/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c b/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c index 6ef43adcc425..d79cf8d8484f 100644 --- a/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c +++ b/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c @@ -26,20 +26,20 @@ * Class, Class Mask, private data (not used) } */ static const struct pci_device_id ngbevf_pci_tbl[] = { - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL_W), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A2), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A2S), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A4), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A4S), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL2), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL2S), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL4), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL4S), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860NCSI), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A1), 0}, - { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL1), 0}, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL_W) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A2) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A2S) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A4) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A4S) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL2) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL2S) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL4) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL4S) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860NCSI) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860A1) }, + { PCI_VDEVICE(WANGXUN, NGBEVF_DEV_ID_EM_WX1860AL1) }, /* required last entry */ - { .device = 0 } + { } }; static const struct net_device_ops ngbevf_netdev_ops = { diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index 8fc32df8e49a..affea1a364ef 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -186,19 +186,15 @@ static void txgbe_get_mac_link(struct wx *wx, int *speed) *speed = SPEED_UNKNOWN; } -int txgbe_set_phy_link(struct wx *wx) +void txgbe_set_phy_link(struct wx *wx) { int speed, autoneg, duplex, err; txgbe_get_link_capabilities(wx, &speed, &autoneg, &duplex); err = txgbe_set_phy_link_hostif(wx, speed, autoneg, duplex); - if (err) { + if (err) wx_err(wx, "Failed to setup link\n"); - return err; - } - - return 0; } static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) @@ -519,6 +515,7 @@ int txgbe_phylink_init_aml(struct txgbe *txgbe) err = phylink_set_fixed_link(phylink, &state); if (err) { wx_err(wx, "Failed to set fixed link\n"); + phylink_destroy(phylink); return err; } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h index 4f6df0ee860b..379c74ad19c6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h @@ -10,7 +10,7 @@ int txgbe_test_hostif(struct wx *wx); int txgbe_read_eeprom_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer, u32 length, u8 *data); -int txgbe_set_phy_link(struct wx *wx); +void txgbe_set_phy_link(struct wx *wx); int txgbe_identify_module(struct wx *wx); void txgbe_setup_link(struct wx *wx); int txgbe_phylink_init_aml(struct txgbe *txgbe); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c index aa14958d439a..8746318ad3bc 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -141,7 +141,7 @@ static irqreturn_t txgbe_misc_irq_handle(int irq, void *data) /* shared interrupt alert! * the interrupt that we masked before the ICR read. */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) txgbe_irq_enable(wx, true); return IRQ_NONE; /* Not our interrupt */ } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 8b7c3753bb6a..ce82e13aa8ae 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -37,16 +37,16 @@ char txgbe_driver_name[] = "txgbe"; * Class, Class Mask, private data (not used) } */ static const struct pci_device_id txgbe_pci_tbl[] = { - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_SP1000), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_WX1820), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5010), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5110), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5025), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5125), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5040), 0}, - { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5140), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_SP1000) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_WX1820) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5010) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5110) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5025) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5125) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5040) }, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5140) }, /* required last entry */ - { .device = 0 } + { } }; #define DEFAULT_DEBUG_LEVEL_SHIFT 3 @@ -93,31 +93,23 @@ static void txgbe_module_detection_subtask(struct wx *wx) { int err; - if (!test_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags)) + if (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags)) return; /* wait for SFF module ready */ msleep(200); err = txgbe_identify_module(wx); - if (err) - return; - - clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags); + if (err == -ENODEV) + set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags); } static void txgbe_link_config_subtask(struct wx *wx) { - int err; - - if (!test_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags)) + if (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags)) return; - err = txgbe_set_phy_link(wx); - if (err) - return; - - clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags); + txgbe_set_phy_link(wx); } /** @@ -151,6 +143,7 @@ static void txgbe_up_complete(struct wx *wx) /* make sure to complete pre-operations */ smp_mb__before_atomic(); + clear_bit(WX_STATE_DOWN, wx->state); wx_napi_enable_all(wx); switch (wx->mac.type) { @@ -213,6 +206,9 @@ static void txgbe_disable_device(struct wx *wx) struct net_device *netdev = wx->netdev; u32 i; + if (test_and_set_bit(WX_STATE_DOWN, wx->state)) + return; + wx_disable_pcie_master(wx); /* disable receives */ wx_disable_rx(wx); @@ -229,6 +225,7 @@ static void txgbe_disable_device(struct wx *wx) wx_napi_disable_all(wx); timer_delete_sync(&wx->service_timer); + cancel_work_sync(&wx->service_task); if (wx->bus.func < 2) wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0); @@ -261,8 +258,6 @@ static void txgbe_disable_device(struct wx *wx) /* Disable the Tx DMA engine */ wr32m(wx, WX_TDM_CTL, WX_TDM_CTL_TE, 0); - - wx_update_stats(wx); } void txgbe_down(struct wx *wx) diff --git a/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c b/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c index 37e4ec487afd..8b16b900820a 100644 --- a/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c +++ b/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c @@ -26,16 +26,16 @@ * Class, Class Mask, private data (not used) } */ static const struct pci_device_id txgbevf_pci_tbl[] = { - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_SP1000), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_WX1820), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML500F), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML510F), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML5024), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML5124), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML503F), 0}, - { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML513F), 0}, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_SP1000) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_WX1820) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML500F) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML510F) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML5024) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML5124) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML503F) }, + { PCI_VDEVICE(WANGXUN, TXGBEVF_DEV_ID_AML513F) }, /* required last entry */ - { .device = 0 } + { } }; static const struct net_device_ops txgbevf_netdev_ops = { diff --git a/drivers/net/ethernet/wiznet/Kconfig b/drivers/net/ethernet/wiznet/Kconfig index 4bac2ad2d6a1..bc83f6d389b1 100644 --- a/drivers/net/ethernet/wiznet/Kconfig +++ b/drivers/net/ethernet/wiznet/Kconfig @@ -5,7 +5,6 @@ config NET_VENDOR_WIZNET bool "WIZnet devices" - depends on HAS_IOMEM default y help If you have a network (Ethernet) card belonging to this class, say Y. @@ -18,8 +17,8 @@ config NET_VENDOR_WIZNET if NET_VENDOR_WIZNET config WIZNET_W5100 - tristate "WIZnet W5100 Ethernet support" - depends on HAS_IOMEM + tristate "WIZnet W5100/W5200/W5500 Ethernet support for SPI mode" + depends on SPI help Support for WIZnet W5100 chips. @@ -30,58 +29,4 @@ config WIZNET_W5100 To compile this driver as a module, choose M here: the module will be called w5100. -config WIZNET_W5300 - tristate "WIZnet W5300 Ethernet support" - depends on HAS_IOMEM - help - Support for WIZnet W5300 chips. - - W5300 is a single chip with integrated 10/100 Ethernet MAC, - PHY and hardware TCP/IP stack, but this driver is limited to - the MAC and PHY functions only, onchip TCP/IP is unused. - - To compile this driver as a module, choose M here: the module - will be called w5300. - -choice - prompt "WIZnet interface mode" - depends on WIZNET_W5100 || WIZNET_W5300 - default WIZNET_BUS_ANY - -config WIZNET_BUS_DIRECT - bool "Direct address bus mode" - help - In direct address mode host system can directly access all registers - after mapping to Memory-Mapped I/O space. - -config WIZNET_BUS_INDIRECT - bool "Indirect address bus mode" - help - In indirect address mode host system indirectly accesses registers - using Indirect Mode Address Register and Indirect Mode Data Register, - which are directly mapped to Memory-Mapped I/O space. - -config WIZNET_BUS_ANY - bool "Select interface mode in runtime" - help - If interface mode is unknown in compile time, it can be selected - in runtime from board/platform resources configuration. - - Performance may decrease compared to explicitly selected bus mode. -endchoice - -config WIZNET_W5100_SPI - tristate "WIZnet W5100/W5200/W5500 Ethernet support for SPI mode" - depends on WIZNET_BUS_ANY && WIZNET_W5100 - depends on SPI - help - In SPI mode host system accesses registers using SPI protocol - (mode 0) on the SPI bus. - - Performance decreases compared to other bus interface mode. - In W5100 SPI mode, burst READ/WRITE processing are not provided. - - To compile this driver as a module, choose M here: the module - will be called w5100-spi. - endif # NET_VENDOR_WIZNET diff --git a/drivers/net/ethernet/wiznet/Makefile b/drivers/net/ethernet/wiznet/Makefile index 78104f0bf415..502150d6ead2 100644 --- a/drivers/net/ethernet/wiznet/Makefile +++ b/drivers/net/ethernet/wiznet/Makefile @@ -1,4 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_WIZNET_W5100) += w5100.o -obj-$(CONFIG_WIZNET_W5100_SPI) += w5100-spi.o -obj-$(CONFIG_WIZNET_W5300) += w5300.o +obj-$(CONFIG_WIZNET_W5100) += w5100.o w5100-spi.o diff --git a/drivers/net/ethernet/wiznet/w5100-spi.c b/drivers/net/ethernet/wiznet/w5100-spi.c index 990a3cce8c0f..d2c5e99ab9c7 100644 --- a/drivers/net/ethernet/wiznet/w5100-spi.c +++ b/drivers/net/ethernet/wiznet/w5100-spi.c @@ -450,7 +450,7 @@ static int w5100_spi_probe(struct spi_device *spi) return -EINVAL; } - return w5100_probe(&spi->dev, ops, priv_size, mac, spi->irq, -EINVAL); + return w5100_probe(&spi->dev, ops, priv_size, mac, spi->irq); } static void w5100_spi_remove(struct spi_device *spi) diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index c5424d882135..53d8dc642fbd 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -23,7 +22,6 @@ #include #include #include -#include #include "w5100.h" @@ -156,8 +154,6 @@ struct w5100_priv { u16 s0_rx_buf_size; int irq; - int link_irq; - int link_gpio; struct napi_struct napi; struct net_device *ndev; @@ -172,311 +168,6 @@ struct w5100_priv { struct work_struct restart_work; }; -/************************************************************************ - * - * Lowlevel I/O functions - * - ***********************************************************************/ - -struct w5100_mmio_priv { - void __iomem *base; - /* Serialize access in indirect address mode */ - spinlock_t reg_lock; -}; - -static inline struct w5100_mmio_priv *w5100_mmio_priv(struct net_device *dev) -{ - return w5100_ops_priv(dev); -} - -static inline void __iomem *w5100_mmio(struct net_device *ndev) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - - return mmio_priv->base; -} - -/* - * In direct address mode host system can directly access W5100 registers - * after mapping to Memory-Mapped I/O space. - * - * 0x8000 bytes are required for memory space. - */ -static inline int w5100_read_direct(struct net_device *ndev, u32 addr) -{ - return ioread8(w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT)); -} - -static inline int __w5100_write_direct(struct net_device *ndev, u32 addr, - u8 data) -{ - iowrite8(data, w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT)); - - return 0; -} - -static inline int w5100_write_direct(struct net_device *ndev, u32 addr, u8 data) -{ - __w5100_write_direct(ndev, addr, data); - - return 0; -} - -static int w5100_read16_direct(struct net_device *ndev, u32 addr) -{ - u16 data; - data = w5100_read_direct(ndev, addr) << 8; - data |= w5100_read_direct(ndev, addr + 1); - return data; -} - -static int w5100_write16_direct(struct net_device *ndev, u32 addr, u16 data) -{ - __w5100_write_direct(ndev, addr, data >> 8); - __w5100_write_direct(ndev, addr + 1, data); - - return 0; -} - -static int w5100_readbulk_direct(struct net_device *ndev, u32 addr, u8 *buf, - int len) -{ - int i; - - for (i = 0; i < len; i++, addr++) - *buf++ = w5100_read_direct(ndev, addr); - - return 0; -} - -static int w5100_writebulk_direct(struct net_device *ndev, u32 addr, - const u8 *buf, int len) -{ - int i; - - for (i = 0; i < len; i++, addr++) - __w5100_write_direct(ndev, addr, *buf++); - - return 0; -} - -static int w5100_mmio_init(struct net_device *ndev) -{ - struct platform_device *pdev = to_platform_device(ndev->dev.parent); - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - - spin_lock_init(&mmio_priv->reg_lock); - - mmio_priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); - if (IS_ERR(mmio_priv->base)) - return PTR_ERR(mmio_priv->base); - - return 0; -} - -static const struct w5100_ops w5100_mmio_direct_ops = { - .chip_id = W5100, - .read = w5100_read_direct, - .write = w5100_write_direct, - .read16 = w5100_read16_direct, - .write16 = w5100_write16_direct, - .readbulk = w5100_readbulk_direct, - .writebulk = w5100_writebulk_direct, - .init = w5100_mmio_init, -}; - -/* - * In indirect address mode host system indirectly accesses registers by - * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data - * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space. - * Mode Register (MR) is directly accessible. - * - * Only 0x04 bytes are required for memory space. - */ -#define W5100_IDM_AR 0x01 /* Indirect Mode Address Register */ -#define W5100_IDM_DR 0x03 /* Indirect Mode Data Register */ - -static int w5100_read_indirect(struct net_device *ndev, u32 addr) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - unsigned long flags; - u8 data; - - spin_lock_irqsave(&mmio_priv->reg_lock, flags); - w5100_write16_direct(ndev, W5100_IDM_AR, addr); - data = w5100_read_direct(ndev, W5100_IDM_DR); - spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); - - return data; -} - -static int w5100_write_indirect(struct net_device *ndev, u32 addr, u8 data) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - unsigned long flags; - - spin_lock_irqsave(&mmio_priv->reg_lock, flags); - w5100_write16_direct(ndev, W5100_IDM_AR, addr); - w5100_write_direct(ndev, W5100_IDM_DR, data); - spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); - - return 0; -} - -static int w5100_read16_indirect(struct net_device *ndev, u32 addr) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - unsigned long flags; - u16 data; - - spin_lock_irqsave(&mmio_priv->reg_lock, flags); - w5100_write16_direct(ndev, W5100_IDM_AR, addr); - data = w5100_read_direct(ndev, W5100_IDM_DR) << 8; - data |= w5100_read_direct(ndev, W5100_IDM_DR); - spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); - - return data; -} - -static int w5100_write16_indirect(struct net_device *ndev, u32 addr, u16 data) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - unsigned long flags; - - spin_lock_irqsave(&mmio_priv->reg_lock, flags); - w5100_write16_direct(ndev, W5100_IDM_AR, addr); - __w5100_write_direct(ndev, W5100_IDM_DR, data >> 8); - w5100_write_direct(ndev, W5100_IDM_DR, data); - spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); - - return 0; -} - -static int w5100_readbulk_indirect(struct net_device *ndev, u32 addr, u8 *buf, - int len) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - unsigned long flags; - int i; - - spin_lock_irqsave(&mmio_priv->reg_lock, flags); - w5100_write16_direct(ndev, W5100_IDM_AR, addr); - - for (i = 0; i < len; i++) - *buf++ = w5100_read_direct(ndev, W5100_IDM_DR); - - spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); - - return 0; -} - -static int w5100_writebulk_indirect(struct net_device *ndev, u32 addr, - const u8 *buf, int len) -{ - struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); - unsigned long flags; - int i; - - spin_lock_irqsave(&mmio_priv->reg_lock, flags); - w5100_write16_direct(ndev, W5100_IDM_AR, addr); - - for (i = 0; i < len; i++) - __w5100_write_direct(ndev, W5100_IDM_DR, *buf++); - - spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); - - return 0; -} - -static int w5100_reset_indirect(struct net_device *ndev) -{ - w5100_write_direct(ndev, W5100_MR, MR_RST); - mdelay(5); - w5100_write_direct(ndev, W5100_MR, MR_PB | MR_AI | MR_IND); - - return 0; -} - -static const struct w5100_ops w5100_mmio_indirect_ops = { - .chip_id = W5100, - .read = w5100_read_indirect, - .write = w5100_write_indirect, - .read16 = w5100_read16_indirect, - .write16 = w5100_write16_indirect, - .readbulk = w5100_readbulk_indirect, - .writebulk = w5100_writebulk_indirect, - .init = w5100_mmio_init, - .reset = w5100_reset_indirect, -}; - -#if defined(CONFIG_WIZNET_BUS_DIRECT) - -static int w5100_read(struct w5100_priv *priv, u32 addr) -{ - return w5100_read_direct(priv->ndev, addr); -} - -static int w5100_write(struct w5100_priv *priv, u32 addr, u8 data) -{ - return w5100_write_direct(priv->ndev, addr, data); -} - -static int w5100_read16(struct w5100_priv *priv, u32 addr) -{ - return w5100_read16_direct(priv->ndev, addr); -} - -static int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data) -{ - return w5100_write16_direct(priv->ndev, addr, data); -} - -static int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len) -{ - return w5100_readbulk_direct(priv->ndev, addr, buf, len); -} - -static int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf, - int len) -{ - return w5100_writebulk_direct(priv->ndev, addr, buf, len); -} - -#elif defined(CONFIG_WIZNET_BUS_INDIRECT) - -static int w5100_read(struct w5100_priv *priv, u32 addr) -{ - return w5100_read_indirect(priv->ndev, addr); -} - -static int w5100_write(struct w5100_priv *priv, u32 addr, u8 data) -{ - return w5100_write_indirect(priv->ndev, addr, data); -} - -static int w5100_read16(struct w5100_priv *priv, u32 addr) -{ - return w5100_read16_indirect(priv->ndev, addr); -} - -static int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data) -{ - return w5100_write16_indirect(priv->ndev, addr, data); -} - -static int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len) -{ - return w5100_readbulk_indirect(priv->ndev, addr, buf, len); -} - -static int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf, - int len) -{ - return w5100_writebulk_indirect(priv->ndev, addr, buf, len); -} - -#else /* CONFIG_WIZNET_BUS_ANY */ - static int w5100_read(struct w5100_priv *priv, u32 addr) { return priv->ops->read(priv->ndev, addr); @@ -508,8 +199,6 @@ static int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf, return priv->ops->writebulk(priv->ndev, addr, buf, len); } -#endif - static int w5100_readbuf(struct w5100_priv *priv, u16 offset, u8 *buf, int len) { u32 addr; @@ -725,16 +414,6 @@ static void w5100_get_drvinfo(struct net_device *ndev, sizeof(info->bus_info)); } -static u32 w5100_get_link(struct net_device *ndev) -{ - struct w5100_priv *priv = netdev_priv(ndev); - - if (gpio_is_valid(priv->link_gpio)) - return !!gpio_get_value(priv->link_gpio); - - return 1; -} - static u32 w5100_get_msglevel(struct net_device *ndev) { struct w5100_priv *priv = netdev_priv(ndev); @@ -937,24 +616,6 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance) return IRQ_HANDLED; } -static irqreturn_t w5100_detect_link(int irq, void *ndev_instance) -{ - struct net_device *ndev = ndev_instance; - struct w5100_priv *priv = netdev_priv(ndev); - - if (netif_running(ndev)) { - if (gpio_get_value(priv->link_gpio) != 0) { - netif_info(priv, link, ndev, "link is up\n"); - netif_carrier_on(ndev); - } else { - netif_info(priv, link, ndev, "link is down\n"); - netif_carrier_off(ndev); - } - } - - return IRQ_HANDLED; -} - static void w5100_setrx_work(struct work_struct *work) { struct w5100_priv *priv = container_of(work, struct w5100_priv, @@ -998,9 +659,6 @@ static int w5100_open(struct net_device *ndev) w5100_hw_start(priv); napi_enable(&priv->napi); netif_start_queue(ndev); - if (!gpio_is_valid(priv->link_gpio) || - gpio_get_value(priv->link_gpio) != 0) - netif_carrier_on(ndev); return 0; } @@ -1020,7 +678,6 @@ static const struct ethtool_ops w5100_ethtool_ops = { .get_drvinfo = w5100_get_drvinfo, .get_msglevel = w5100_get_msglevel, .set_msglevel = w5100_set_msglevel, - .get_link = w5100_get_link, .get_regs_len = w5100_get_regs_len, .get_regs = w5100_get_regs, }; @@ -1035,38 +692,6 @@ static const struct net_device_ops w5100_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; -static int w5100_mmio_probe(struct platform_device *pdev) -{ - struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev); - const void *mac_addr = NULL; - struct resource *mem; - const struct w5100_ops *ops; - int irq; - - if (data && is_valid_ether_addr(data->mac_addr)) - mac_addr = data->mac_addr; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) - return -EINVAL; - if (resource_size(mem) < W5100_BUS_DIRECT_SIZE) - ops = &w5100_mmio_indirect_ops; - else - ops = &w5100_mmio_direct_ops; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - return w5100_probe(&pdev->dev, ops, sizeof(struct w5100_mmio_priv), - mac_addr, irq, data ? data->link_gpio : -EINVAL); -} - -static void w5100_mmio_remove(struct platform_device *pdev) -{ - w5100_remove(&pdev->dev); -} - void *w5100_ops_priv(const struct net_device *ndev) { return netdev_priv(ndev) + @@ -1075,8 +700,7 @@ void *w5100_ops_priv(const struct net_device *ndev) EXPORT_SYMBOL_GPL(w5100_ops_priv); int w5100_probe(struct device *dev, const struct w5100_ops *ops, - int sizeof_ops_priv, const void *mac_addr, int irq, - int link_gpio) + int sizeof_ops_priv, const void *mac_addr, int irq) { struct w5100_priv *priv; struct net_device *ndev; @@ -1127,7 +751,6 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, priv->ndev = ndev; priv->ops = ops; priv->irq = irq; - priv->link_gpio = link_gpio; ndev->netdev_ops = &w5100_netdev_ops; ndev->ethtool_ops = &w5100_ethtool_ops; @@ -1180,26 +803,8 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, if (err) goto err_hw; - if (gpio_is_valid(priv->link_gpio)) { - char *link_name = devm_kzalloc(dev, 16, GFP_KERNEL); - - if (!link_name) { - err = -ENOMEM; - goto err_gpio; - } - snprintf(link_name, 16, "%s-link", netdev_name(ndev)); - priv->link_irq = gpio_to_irq(priv->link_gpio); - if (request_any_context_irq(priv->link_irq, w5100_detect_link, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, - link_name, priv->ndev) < 0) - priv->link_gpio = -EINVAL; - } - return 0; -err_gpio: - free_irq(priv->irq, ndev); err_hw: destroy_workqueue(priv->xfer_wq); err_wq: @@ -1217,8 +822,6 @@ void w5100_remove(struct device *dev) w5100_hw_reset(priv); free_irq(priv->irq, ndev); - if (gpio_is_valid(priv->link_gpio)) - free_irq(priv->link_irq, ndev); flush_work(&priv->setrx_work); flush_work(&priv->restart_work); @@ -1254,9 +857,6 @@ static int w5100_resume(struct device *dev) w5100_hw_start(priv); netif_device_attach(ndev); - if (!gpio_is_valid(priv->link_gpio) || - gpio_get_value(priv->link_gpio) != 0) - netif_carrier_on(ndev); } return 0; } @@ -1264,13 +864,3 @@ static int w5100_resume(struct device *dev) SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume); EXPORT_SYMBOL_GPL(w5100_pm_ops); - -static struct platform_driver w5100_mmio_driver = { - .driver = { - .name = DRV_NAME, - .pm = &w5100_pm_ops, - }, - .probe = w5100_mmio_probe, - .remove = w5100_mmio_remove, -}; -module_platform_driver(w5100_mmio_driver); diff --git a/drivers/net/ethernet/wiznet/w5100.h b/drivers/net/ethernet/wiznet/w5100.h index 481af3b6d9e8..76e1b8149041 100644 --- a/drivers/net/ethernet/wiznet/w5100.h +++ b/drivers/net/ethernet/wiznet/w5100.h @@ -29,8 +29,7 @@ struct w5100_ops { void *w5100_ops_priv(const struct net_device *ndev); int w5100_probe(struct device *dev, const struct w5100_ops *ops, - int sizeof_ops_priv, const void *mac_addr, int irq, - int link_gpio); + int sizeof_ops_priv, const void *mac_addr, int irq); void w5100_remove(struct device *dev); extern const struct dev_pm_ops w5100_pm_ops; diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c deleted file mode 100644 index 3e711dea3b2c..000000000000 --- a/drivers/net/ethernet/wiznet/w5300.c +++ /dev/null @@ -1,687 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Ethernet driver for the WIZnet W5300 chip. - * - * Copyright (C) 2008-2009 WIZnet Co.,Ltd. - * Copyright (C) 2011 Taehun Kim gmail.com> - * Copyright (C) 2012 Mike Sinkovsky - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "w5300" -#define DRV_VERSION "2012-04-04" - -MODULE_DESCRIPTION("WIZnet W5300 Ethernet driver v"DRV_VERSION); -MODULE_AUTHOR("Mike Sinkovsky "); -MODULE_ALIAS("platform:"DRV_NAME); -MODULE_LICENSE("GPL"); - -/* - * Registers - */ -#define W5300_MR 0x0000 /* Mode Register */ -#define MR_DBW (1 << 15) /* Data bus width */ -#define MR_MPF (1 << 14) /* Mac layer pause frame */ -#define MR_WDF(n) (((n)&7)<<11) /* Write data fetch time */ -#define MR_RDH (1 << 10) /* Read data hold time */ -#define MR_FS (1 << 8) /* FIFO swap */ -#define MR_RST (1 << 7) /* S/W reset */ -#define MR_PB (1 << 4) /* Ping block */ -#define MR_DBS (1 << 2) /* Data bus swap */ -#define MR_IND (1 << 0) /* Indirect mode */ -#define W5300_IR 0x0002 /* Interrupt Register */ -#define W5300_IMR 0x0004 /* Interrupt Mask Register */ -#define IR_S0 0x0001 /* S0 interrupt */ -#define W5300_SHARL 0x0008 /* Source MAC address (0123) */ -#define W5300_SHARH 0x000c /* Source MAC address (45) */ -#define W5300_TMSRL 0x0020 /* Transmit Memory Size (0123) */ -#define W5300_TMSRH 0x0024 /* Transmit Memory Size (4567) */ -#define W5300_RMSRL 0x0028 /* Receive Memory Size (0123) */ -#define W5300_RMSRH 0x002c /* Receive Memory Size (4567) */ -#define W5300_MTYPE 0x0030 /* Memory Type */ -#define W5300_IDR 0x00fe /* Chip ID register */ -#define IDR_W5300 0x5300 /* =0x5300 for WIZnet W5300 */ -#define W5300_S0_MR 0x0200 /* S0 Mode Register */ -#define S0_MR_CLOSED 0x0000 /* Close mode */ -#define S0_MR_MACRAW 0x0004 /* MAC RAW mode (promiscuous) */ -#define S0_MR_MACRAW_MF 0x0044 /* MAC RAW mode (filtered) */ -#define W5300_S0_CR 0x0202 /* S0 Command Register */ -#define S0_CR_OPEN 0x0001 /* OPEN command */ -#define S0_CR_CLOSE 0x0010 /* CLOSE command */ -#define S0_CR_SEND 0x0020 /* SEND command */ -#define S0_CR_RECV 0x0040 /* RECV command */ -#define W5300_S0_IMR 0x0204 /* S0 Interrupt Mask Register */ -#define W5300_S0_IR 0x0206 /* S0 Interrupt Register */ -#define S0_IR_RECV 0x0004 /* Receive interrupt */ -#define S0_IR_SENDOK 0x0010 /* Send OK interrupt */ -#define W5300_S0_SSR 0x0208 /* S0 Socket Status Register */ -#define W5300_S0_TX_WRSR 0x0220 /* S0 TX Write Size Register */ -#define W5300_S0_TX_FSR 0x0224 /* S0 TX Free Size Register */ -#define W5300_S0_RX_RSR 0x0228 /* S0 Received data Size */ -#define W5300_S0_TX_FIFO 0x022e /* S0 Transmit FIFO */ -#define W5300_S0_RX_FIFO 0x0230 /* S0 Receive FIFO */ -#define W5300_REGS_LEN 0x0400 - -/* - * Device driver private data structure - */ -struct w5300_priv { - void __iomem *base; - spinlock_t reg_lock; - bool indirect; - u16 (*read) (struct w5300_priv *priv, u16 addr); - void (*write)(struct w5300_priv *priv, u16 addr, u16 data); - int irq; - int link_irq; - int link_gpio; - - struct napi_struct napi; - struct net_device *ndev; - bool promisc; - u32 msg_enable; -}; - -/************************************************************************ - * - * Lowlevel I/O functions - * - ***********************************************************************/ - -/* - * In direct address mode host system can directly access W5300 registers - * after mapping to Memory-Mapped I/O space. - * - * 0x400 bytes are required for memory space. - */ -static inline u16 w5300_read_direct(struct w5300_priv *priv, u16 addr) -{ - return ioread16(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT)); -} - -static inline void w5300_write_direct(struct w5300_priv *priv, - u16 addr, u16 data) -{ - iowrite16(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT)); -} - -/* - * In indirect address mode host system indirectly accesses registers by - * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data - * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space. - * Mode Register (MR) is directly accessible. - * - * Only 0x06 bytes are required for memory space. - */ -#define W5300_IDM_AR 0x0002 /* Indirect Mode Address */ -#define W5300_IDM_DR 0x0004 /* Indirect Mode Data */ - -static u16 w5300_read_indirect(struct w5300_priv *priv, u16 addr) -{ - unsigned long flags; - u16 data; - - spin_lock_irqsave(&priv->reg_lock, flags); - w5300_write_direct(priv, W5300_IDM_AR, addr); - data = w5300_read_direct(priv, W5300_IDM_DR); - spin_unlock_irqrestore(&priv->reg_lock, flags); - - return data; -} - -static void w5300_write_indirect(struct w5300_priv *priv, u16 addr, u16 data) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->reg_lock, flags); - w5300_write_direct(priv, W5300_IDM_AR, addr); - w5300_write_direct(priv, W5300_IDM_DR, data); - spin_unlock_irqrestore(&priv->reg_lock, flags); -} - -#if defined(CONFIG_WIZNET_BUS_DIRECT) -#define w5300_read w5300_read_direct -#define w5300_write w5300_write_direct - -#elif defined(CONFIG_WIZNET_BUS_INDIRECT) -#define w5300_read w5300_read_indirect -#define w5300_write w5300_write_indirect - -#else /* CONFIG_WIZNET_BUS_ANY */ -#define w5300_read priv->read -#define w5300_write priv->write -#endif - -static u32 w5300_read32(struct w5300_priv *priv, u16 addr) -{ - u32 data; - data = w5300_read(priv, addr) << 16; - data |= w5300_read(priv, addr + 2); - return data; -} - -static void w5300_write32(struct w5300_priv *priv, u16 addr, u32 data) -{ - w5300_write(priv, addr, data >> 16); - w5300_write(priv, addr + 2, data); -} - -static int w5300_command(struct w5300_priv *priv, u16 cmd) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(100); - - w5300_write(priv, W5300_S0_CR, cmd); - - while (w5300_read(priv, W5300_S0_CR) != 0) { - if (time_after(jiffies, timeout)) - return -EIO; - cpu_relax(); - } - - return 0; -} - -static void w5300_read_frame(struct w5300_priv *priv, u8 *buf, int len) -{ - u16 fifo; - int i; - - for (i = 0; i < len; i += 2) { - fifo = w5300_read(priv, W5300_S0_RX_FIFO); - *buf++ = fifo >> 8; - *buf++ = fifo; - } - fifo = w5300_read(priv, W5300_S0_RX_FIFO); - fifo = w5300_read(priv, W5300_S0_RX_FIFO); -} - -static void w5300_write_frame(struct w5300_priv *priv, u8 *buf, int len) -{ - u16 fifo; - int i; - - for (i = 0; i < len; i += 2) { - fifo = *buf++ << 8; - fifo |= *buf++; - w5300_write(priv, W5300_S0_TX_FIFO, fifo); - } - w5300_write32(priv, W5300_S0_TX_WRSR, len); -} - -static void w5300_write_macaddr(struct w5300_priv *priv) -{ - struct net_device *ndev = priv->ndev; - w5300_write32(priv, W5300_SHARL, - ndev->dev_addr[0] << 24 | - ndev->dev_addr[1] << 16 | - ndev->dev_addr[2] << 8 | - ndev->dev_addr[3]); - w5300_write(priv, W5300_SHARH, - ndev->dev_addr[4] << 8 | - ndev->dev_addr[5]); -} - -static void w5300_hw_reset(struct w5300_priv *priv) -{ - w5300_write_direct(priv, W5300_MR, MR_RST); - mdelay(5); - w5300_write_direct(priv, W5300_MR, priv->indirect ? - MR_WDF(7) | MR_PB | MR_IND : - MR_WDF(7) | MR_PB); - w5300_write(priv, W5300_IMR, 0); - w5300_write_macaddr(priv); - - /* Configure 128K of internal memory - * as 64K RX fifo and 64K TX fifo - */ - w5300_write32(priv, W5300_RMSRL, 64 << 24); - w5300_write32(priv, W5300_RMSRH, 0); - w5300_write32(priv, W5300_TMSRL, 64 << 24); - w5300_write32(priv, W5300_TMSRH, 0); - w5300_write(priv, W5300_MTYPE, 0x00ff); -} - -static void w5300_hw_start(struct w5300_priv *priv) -{ - w5300_write(priv, W5300_S0_MR, priv->promisc ? - S0_MR_MACRAW : S0_MR_MACRAW_MF); - w5300_command(priv, S0_CR_OPEN); - w5300_write(priv, W5300_S0_IMR, S0_IR_RECV | S0_IR_SENDOK); - w5300_write(priv, W5300_IMR, IR_S0); -} - -static void w5300_hw_close(struct w5300_priv *priv) -{ - w5300_write(priv, W5300_IMR, 0); - w5300_command(priv, S0_CR_CLOSE); -} - -/*********************************************************************** - * - * Device driver functions / callbacks - * - ***********************************************************************/ - -static void w5300_get_drvinfo(struct net_device *ndev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - strscpy(info->bus_info, dev_name(ndev->dev.parent), - sizeof(info->bus_info)); -} - -static u32 w5300_get_link(struct net_device *ndev) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - if (gpio_is_valid(priv->link_gpio)) - return !!gpio_get_value(priv->link_gpio); - - return 1; -} - -static u32 w5300_get_msglevel(struct net_device *ndev) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - return priv->msg_enable; -} - -static void w5300_set_msglevel(struct net_device *ndev, u32 value) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - priv->msg_enable = value; -} - -static int w5300_get_regs_len(struct net_device *ndev) -{ - return W5300_REGS_LEN; -} - -static void w5300_get_regs(struct net_device *ndev, - struct ethtool_regs *regs, void *_buf) -{ - struct w5300_priv *priv = netdev_priv(ndev); - u8 *buf = _buf; - u16 addr; - u16 data; - - regs->version = 1; - for (addr = 0; addr < W5300_REGS_LEN; addr += 2) { - switch (addr & 0x23f) { - case W5300_S0_TX_FIFO: /* cannot read TX_FIFO */ - case W5300_S0_RX_FIFO: /* cannot read RX_FIFO */ - data = 0xffff; - break; - default: - data = w5300_read(priv, addr); - break; - } - *buf++ = data >> 8; - *buf++ = data; - } -} - -static void w5300_tx_timeout(struct net_device *ndev, unsigned int txqueue) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - netif_stop_queue(ndev); - w5300_hw_reset(priv); - w5300_hw_start(priv); - ndev->stats.tx_errors++; - netif_trans_update(ndev); - netif_wake_queue(ndev); -} - -static netdev_tx_t w5300_start_tx(struct sk_buff *skb, struct net_device *ndev) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - netif_stop_queue(ndev); - - w5300_write_frame(priv, skb->data, skb->len); - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; - dev_kfree_skb(skb); - netif_dbg(priv, tx_queued, ndev, "tx queued\n"); - - w5300_command(priv, S0_CR_SEND); - - return NETDEV_TX_OK; -} - -static int w5300_napi_poll(struct napi_struct *napi, int budget) -{ - struct w5300_priv *priv = container_of(napi, struct w5300_priv, napi); - struct net_device *ndev = priv->ndev; - struct sk_buff *skb; - int rx_count; - u16 rx_len; - - for (rx_count = 0; rx_count < budget; rx_count++) { - u32 rx_fifo_len = w5300_read32(priv, W5300_S0_RX_RSR); - if (rx_fifo_len == 0) - break; - - rx_len = w5300_read(priv, W5300_S0_RX_FIFO); - - skb = netdev_alloc_skb_ip_align(ndev, roundup(rx_len, 2)); - if (unlikely(!skb)) { - u32 i; - for (i = 0; i < rx_fifo_len; i += 2) - w5300_read(priv, W5300_S0_RX_FIFO); - ndev->stats.rx_dropped++; - return -ENOMEM; - } - - skb_put(skb, rx_len); - w5300_read_frame(priv, skb->data, rx_len); - skb->protocol = eth_type_trans(skb, ndev); - - netif_receive_skb(skb); - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += rx_len; - } - - if (rx_count < budget) { - napi_complete_done(napi, rx_count); - w5300_write(priv, W5300_IMR, IR_S0); - } - - return rx_count; -} - -static irqreturn_t w5300_interrupt(int irq, void *ndev_instance) -{ - struct net_device *ndev = ndev_instance; - struct w5300_priv *priv = netdev_priv(ndev); - - int ir = w5300_read(priv, W5300_S0_IR); - if (!ir) - return IRQ_NONE; - w5300_write(priv, W5300_S0_IR, ir); - - if (ir & S0_IR_SENDOK) { - netif_dbg(priv, tx_done, ndev, "tx done\n"); - netif_wake_queue(ndev); - } - - if (ir & S0_IR_RECV) { - if (napi_schedule_prep(&priv->napi)) { - w5300_write(priv, W5300_IMR, 0); - __napi_schedule(&priv->napi); - } - } - - return IRQ_HANDLED; -} - -static irqreturn_t w5300_detect_link(int irq, void *ndev_instance) -{ - struct net_device *ndev = ndev_instance; - struct w5300_priv *priv = netdev_priv(ndev); - - if (netif_running(ndev)) { - if (gpio_get_value(priv->link_gpio) != 0) { - netif_info(priv, link, ndev, "link is up\n"); - netif_carrier_on(ndev); - } else { - netif_info(priv, link, ndev, "link is down\n"); - netif_carrier_off(ndev); - } - } - - return IRQ_HANDLED; -} - -static void w5300_set_rx_mode(struct net_device *ndev) -{ - struct w5300_priv *priv = netdev_priv(ndev); - bool set_promisc = (ndev->flags & IFF_PROMISC) != 0; - - if (priv->promisc != set_promisc) { - priv->promisc = set_promisc; - w5300_hw_start(priv); - } -} - -static int w5300_set_macaddr(struct net_device *ndev, void *addr) -{ - struct w5300_priv *priv = netdev_priv(ndev); - struct sockaddr *sock_addr = addr; - - if (!is_valid_ether_addr(sock_addr->sa_data)) - return -EADDRNOTAVAIL; - eth_hw_addr_set(ndev, sock_addr->sa_data); - w5300_write_macaddr(priv); - return 0; -} - -static int w5300_open(struct net_device *ndev) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - netif_info(priv, ifup, ndev, "enabling\n"); - w5300_hw_start(priv); - napi_enable(&priv->napi); - netif_start_queue(ndev); - if (!gpio_is_valid(priv->link_gpio) || - gpio_get_value(priv->link_gpio) != 0) - netif_carrier_on(ndev); - return 0; -} - -static int w5300_stop(struct net_device *ndev) -{ - struct w5300_priv *priv = netdev_priv(ndev); - - netif_info(priv, ifdown, ndev, "shutting down\n"); - w5300_hw_close(priv); - netif_carrier_off(ndev); - netif_stop_queue(ndev); - napi_disable(&priv->napi); - return 0; -} - -static const struct ethtool_ops w5300_ethtool_ops = { - .get_drvinfo = w5300_get_drvinfo, - .get_msglevel = w5300_get_msglevel, - .set_msglevel = w5300_set_msglevel, - .get_link = w5300_get_link, - .get_regs_len = w5300_get_regs_len, - .get_regs = w5300_get_regs, -}; - -static const struct net_device_ops w5300_netdev_ops = { - .ndo_open = w5300_open, - .ndo_stop = w5300_stop, - .ndo_start_xmit = w5300_start_tx, - .ndo_tx_timeout = w5300_tx_timeout, - .ndo_set_rx_mode = w5300_set_rx_mode, - .ndo_set_mac_address = w5300_set_macaddr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int w5300_hw_probe(struct platform_device *pdev) -{ - struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev); - struct net_device *ndev = platform_get_drvdata(pdev); - struct w5300_priv *priv = netdev_priv(ndev); - const char *name = netdev_name(ndev); - struct resource *mem; - int mem_size; - int irq; - int ret; - - if (data && is_valid_ether_addr(data->mac_addr)) { - eth_hw_addr_set(ndev, data->mac_addr); - } else { - eth_hw_addr_random(ndev); - } - - priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - mem_size = resource_size(mem); - - spin_lock_init(&priv->reg_lock); - priv->indirect = mem_size < W5300_BUS_DIRECT_SIZE; - if (priv->indirect) { - priv->read = w5300_read_indirect; - priv->write = w5300_write_indirect; - } else { - priv->read = w5300_read_direct; - priv->write = w5300_write_direct; - } - - w5300_hw_reset(priv); - if (w5300_read(priv, W5300_IDR) != IDR_W5300) - return -ENODEV; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - ret = request_irq(irq, w5300_interrupt, - IRQ_TYPE_LEVEL_LOW, name, ndev); - if (ret < 0) - return ret; - priv->irq = irq; - - priv->link_gpio = data ? data->link_gpio : -EINVAL; - if (gpio_is_valid(priv->link_gpio)) { - char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL); - if (!link_name) - return -ENOMEM; - snprintf(link_name, 16, "%s-link", name); - priv->link_irq = gpio_to_irq(priv->link_gpio); - if (request_any_context_irq(priv->link_irq, w5300_detect_link, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - link_name, priv->ndev) < 0) - priv->link_gpio = -EINVAL; - } - - netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq); - return 0; -} - -static int w5300_probe(struct platform_device *pdev) -{ - struct w5300_priv *priv; - struct net_device *ndev; - int err; - - ndev = alloc_etherdev(sizeof(*priv)); - if (!ndev) - return -ENOMEM; - SET_NETDEV_DEV(ndev, &pdev->dev); - platform_set_drvdata(pdev, ndev); - priv = netdev_priv(ndev); - priv->ndev = ndev; - - ndev->netdev_ops = &w5300_netdev_ops; - ndev->ethtool_ops = &w5300_ethtool_ops; - ndev->watchdog_timeo = HZ; - netif_napi_add_weight(ndev, &priv->napi, w5300_napi_poll, 16); - - /* This chip doesn't support VLAN packets with normal MTU, - * so disable VLAN for this device. - */ - ndev->features |= NETIF_F_VLAN_CHALLENGED; - - err = register_netdev(ndev); - if (err < 0) - goto err_register; - - err = w5300_hw_probe(pdev); - if (err < 0) - goto err_hw_probe; - - return 0; - -err_hw_probe: - unregister_netdev(ndev); -err_register: - free_netdev(ndev); - return err; -} - -static void w5300_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct w5300_priv *priv = netdev_priv(ndev); - - w5300_hw_reset(priv); - free_irq(priv->irq, ndev); - if (gpio_is_valid(priv->link_gpio)) - free_irq(priv->link_irq, ndev); - - unregister_netdev(ndev); - free_netdev(ndev); -} - -#ifdef CONFIG_PM_SLEEP -static int w5300_suspend(struct device *dev) -{ - struct net_device *ndev = dev_get_drvdata(dev); - struct w5300_priv *priv = netdev_priv(ndev); - - if (netif_running(ndev)) { - netif_carrier_off(ndev); - netif_device_detach(ndev); - - w5300_hw_close(priv); - } - return 0; -} - -static int w5300_resume(struct device *dev) -{ - struct net_device *ndev = dev_get_drvdata(dev); - struct w5300_priv *priv = netdev_priv(ndev); - - if (!netif_running(ndev)) { - w5300_hw_reset(priv); - w5300_hw_start(priv); - - netif_device_attach(ndev); - if (!gpio_is_valid(priv->link_gpio) || - gpio_get_value(priv->link_gpio) != 0) - netif_carrier_on(ndev); - } - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static SIMPLE_DEV_PM_OPS(w5300_pm_ops, w5300_suspend, w5300_resume); - -static struct platform_driver w5300_driver = { - .driver = { - .name = DRV_NAME, - .pm = &w5300_pm_ops, - }, - .probe = w5300_probe, - .remove = w5300_remove, -}; - -module_platform_driver(w5300_driver); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 263c4b67fd5a..fcf517069d16 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -2536,6 +2536,25 @@ static const struct ethtool_ops axienet_ethtool_ops = { .get_rmon_stats = axienet_ethtool_get_rmon_stats, }; +static const struct ethtool_ops axienet_ethtool_dmaengine_ops = { + .get_drvinfo = axienet_ethtools_get_drvinfo, + .get_regs_len = axienet_ethtools_get_regs_len, + .get_regs = axienet_ethtools_get_regs, + .get_link = ethtool_op_get_link, + .get_pauseparam = axienet_ethtools_get_pauseparam, + .set_pauseparam = axienet_ethtools_set_pauseparam, + .get_link_ksettings = axienet_ethtools_get_link_ksettings, + .set_link_ksettings = axienet_ethtools_set_link_ksettings, + .nway_reset = axienet_ethtools_nway_reset, + .get_ethtool_stats = axienet_ethtools_get_ethtool_stats, + .get_strings = axienet_ethtools_get_strings, + .get_sset_count = axienet_ethtools_get_sset_count, + .get_pause_stats = axienet_ethtools_get_pause_stats, + .get_eth_mac_stats = axienet_ethtool_get_eth_mac_stats, + .get_eth_ctrl_stats = axienet_ethtool_get_eth_ctrl_stats, + .get_rmon_stats = axienet_ethtool_get_rmon_stats, +}; + static struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs) { return container_of(pcs, struct axienet_local, pcs); @@ -2792,7 +2811,6 @@ static int axienet_probe(struct platform_device *pdev) SET_NETDEV_DEV(ndev, &pdev->dev); ndev->features = NETIF_F_SG; - ndev->ethtool_ops = &axienet_ethtool_ops; /* MTU range: 64 - 9000 */ ndev->min_mtu = 64; @@ -3021,10 +3039,13 @@ static int axienet_probe(struct platform_device *pdev) lp->use_dmaengine = 1; } - if (lp->use_dmaengine) + if (lp->use_dmaengine) { ndev->netdev_ops = &axienet_netdev_dmaengine_ops; - else + ndev->ethtool_ops = &axienet_ethtool_dmaengine_ops; + } else { ndev->netdev_ops = &axienet_netdev_ops; + ndev->ethtool_ops = &axienet_ethtool_ops; + } /* Check for Ethernet core IRQ (optional) */ if (lp->eth_irq <= 0) dev_info(&pdev->dev, "Ethernet core IRQ not defined\n"); diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 715180c3a1b3..9afff7bcaa0b 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -73,6 +73,7 @@ struct geneve_dev_node { struct geneve_config { bool collect_md; + bool dualstack; bool use_udp6_rx_checksums; bool ttl_inherit; bool gro_hint; @@ -106,7 +107,7 @@ struct geneve_sock { bool collect_md; bool gro_hint; struct list_head list; - struct socket *sock; + struct sock *sk; struct rcu_head rcu; int refcnt; struct hlist_head vni_list[VNI_HASH_SIZE]; @@ -167,7 +168,7 @@ static bool eq_tun_id_and_vni(u8 *tun_id, u8 *vni) static sa_family_t geneve_get_sk_family(struct geneve_sock *gs) { - return gs->sock->sk->sk_family; + return gs->sk->sk_family; } static struct geneve_dev *geneve_lookup(struct geneve_sock *gs, @@ -603,7 +604,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; iph = (struct iphdr *)ipv6h; total_len = skb->len - gro_hint->nested_nh_offset; - if (total_len > GRO_LEGACY_MAX_SIZE) + if (total_len >= GRO_LEGACY_MAX_SIZE) return -E2BIG; /* @@ -760,25 +761,30 @@ static int geneve_udp_encap_err_lookup(struct sock *sk, struct sk_buff *skb) return -EPFNOSUPPORT; } -static struct socket *geneve_create_sock(struct net *net, bool ipv6, - __be16 port, bool ipv6_rx_csum) +static struct sock *geneve_create_sock(struct net *net, + struct geneve_dev *geneve, bool ipv6) { - struct socket *sock; + struct ip_tunnel_info *info = &geneve->cfg.info; struct udp_port_cfg udp_conf; + struct socket *sock; int err; memset(&udp_conf, 0, sizeof(udp_conf)); +#if IS_ENABLED(CONFIG_IPV6) if (ipv6) { udp_conf.family = AF_INET6; udp_conf.ipv6_v6only = 1; - udp_conf.use_udp6_rx_checksums = ipv6_rx_csum; - } else { + udp_conf.use_udp6_rx_checksums = geneve->cfg.use_udp6_rx_checksums; + udp_conf.local_ip6 = info->key.u.ipv6.src; + } else +#endif + { udp_conf.family = AF_INET; - udp_conf.local_ip.s_addr = htonl(INADDR_ANY); + udp_conf.local_ip.s_addr = info->key.u.ipv4.src; } - udp_conf.local_udp_port = port; + udp_conf.local_udp_port = info->key.tp_dst; /* Open UDP socket */ err = udp_sock_create(net, &udp_conf, &sock); @@ -786,7 +792,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6, return ERR_PTR(err); udp_allow_gso(sock->sk); - return sock; + return sock->sk; } static bool geneve_hdr_match(struct sk_buff *skb, @@ -970,32 +976,32 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, } /* Create new listen socket if needed */ -static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, - bool ipv6, bool ipv6_rx_csum) +static struct geneve_sock *geneve_socket_create(struct net *net, + struct geneve_dev *geneve, bool ipv6) { struct geneve_net *gn = net_generic(net, geneve_net_id); - struct geneve_sock *gs; - struct socket *sock; struct udp_tunnel_sock_cfg tunnel_cfg; + struct geneve_sock *gs; + struct sock *sk; int h; gs = kzalloc_obj(*gs); if (!gs) return ERR_PTR(-ENOMEM); - sock = geneve_create_sock(net, ipv6, port, ipv6_rx_csum); - if (IS_ERR(sock)) { + sk = geneve_create_sock(net, geneve, ipv6); + if (IS_ERR(sk)) { kfree(gs); - return ERR_CAST(sock); + return ERR_CAST(sk); } - gs->sock = sock; + gs->sk = sk; gs->refcnt = 1; for (h = 0; h < VNI_HASH_SIZE; ++h) INIT_HLIST_HEAD(&gs->vni_list[h]); /* Initialize the geneve udp offloads structure */ - udp_tunnel_notify_add_rx_port(gs->sock, UDP_TUNNEL_TYPE_GENEVE); + udp_tunnel_notify_add_rx_port(sk, UDP_TUNNEL_TYPE_GENEVE); /* Mark socket as an encapsulation socket */ memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); @@ -1006,7 +1012,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, tunnel_cfg.encap_rcv = geneve_udp_encap_recv; tunnel_cfg.encap_err_lookup = geneve_udp_encap_err_lookup; tunnel_cfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(net, sk, &tunnel_cfg); list_add(&gs->list, &gn->sock_list); return gs; } @@ -1017,8 +1023,8 @@ static void __geneve_sock_release(struct geneve_sock *gs) return; list_del(&gs->list); - udp_tunnel_notify_del_rx_port(gs->sock, UDP_TUNNEL_TYPE_GENEVE); - udp_tunnel_sock_release(gs->sock); + udp_tunnel_notify_del_rx_port(gs->sk, UDP_TUNNEL_TYPE_GENEVE); + udp_tunnel_sock_release(gs->sk); kfree_rcu(gs, rcu); } @@ -1032,7 +1038,6 @@ static void geneve_sock_release(struct geneve_dev *geneve) #endif rcu_assign_pointer(geneve->sock4, NULL); - synchronize_net(); __geneve_sock_release(gs4); #if IS_ENABLED(CONFIG_IPV6) @@ -1040,48 +1045,63 @@ static void geneve_sock_release(struct geneve_dev *geneve) #endif } -static struct geneve_sock *geneve_find_sock(struct geneve_net *gn, - sa_family_t family, - __be16 dst_port, - bool gro_hint) +static struct geneve_sock *geneve_find_sock(struct net *net, + struct geneve_dev *geneve, bool ipv6) { + struct geneve_net *gn = net_generic(net, geneve_net_id); + struct ip_tunnel_info *info = &geneve->cfg.info; + sa_family_t family = ipv6 ? AF_INET6 : AF_INET; + bool gro_hint = geneve->cfg.gro_hint; + __be16 dst_port = info->key.tp_dst; struct geneve_sock *gs; list_for_each_entry(gs, &gn->sock_list, list) { - if (inet_sk(gs->sock->sk)->inet_sport == dst_port && - geneve_get_sk_family(gs) == family && - gs->gro_hint == gro_hint) { - return gs; - } + if (inet_sk(gs->sk)->inet_sport != dst_port) + continue; + + if (geneve_get_sk_family(gs) != family) + continue; + + if (gs->gro_hint != gro_hint) + continue; + + if (family == AF_INET && + inet_sk(gs->sk)->inet_saddr != info->key.u.ipv4.src) + continue; + +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6 && + !ipv6_addr_equal(&gs->sk->sk_v6_rcv_saddr, &info->key.u.ipv6.src)) + continue; +#endif + + return gs; } + return NULL; } static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6) { struct net *net = geneve->net; - struct geneve_net *gn = net_generic(net, geneve_net_id); - bool gro_hint = geneve->cfg.gro_hint; struct geneve_dev_node *node; struct geneve_sock *gs; __u8 vni[3]; __u32 hash; - gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, - geneve->cfg.info.key.tp_dst, gro_hint); + gs = geneve_find_sock(net, geneve, ipv6); if (gs) { gs->refcnt++; goto out; } - gs = geneve_socket_create(net, geneve->cfg.info.key.tp_dst, ipv6, - geneve->cfg.use_udp6_rx_checksums); + gs = geneve_socket_create(net, geneve, ipv6); if (IS_ERR(gs)) return PTR_ERR(gs); out: gs->collect_md = geneve->cfg.collect_md; - gs->gro_hint = gro_hint; + gs->gro_hint = geneve->cfg.gro_hint; #if IS_ENABLED(CONFIG_IPV6) if (ipv6) { rcu_assign_pointer(geneve->sock6, gs); @@ -1103,12 +1123,12 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6) static int geneve_open(struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); - bool metadata = geneve->cfg.collect_md; + bool dualstack = geneve->cfg.dualstack; bool ipv4, ipv6; int ret = 0; - ipv6 = geneve->cfg.info.mode & IP_TUNNEL_INFO_IPV6 || metadata; - ipv4 = !ipv6 || metadata; + ipv6 = geneve->cfg.info.mode & IP_TUNNEL_INFO_IPV6 || dualstack; + ipv4 = !ipv6 || dualstack; #if IS_ENABLED(CONFIG_IPV6) if (ipv6) { ret = geneve_sock_add(geneve, true); @@ -1321,6 +1341,12 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (IS_ERR(rt)) return PTR_ERR(rt); + if (geneve->cfg.info.key.u.ipv4.src && + saddr != geneve->cfg.info.key.u.ipv4.src) { + dst_release(&rt->dst); + return -EADDRNOTAVAIL; + } + err = skb_tunnel_check_pmtu(skb, &rt->dst, GENEVE_IPV4_HLEN + info->options_len + geneve_build_gro_hint_opt(geneve, skb), @@ -1390,7 +1416,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) return err; - udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, saddr, info->key.u.ipv4.dst, + udp_tunnel_xmit_skb(rt, gs4->sk, skb, saddr, info->key.u.ipv4.dst, tos, ttl, df, sport, geneve->cfg.info.key.tp_dst, !net_eq(geneve->net, dev_net(geneve->dev)), !test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags), @@ -1424,7 +1450,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, geneve->cfg.port_min, geneve->cfg.port_max, true); - dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock, 0, + dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sk, 0, &saddr, key, sport, geneve->cfg.info.key.tp_dst, prio, use_cache ? @@ -1432,6 +1458,12 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (IS_ERR(dst)) return PTR_ERR(dst); + if (!ipv6_addr_any(&geneve->cfg.info.key.u.ipv6.src) && + !ipv6_addr_equal(&saddr, &geneve->cfg.info.key.u.ipv6.src)) { + dst_release(dst); + return -EADDRNOTAVAIL; + } + err = skb_tunnel_check_pmtu(skb, dst, GENEVE_IPV6_HLEN + info->options_len + geneve_build_gro_hint_opt(geneve, skb), @@ -1480,7 +1512,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) return err; - udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev, + udp_tunnel6_xmit_skb(dst, gs6->sk, skb, dev, &saddr, &key->u.ipv6.dst, prio, ttl, info->key.label, sport, geneve->cfg.info.key.tp_dst, !test_bit(IP_TUNNEL_CSUM_BIT, @@ -1592,7 +1624,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) geneve->cfg.port_min, geneve->cfg.port_max, true); - dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock, 0, + dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sk, 0, &saddr, &info->key, sport, geneve->cfg.info.key.tp_dst, prio, use_cache ? &info->dst_cache : NULL); @@ -1654,10 +1686,10 @@ static void geneve_offload_rx_ports(struct net_device *dev, bool push) list_for_each_entry(gs, &gn->sock_list, list) { if (push) { - udp_tunnel_push_rx_port(dev, gs->sock, + udp_tunnel_push_rx_port(dev, gs->sk, UDP_TUNNEL_TYPE_GENEVE); } else { - udp_tunnel_drop_rx_port(dev, gs->sock, + udp_tunnel_drop_rx_port(dev, gs->sk, UDP_TUNNEL_TYPE_GENEVE); } } @@ -1723,6 +1755,8 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { [IFLA_GENEVE_INNER_PROTO_INHERIT] = { .type = NLA_FLAG }, [IFLA_GENEVE_PORT_RANGE] = NLA_POLICY_EXACT_LEN(sizeof(struct ifla_geneve_port_range)), [IFLA_GENEVE_GRO_HINT] = { .type = NLA_FLAG }, + [IFLA_GENEVE_LOCAL] = { .type = NLA_BE32 }, + [IFLA_GENEVE_LOCAL6] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), }; static int geneve_validate(struct nlattr *tb[], struct nlattr *data[], @@ -1782,7 +1816,45 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[], return 0; } +static bool geneve_saddr_wildcard(const struct ip_tunnel_info *info) +{ + if (ip_tunnel_info_af(info) == AF_INET) { + if (!info->key.u.ipv4.src) + return true; +#if IS_ENABLED(CONFIG_IPV6) + } else { + if (ipv6_addr_any(&info->key.u.ipv6.src)) + return true; +#endif + } + + return false; +} + +static bool geneve_saddr_conflict(const struct ip_tunnel_info *a, + const struct ip_tunnel_info *b) +{ + if (ip_tunnel_info_af(a) != ip_tunnel_info_af(b)) + return false; + + if (geneve_saddr_wildcard(a) || geneve_saddr_wildcard(b)) + return true; + + if (ip_tunnel_info_af(a) == AF_INET) { + if (a->key.u.ipv4.src == b->key.u.ipv4.src) + return true; +#if IS_ENABLED(CONFIG_IPV6) + } else { + if (ipv6_addr_equal(&a->key.u.ipv6.src, &b->key.u.ipv6.src)) + return true; +#endif + } + + return false; +} + static struct geneve_dev *geneve_find_dev(struct geneve_net *gn, + const struct geneve_config *cfg, const struct ip_tunnel_info *info, bool *tun_on_same_port, bool *tun_collect_md) @@ -1792,8 +1864,10 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn, *tun_on_same_port = false; *tun_collect_md = false; list_for_each_entry(geneve, &gn->geneve_list, next) { - if (info->key.tp_dst == geneve->cfg.info.key.tp_dst) { - *tun_collect_md = geneve->cfg.collect_md; + if (info->key.tp_dst == geneve->cfg.info.key.tp_dst && + (cfg->dualstack || geneve->cfg.dualstack || + geneve_saddr_conflict(info, &geneve->cfg.info))) { + *tun_collect_md |= geneve->cfg.collect_md; *tun_on_same_port = true; } if (info->key.tun_id == geneve->cfg.info.key.tun_id && @@ -1809,7 +1883,12 @@ static bool is_tnl_info_zero(const struct ip_tunnel_info *info) return !(info->key.tun_id || info->key.tos || !ip_tunnel_flags_empty(info->key.tun_flags) || info->key.ttl || info->key.label || info->key.tp_src || - memchr_inv(&info->key.u, 0, sizeof(info->key.u))); +#if IS_ENABLED(CONFIG_IPV6) + (ip_tunnel_info_af(info) == AF_INET6 && + !ipv6_addr_any(&info->key.u.ipv6.dst)) || +#endif + (ip_tunnel_info_af(info) == AF_INET && + info->key.u.ipv4.dst)); } static bool geneve_dst_addr_equal(struct ip_tunnel_info *a, @@ -1840,7 +1919,7 @@ static int geneve_configure(struct net *net, struct net_device *dev, geneve->net = net; geneve->dev = dev; - t = geneve_find_dev(gn, info, &tun_on_same_port, &tun_collect_md); + t = geneve_find_dev(gn, cfg, info, &tun_on_same_port, &tun_collect_md); if (t) return -EBUSY; @@ -1858,13 +1937,13 @@ static int geneve_configure(struct net *net, struct net_device *dev, if (cfg->collect_md) { if (tun_on_same_port) { NL_SET_ERR_MSG(extack, - "There can be only one externally controlled device on a destination port"); + "There can be only one externally controlled device on a destination port and a source address"); return -EPERM; } } else { if (tun_collect_md) { NL_SET_ERR_MSG(extack, - "There already exists an externally controlled device on this destination port"); + "There already exists an externally controlled device on this destination port and the source address"); return -EPERM; } } @@ -1901,9 +1980,20 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], struct ip_tunnel_info *info = &cfg->info; int attrtype; - if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) { + if (data[IFLA_GENEVE_COLLECT_METADATA]) { + if (changelink) { + attrtype = IFLA_GENEVE_COLLECT_METADATA; + goto change_notsup; + } + + cfg->collect_md = true; + cfg->dualstack = true; + } + + if ((data[IFLA_GENEVE_LOCAL] || data[IFLA_GENEVE_REMOTE]) && + (data[IFLA_GENEVE_LOCAL6] || data[IFLA_GENEVE_REMOTE6])) { NL_SET_ERR_MSG(extack, - "Cannot specify both IPv4 and IPv6 Remote addresses"); + "Cannot specify both IPv4/IPv6 Remote/Local addresses"); return -EINVAL; } @@ -1925,6 +2015,8 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], if (data[IFLA_GENEVE_REMOTE6]) { #if IS_ENABLED(CONFIG_IPV6) + int addr_type; + if (changelink && (ip_tunnel_info_af(info) == AF_INET)) { attrtype = IFLA_GENEVE_REMOTE6; goto change_notsup; @@ -1934,13 +2026,13 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], info->key.u.ipv6.dst = nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]); - if (ipv6_addr_type(&info->key.u.ipv6.dst) & - IPV6_ADDR_LINKLOCAL) { + addr_type = ipv6_addr_type(&info->key.u.ipv6.dst); + if (addr_type & IPV6_ADDR_LINKLOCAL) { NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6], "Remote IPv6 address cannot be link-local"); return -EINVAL; } - if (ipv6_addr_is_multicast(&info->key.u.ipv6.dst)) { + if (addr_type & IPV6_ADDR_MULTICAST) { NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_REMOTE6], "Remote IPv6 address cannot be Multicast"); return -EINVAL; @@ -1954,6 +2046,65 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], #endif } + if (data[IFLA_GENEVE_LOCAL]) { + if (changelink) { + __be32 src = nla_get_in_addr(data[IFLA_GENEVE_LOCAL]); + + if (ip_tunnel_info_af(info) == AF_INET6 || + src != info->key.u.ipv4.src) { + attrtype = IFLA_GENEVE_LOCAL; + goto change_notsup; + } + } else { + info->key.u.ipv4.src = nla_get_in_addr(data[IFLA_GENEVE_LOCAL]); + + if (ipv4_is_multicast(info->key.u.ipv4.src)) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL], + "Local IPv4 address cannot be Multicast"); + return -EINVAL; + } + + cfg->dualstack = false; + } + } + + if (data[IFLA_GENEVE_LOCAL6]) { +#if IS_ENABLED(CONFIG_IPV6) + if (changelink) { + struct in6_addr src = nla_get_in6_addr(data[IFLA_GENEVE_LOCAL6]); + + if (ip_tunnel_info_af(info) == AF_INET || + !ipv6_addr_equal(&src, &info->key.u.ipv6.src)) { + attrtype = IFLA_GENEVE_LOCAL6; + goto change_notsup; + } + } else { + int addr_type; + + info->mode = IP_TUNNEL_INFO_IPV6; + info->key.u.ipv6.src = nla_get_in6_addr(data[IFLA_GENEVE_LOCAL6]); + + addr_type = ipv6_addr_type(&info->key.u.ipv6.src); + if (addr_type & IPV6_ADDR_LINKLOCAL) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL6], + "Local IPv6 address cannot be link-local"); + return -EINVAL; + } + if (addr_type & IPV6_ADDR_MULTICAST) { + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL6], + "Local IPv6 address cannot be Multicast"); + return -EINVAL; + } + + cfg->dualstack = false; + } +#else + NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL6], + "IPv6 support not enabled in the kernel"); + return -EPFNOSUPPORT; +#endif + } + if (data[IFLA_GENEVE_ID]) { __u32 vni; __u8 tvni[3]; @@ -2018,14 +2169,6 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[], cfg->port_max = ntohs(p->high); } - if (data[IFLA_GENEVE_COLLECT_METADATA]) { - if (changelink) { - attrtype = IFLA_GENEVE_COLLECT_METADATA; - goto change_notsup; - } - cfg->collect_md = true; - } - if (data[IFLA_GENEVE_UDP_CSUM]) { if (changelink) { attrtype = IFLA_GENEVE_UDP_CSUM; @@ -2146,6 +2289,7 @@ static int geneve_newlink(struct net_device *dev, .use_udp6_rx_checksums = false, .ttl_inherit = false, .collect_md = false, + .dualstack = false, .port_min = 1, .port_max = USHRT_MAX, }; @@ -2183,12 +2327,12 @@ static void geneve_quiesce(struct geneve_dev *geneve, struct geneve_sock **gs4, *gs4 = rtnl_dereference(geneve->sock4); rcu_assign_pointer(geneve->sock4, NULL); if (*gs4) - rcu_assign_sk_user_data((*gs4)->sock->sk, NULL); + rcu_assign_sk_user_data((*gs4)->sk, NULL); #if IS_ENABLED(CONFIG_IPV6) *gs6 = rtnl_dereference(geneve->sock6); rcu_assign_pointer(geneve->sock6, NULL); if (*gs6) - rcu_assign_sk_user_data((*gs6)->sock->sk, NULL); + rcu_assign_sk_user_data((*gs6)->sk, NULL); #else *gs6 = NULL; #endif @@ -2201,13 +2345,12 @@ static void geneve_unquiesce(struct geneve_dev *geneve, struct geneve_sock *gs4, { rcu_assign_pointer(geneve->sock4, gs4); if (gs4) - rcu_assign_sk_user_data(gs4->sock->sk, gs4); + rcu_assign_sk_user_data(gs4->sk, gs4); #if IS_ENABLED(CONFIG_IPV6) rcu_assign_pointer(geneve->sock6, gs6); if (gs6) - rcu_assign_sk_user_data(gs6->sock->sk, gs6); + rcu_assign_sk_user_data(gs6->sk, gs6); #endif - synchronize_net(); } static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], @@ -2255,6 +2398,7 @@ static size_t geneve_get_size(const struct net_device *dev) { return nla_total_size(sizeof(__u32)) + /* IFLA_GENEVE_ID */ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */ + nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_LOCAL{6} */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_DF */ @@ -2310,6 +2454,24 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) #endif } + if (!geneve->cfg.dualstack) { + if (ip_tunnel_info_af(info) == AF_INET) { + if ((info->key.u.ipv4.src || + geneve->cfg.collect_md) && + nla_put_in_addr(skb, IFLA_GENEVE_LOCAL, + info->key.u.ipv4.src)) + goto nla_put_failure; +#if IS_ENABLED(CONFIG_IPV6) + } else { + if ((!ipv6_addr_any(&info->key.u.ipv6.src) || + geneve->cfg.collect_md) && + nla_put_in6_addr(skb, IFLA_GENEVE_LOCAL6, + &info->key.u.ipv6.src)) + goto nla_put_failure; +#endif + } + } + if (nla_put_u8(skb, IFLA_GENEVE_TTL, info->key.ttl) || nla_put_u8(skb, IFLA_GENEVE_TOS, info->key.tos) || nla_put_be32(skb, IFLA_GENEVE_LABEL, info->key.label)) @@ -2376,6 +2538,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, .use_udp6_rx_checksums = true, .ttl_inherit = false, .collect_md = true, + .dualstack = true, .port_min = 1, .port_max = USHRT_MAX, }; diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 5150f2e4f66b..a60ef32b35b8 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -885,8 +885,8 @@ static void gtp_encap_disable_sock(struct sock *sk) static void gtp_encap_disable(struct gtp_dev *gtp) { if (gtp->sk_created) { - udp_tunnel_sock_release(gtp->sk0->sk_socket); - udp_tunnel_sock_release(gtp->sk1u->sk_socket); + udp_tunnel_sock_release(gtp->sk0); + udp_tunnel_sock_release(gtp->sk1u); gtp->sk_created = false; gtp->sk0 = NULL; gtp->sk1u = NULL; @@ -1434,7 +1434,7 @@ static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp, tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); return sock->sk; } @@ -1451,7 +1451,7 @@ static int gtp_create_sockets(struct gtp_dev *gtp, const struct nlattr *nla, sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp, nla, family); if (IS_ERR(sk1u)) { - udp_tunnel_sock_release(sk0->sk_socket); + udp_tunnel_sock_release(sk0); return PTR_ERR(sk1u); } @@ -1689,7 +1689,7 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; - setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); + setup_udp_tunnel_sock(sock_net(sock->sk), sk, &tuncfg); out_rel_sock: release_sock(sock->sk); diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 1be8620ad397..7ad12dc7845c 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -286,7 +286,7 @@ void ipvlan_process_multicast(struct work_struct *work) if (tx_pkt) ret = dev_forward_skb(ipvlan->dev, nskb); else - ret = netif_rx(nskb); + ret = netif_receive_skb(nskb); } ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true); local_bh_enable(); diff --git a/drivers/net/mctp/mctp-i2c.c b/drivers/net/mctp/mctp-i2c.c index ee2913758e54..b02476d338bb 100644 --- a/drivers/net/mctp/mctp-i2c.c +++ b/drivers/net/mctp/mctp-i2c.c @@ -1101,8 +1101,8 @@ static struct notifier_block mctp_i2c_notifier = { }; static const struct i2c_device_id mctp_i2c_id[] = { - { "mctp-i2c-interface" }, - {} + { .name = "mctp-i2c-interface" }, + { } }; MODULE_DEVICE_TABLE(i2c, mctp_i2c_id); diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index c71132f33f84..c591eec8e97a 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -254,7 +254,7 @@ config MDIO_BUS_MUX_BCM_IPROC config MDIO_BUS_MUX_GPIO tristate "GPIO controlled MDIO bus multiplexers" - depends on OF_GPIO && OF_MDIO + depends on OF_MDIO select MDIO_BUS_MUX help This module provides a driver for MDIO bus multiplexers that diff --git a/drivers/net/mdio/mdio-i2c.c b/drivers/net/mdio/mdio-i2c.c index ed20352a589a..b88f63234b4e 100644 --- a/drivers/net/mdio/mdio-i2c.c +++ b/drivers/net/mdio/mdio-i2c.c @@ -419,6 +419,50 @@ static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad, return 0; } +static int i2c_mii_probe_rollball(struct i2c_adapter *i2c) +{ + u8 data_buf[] = { ROLLBALL_DATA_ADDR, 0x01, 0x00, 0x00 }; + u8 cmd_buf[] = { ROLLBALL_CMD_ADDR, ROLLBALL_CMD_READ }; + u8 cmd_addr = ROLLBALL_CMD_ADDR; + struct i2c_msg msgs[2]; + u8 result; + int ret; + int i; + + msgs[0].addr = ROLLBALL_PHY_I2C_ADDR; + msgs[0].flags = 0; + msgs[0].len = sizeof(data_buf); + msgs[0].buf = data_buf; + msgs[1].addr = ROLLBALL_PHY_I2C_ADDR; + msgs[1].flags = 0; + msgs[1].len = sizeof(cmd_buf); + msgs[1].buf = cmd_buf; + + ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return -ENODEV; + + msgs[0].addr = ROLLBALL_PHY_I2C_ADDR; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &cmd_addr; + msgs[1].addr = ROLLBALL_PHY_I2C_ADDR; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &result; + + for (i = 0; i < 10; i++) { + msleep(20); + ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return -ENODEV; + if (result == ROLLBALL_CMD_DONE) + return 0; + } + + return -ENODEV; +} + static int i2c_mii_init_rollball(struct i2c_adapter *i2c) { struct i2c_msg msg; @@ -438,11 +482,11 @@ static int i2c_mii_init_rollball(struct i2c_adapter *i2c) ret = i2c_transfer(i2c, &msg, 1); if (ret < 0) - return ret; - else if (ret != 1) + return -ENODEV; + if (ret != 1) return -EIO; - else - return 0; + + return i2c_mii_probe_rollball(i2c); } static bool mdio_i2c_check_functionality(struct i2c_adapter *i2c, @@ -487,9 +531,10 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, case MDIO_I2C_ROLLBALL: ret = i2c_mii_init_rollball(i2c); if (ret < 0) { - dev_err(parent, - "Cannot initialize RollBall MDIO I2C protocol: %d\n", - ret); + if (ret != -ENODEV) + dev_err(parent, + "Cannot initialize RollBall MDIO I2C protocol: %d\n", + ret); mdiobus_free(mii); return ERR_PTR(ret); } diff --git a/drivers/net/mdio/mdio-mscc-miim.c b/drivers/net/mdio/mdio-mscc-miim.c index 944efd33da6d..259944d37fbd 100644 --- a/drivers/net/mdio/mdio-mscc-miim.c +++ b/drivers/net/mdio/mdio-mscc-miim.c @@ -330,7 +330,7 @@ static int mscc_miim_probe(struct platform_device *pdev) ret = of_mdiobus_register(bus, np); if (ret < 0) { - dev_err(dev, "Cannot register MDIO bus (%d)\n", ret); + dev_err_probe(dev, ret, "Cannot register MDIO bus\n"); goto out_disable_clk; } diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c index 8d5fb014ca06..892ed3780a65 100644 --- a/drivers/net/mdio/mdio-realtek-rtl9300.c +++ b/drivers/net/mdio/mdio-realtek-rtl9300.c @@ -1,11 +1,40 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * MDIO controller for RTL9300 switches with integrated SoC. + * Realtek switches of the Otto series (RTL838x, RTL839x, RTL930x and RTL931x SoCs) have multiple + * integrated MDIO controllers. This driver targets the ethernet MDIO controller. It serves only + * 1G/2.5G/10G ethernet PHYs attached to up to 4 individual buses. * - * The MDIO communication is abstracted by the switch. At the software level - * communication uses the switch port to address the PHY. We work out the - * mapping based on the MDIO bus described in device tree and phandles on the - * ethernet-ports property. + * The controller is programmed through MMIO. The MDIO communication is abstracted by the hardware + * and uses the switch port number for its addressing. For this to work, mapping registers need to + * be setup in advance. With that the controller translates each port based I/O operation into the + * physical bus and address. This gives the following end-to-end communication + * + * +----------+ +----------+ +----------+ +----------+ + * | phydev | ... | phydev | | phydev | ... | phydev | + * +----------+ +----------+ +----------+ +----------+ + * | | | | + * mii_bus 0 +------------------+ +------------------+ mii_bus 1 + * | | + * +-----------------------------------------------------+ + * | MDIO driver | + * | translate bus/address -> port | + * +-----------------------------------------------------+ + * | Software + * - - - - - - - - - - - - - - - - - - - - - - - - - + * | Hardware + * +-----------------------------------------------------+ + * | MDIO controller | + * | translate port -> bus/address | + * +-----------------------------------------------------+ + * | | + * bus 0 +------------------+ +------------------+ bus 1 + * | | | | + * +----------+ +----------+ +----------+ +----------+ + * | PHY 0/1 | ... | PHY 0/31 | | PHY 1/1 | ... | PHY 1/31 | + * +----------+ +----------+ +----------+ +----------+ + * + * The driver works out the mapping based on the MDIO bus described in device tree and phandles on + * the ethernet-ports property. */ #include @@ -22,33 +51,79 @@ #include #include -#define SMI_GLB_CTRL 0xca00 -#define GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) -#define SMI_PORT0_15_POLLING_SEL 0xca08 -#define SMI_ACCESS_PHY_CTRL_0 0xcb70 -#define SMI_ACCESS_PHY_CTRL_1 0xcb74 -#define PHY_CTRL_REG_ADDR GENMASK(24, 20) -#define PHY_CTRL_PARK_PAGE GENMASK(19, 15) -#define PHY_CTRL_MAIN_PAGE GENMASK(14, 3) -#define PHY_CTRL_WRITE BIT(2) -#define PHY_CTRL_READ 0 -#define PHY_CTRL_TYPE_C45 BIT(1) -#define PHY_CTRL_TYPE_C22 0 -#define PHY_CTRL_CMD BIT(0) -#define PHY_CTRL_FAIL BIT(25) -#define SMI_ACCESS_PHY_CTRL_2 0xcb78 -#define PHY_CTRL_INDATA GENMASK(31, 16) -#define PHY_CTRL_DATA GENMASK(15, 0) -#define SMI_ACCESS_PHY_CTRL_3 0xcb7c -#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) -#define PHY_CTRL_MMD_REG GENMASK(15, 0) -#define SMI_PORT0_5_ADDR_CTRL 0xcb80 +#define RTL9300_NUM_BUSES 4 +#define RTL9300_NUM_PAGES 4096 +#define RTL9300_NUM_PORTS 28 +#define RTL9300_SMI_GLB_CTRL 0xca00 +#define RTL9300_GLB_CTRL_INTF_SEL(intf) BIT(16 + (intf)) +#define RTL9300_SMI_PORT0_15_POLLING_SEL 0xca08 +#define RTL9300_SMI_ACCESS_PHY_CTRL_0 0xcb70 +#define RTL9300_SMI_ACCESS_PHY_CTRL_1 0xcb74 +#define RTL9300_PHY_CTRL_REG_ADDR GENMASK(24, 20) +#define RTL9300_PHY_CTRL_PARK_PAGE GENMASK(19, 15) +#define RTL9300_PHY_CTRL_MAIN_PAGE GENMASK(14, 3) +#define RTL9300_PHY_CTRL_WRITE BIT(2) +#define RTL9300_PHY_CTRL_READ 0 +#define RTL9300_PHY_CTRL_TYPE_C45 BIT(1) +#define RTL9300_PHY_CTRL_TYPE_C22 0 +#define RTL9300_PHY_CTRL_FAIL BIT(25) +#define RTL9300_SMI_ACCESS_PHY_CTRL_2 0xcb78 +#define RTL9300_PHY_CTRL_INDATA GENMASK(31, 16) +#define RTL9300_PHY_CTRL_DATA GENMASK(15, 0) +#define RTL9300_SMI_ACCESS_PHY_CTRL_3 0xcb7c +#define RTL9300_SMI_PORT0_5_ADDR_CTRL 0xcb80 -#define MAX_PORTS 28 -#define MAX_SMI_BUSSES 4 -#define MAX_SMI_ADDR 0x1f +#define RTL9310_NUM_BUSES 4 +#define RTL9310_NUM_PAGES 8192 +#define RTL9310_NUM_PORTS 56 +#define RTL9310_SMI_GLB_CTRL1 0x0cbc +#define RTL9310_SMI_GLB_FMT_SEL_C45(intf) BIT((intf) * 2 + 1) +#define RTL9310_SMI_INDRT_ACCESS_CTRL_0 0x0c00 +#define RTL9310_PHY_CTRL_REG_ADDR GENMASK(10, 6) +#define RTL9310_PHY_CTRL_MAIN_PAGE GENMASK(23, 11) +#define RTL9310_PHY_CTRL_READ 0 +#define RTL9310_PHY_CTRL_WRITE BIT(4) +#define RTL9310_PHY_CTRL_TYPE_C45 BIT(3) +#define RTL9310_PHY_CTRL_TYPE_C22 0 +#define RTL9310_PHY_CTRL_FAIL BIT(1) +#define RTL9310_SMI_INDRT_ACCESS_BC_PHYID_CTRL 0x0c14 +#define RTL9310_BC_PORT_ID GENMASK(10, 5) +#define RTL9310_SMI_INDRT_ACCESS_CTRL_1 0x0c04 +#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_LOW 0x0c08 +#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_HIGH 0x0c0c +#define RTL9310_SMI_INDRT_ACCESS_CTRL_3 0x0c10 /* I/O fields flipped */ +#define RTL9310_PHY_CTRL_DATA GENMASK(31, 16) +#define RTL9310_PHY_CTRL_INDATA GENMASK(15, 0) +#define RTL9310_SMI_INDRT_ACCESS_MMD_CTRL 0x0c18 +#define RTL9310_SMI_PORT_ADDR_CTRL 0x0c74 +#define RTL9310_SMI_PORT_POLLING_SEL 0x0c9c -struct rtl9300_mdio_priv { +#define PHY_CTRL_CMD BIT(0) +#define PHY_CTRL_MMD_DEVAD GENMASK(20, 16) +#define PHY_CTRL_MMD_REG GENMASK(15, 0) + +#define MAP_ADDRS_PER_REG 6 +#define MAP_BITS_PER_ADDR 5 +#define MAP_BITS_PER_BUS 2 +#define MAP_BUSES_PER_REG 16 +#define MAX_PORTS 56 +#define MAX_SMI_BUSSES 4 +#define RAW_PAGE(priv) ((priv)->info->num_pages - 1) + + +struct otto_emdio_cmd_regs { + u32 c22_data; + u32 c45_data; + u32 io_data; + u32 port_mask_low; + /* additional registers for high port count models RTL839x/RTL931x */ + u32 port_mask_high; + u32 broadcast; + u32 ext_page; +}; + +struct otto_emdio_priv { + const struct otto_emdio_info *info; struct regmap *regmap; struct mutex lock; /* protect HW access */ DECLARE_BITMAP(valid_ports, MAX_PORTS); @@ -58,20 +133,37 @@ struct rtl9300_mdio_priv { struct mii_bus *bus[MAX_SMI_BUSSES]; }; -struct rtl9300_mdio_chan { - struct rtl9300_mdio_priv *priv; +struct otto_emdio_info { + u32 addr_map_base; + u32 bus_map_base; + u32 cmd_fail; + u32 cmd_read; + u32 cmd_write; + struct otto_emdio_cmd_regs cmd_regs; + u8 num_buses; + u8 num_ports; + u16 num_pages; + int (*setup_controller)(struct otto_emdio_priv *priv); + int (*read_c22)(struct mii_bus *bus, int port, int regnum, u32 *value); + int (*read_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u32 *value); + int (*write_c22)(struct mii_bus *bus, int port, int regnum, u16 value); + int (*write_c45)(struct mii_bus *bus, int port, int dev_addr, int regnum, u16 value); +}; + +struct otto_emdio_chan { + struct otto_emdio_priv *priv; u8 mdio_bus; }; -static int rtl9300_mdio_phy_to_port(struct mii_bus *bus, int phy_id) +static int otto_emdio_phy_to_port(struct mii_bus *bus, int phy_id) { - struct rtl9300_mdio_chan *chan = bus->priv; - struct rtl9300_mdio_priv *priv; + struct otto_emdio_chan *chan = bus->priv; + struct otto_emdio_priv *priv; int i; priv = chan->priv; - for_each_set_bit(i, priv->valid_ports, MAX_PORTS) + for_each_set_bit(i, priv->valid_ports, priv->info->num_ports) if (priv->smi_bus[i] == chan->mdio_bus && priv->smi_addr[i] == phy_id) return i; @@ -79,270 +171,320 @@ static int rtl9300_mdio_phy_to_port(struct mii_bus *bus, int phy_id) return -ENOENT; } -static int rtl9300_mdio_wait_ready(struct rtl9300_mdio_priv *priv) +static struct otto_emdio_priv *otto_emdio_bus_to_priv(struct mii_bus *bus) { - struct regmap *regmap = priv->regmap; - u32 val; + struct otto_emdio_chan *chan = bus->priv; + + return chan->priv; +} + +static int otto_emdio_run_cmd(struct mii_bus *bus, u32 cmd, + struct otto_emdio_cmd_regs *cmd_data) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + const struct otto_emdio_info *info = priv->info; + u32 cmdstate; + int ret; + + /* Defensive pre check just in case something goes horrible wrong */ + ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data, + cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000); + if (ret) + return ret; + + /* Fill all registers. Hardware will read only the needed bits depending on command */ + if (info->cmd_regs.port_mask_high) { + /* Fill extra registers for high port count models */ + ret = regmap_write(priv->regmap, info->cmd_regs.broadcast, cmd_data->broadcast); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, info->cmd_regs.ext_page, cmd_data->ext_page); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, + info->cmd_regs.port_mask_high, cmd_data->port_mask_high); + if (ret) + return ret; + } + + ret = regmap_write(priv->regmap, info->cmd_regs.port_mask_low, cmd_data->port_mask_low); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, info->cmd_regs.io_data, cmd_data->io_data); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, info->cmd_regs.c45_data, cmd_data->c45_data); + if (ret) + return ret; + + /* C22 data and command bits share the same register. */ + ret = regmap_write(priv->regmap, info->cmd_regs.c22_data, + cmd_data->c22_data | cmd | PHY_CTRL_CMD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(priv->regmap, info->cmd_regs.c22_data, + cmdstate, !(cmdstate & PHY_CTRL_CMD), 10, 1000); + if (ret) + return ret; + + return cmdstate & info->cmd_fail ? -ENXIO : 0; +} + +static int otto_emdio_read_cmd(struct mii_bus *bus, u32 cmd, + struct otto_emdio_cmd_regs *cmd_data, u32 mask, u32 *value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + int ret; + + lockdep_assert_held(&priv->lock); + ret = otto_emdio_run_cmd(bus, cmd | priv->info->cmd_read, cmd_data); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, priv->info->cmd_regs.io_data, value); + if (ret) + return ret; + + *value = field_get(mask, *value); + + return 0; +} + +static int otto_emdio_write_cmd(struct mii_bus *bus, u32 cmd, + struct otto_emdio_cmd_regs *cmd_data) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); lockdep_assert_held(&priv->lock); - return regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1, - val, !(val & PHY_CTRL_CMD), 10, 1000); + return otto_emdio_run_cmd(bus, cmd | priv->info->cmd_write, cmd_data); } -static int rtl9300_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) +static int otto_emdio_9300_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value) { - struct rtl9300_mdio_chan *chan = bus->priv; - struct rtl9300_mdio_priv *priv; - struct regmap *regmap; - int port; - u32 val; - int err; + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) | + FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port), + }; - priv = chan->priv; - regmap = priv->regmap; + return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, &cmd_data, + RTL9300_PHY_CTRL_DATA, value); +} - port = rtl9300_mdio_phy_to_port(bus, phy_id); +static int otto_emdio_9300_write_c22(struct mii_bus *bus, int port, int regnum, u16 value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL9300_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL9300_PHY_CTRL_PARK_PAGE, 0x1f) | + FIELD_PREP(RTL9300_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value), + .port_mask_low = BIT(port), + }; + + return otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C22, &cmd_data); +} + +static int otto_emdio_9300_read_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u32 *value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, port), + }; + + return otto_emdio_read_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data, + RTL9300_PHY_CTRL_DATA, value); +} + +static int otto_emdio_9300_write_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u16 value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL9300_PHY_CTRL_INDATA, value), + .port_mask_low = BIT(port), + }; + + return otto_emdio_write_cmd(bus, RTL9300_PHY_CTRL_TYPE_C45, &cmd_data); +} + +static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u32 *value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port), + .c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + }; + + return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data, + RTL9310_PHY_CTRL_DATA, value); +} + +static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum, u16 value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + struct otto_emdio_cmd_regs cmd_data = { + .c22_data = FIELD_PREP(RTL9310_PHY_CTRL_REG_ADDR, regnum) | + FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + .io_data = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value), + .port_mask_high = (u32)(BIT_ULL(port) >> 32), + .port_mask_low = (u32)(BIT_ULL(port)), + }; + + return otto_emdio_write_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data); +} + +static int otto_emdio_9310_read_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u32 *value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port), + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + }; + + return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C45, &cmd_data, + RTL9310_PHY_CTRL_DATA, value); +} + +static int otto_emdio_9310_write_c45(struct mii_bus *bus, int port, + int dev_addr, int regnum, u16 value) +{ + struct otto_emdio_cmd_regs cmd_data = { + .c45_data = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | + FIELD_PREP(PHY_CTRL_MMD_REG, regnum), + .io_data = FIELD_PREP(RTL9310_PHY_CTRL_INDATA, value), + .port_mask_high = (u32)(BIT_ULL(port) >> 32), + .port_mask_low = (u32)(BIT_ULL(port)), + }; + + return otto_emdio_write_cmd(bus, RTL9310_PHY_CTRL_TYPE_C45, &cmd_data); +} + +static int otto_emdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + int ret, port; + u32 value; + + port = otto_emdio_phy_to_port(bus, phy_id); if (port < 0) return port; - mutex_lock(&priv->lock); - err = rtl9300_mdio_wait_ready(priv); - if (err) - goto out_err; + scoped_guard(mutex, &priv->lock) + ret = priv->info->read_c22(bus, port, regnum, &value); - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, FIELD_PREP(PHY_CTRL_INDATA, port)); - if (err) - goto out_err; - - val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) | - FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) | - FIELD_PREP(PHY_CTRL_MAIN_PAGE, 0xfff) | - PHY_CTRL_READ | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD; - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val); - if (err) - goto out_err; - - err = rtl9300_mdio_wait_ready(priv); - if (err) - goto out_err; - - err = regmap_read(regmap, SMI_ACCESS_PHY_CTRL_2, &val); - if (err) - goto out_err; - - mutex_unlock(&priv->lock); - return FIELD_GET(PHY_CTRL_DATA, val); - -out_err: - mutex_unlock(&priv->lock); - return err; + return ret ? ret : value; } -static int rtl9300_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value) +static int otto_emdio_write_c22(struct mii_bus *bus, int phy_id, int regnum, u16 value) { - struct rtl9300_mdio_chan *chan = bus->priv; - struct rtl9300_mdio_priv *priv; - struct regmap *regmap; - int port; - u32 val; - int err; + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + int ret, port; - priv = chan->priv; - regmap = priv->regmap; - - port = rtl9300_mdio_phy_to_port(bus, phy_id); + port = otto_emdio_phy_to_port(bus, phy_id); if (port < 0) return port; - mutex_lock(&priv->lock); - err = rtl9300_mdio_wait_ready(priv); - if (err) - goto out_err; + scoped_guard(mutex, &priv->lock) + ret = priv->info->write_c22(bus, port, regnum, value); - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_0, BIT(port)); - if (err) - goto out_err; + return ret; +} - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, FIELD_PREP(PHY_CTRL_INDATA, value)); - if (err) - goto out_err; +static int otto_emdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + int ret, port; + u32 value; - val = FIELD_PREP(PHY_CTRL_REG_ADDR, regnum) | - FIELD_PREP(PHY_CTRL_PARK_PAGE, 0x1f) | - FIELD_PREP(PHY_CTRL_MAIN_PAGE, 0xfff) | - PHY_CTRL_WRITE | PHY_CTRL_TYPE_C22 | PHY_CTRL_CMD; - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, val); - if (err) - goto out_err; + port = otto_emdio_phy_to_port(bus, phy_id); + if (port < 0) + return port; - err = regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1, - val, !(val & PHY_CTRL_CMD), 10, 100); - if (err) - goto out_err; + scoped_guard(mutex, &priv->lock) + ret = priv->info->read_c45(bus, port, dev_addr, regnum, &value); - if (val & PHY_CTRL_FAIL) { - err = -ENXIO; - goto out_err; + return ret ? ret : value; +} + +static int otto_emdio_write_c45(struct mii_bus *bus, int phy_id, + int dev_addr, int regnum, u16 value) +{ + struct otto_emdio_priv *priv = otto_emdio_bus_to_priv(bus); + int ret, port; + + port = otto_emdio_phy_to_port(bus, phy_id); + if (port < 0) + return port; + + scoped_guard(mutex, &priv->lock) + ret = priv->info->write_c45(bus, port, dev_addr, regnum, value); + + return ret; +} + +static int otto_emdio_write_mapping(struct otto_emdio_priv *priv, u32 base, u32 port, + u32 vals_per_reg, u32 bits_per_val, u32 value) +{ + u32 shift = (port % vals_per_reg) * bits_per_val; + u32 reg = base + (port / vals_per_reg) * 4; + u32 mask = GENMASK(bits_per_val - 1, 0); + + return regmap_update_bits(priv->regmap, reg, mask << shift, value << shift); +} + +static int otto_emdio_setup_topology(struct otto_emdio_priv *priv) +{ + const struct otto_emdio_info *info = priv->info; + u32 port; + int ret; + + for_each_set_bit(port, priv->valid_ports, info->num_ports) { + if (info->bus_map_base) { + ret = otto_emdio_write_mapping(priv, info->bus_map_base, port, + MAP_BUSES_PER_REG, MAP_BITS_PER_BUS, + priv->smi_bus[port]); + if (ret) + return ret; + } + if (info->addr_map_base) { + ret = otto_emdio_write_mapping(priv, info->addr_map_base, port, + MAP_ADDRS_PER_REG, MAP_BITS_PER_ADDR, + priv->smi_addr[port]); + if (ret) + return ret; + } } - mutex_unlock(&priv->lock); return 0; - -out_err: - mutex_unlock(&priv->lock); - return err; } -static int rtl9300_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr, int regnum) -{ - struct rtl9300_mdio_chan *chan = bus->priv; - struct rtl9300_mdio_priv *priv; - struct regmap *regmap; - int port; - u32 val; - int err; - - priv = chan->priv; - regmap = priv->regmap; - - port = rtl9300_mdio_phy_to_port(bus, phy_id); - if (port < 0) - return port; - - mutex_lock(&priv->lock); - err = rtl9300_mdio_wait_ready(priv); - if (err) - goto out_err; - - val = FIELD_PREP(PHY_CTRL_INDATA, port); - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, val); - if (err) - goto out_err; - - val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | - FIELD_PREP(PHY_CTRL_MMD_REG, regnum); - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_3, val); - if (err) - goto out_err; - - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, - PHY_CTRL_READ | PHY_CTRL_TYPE_C45 | PHY_CTRL_CMD); - if (err) - goto out_err; - - err = rtl9300_mdio_wait_ready(priv); - if (err) - goto out_err; - - err = regmap_read(regmap, SMI_ACCESS_PHY_CTRL_2, &val); - if (err) - goto out_err; - - mutex_unlock(&priv->lock); - return FIELD_GET(PHY_CTRL_DATA, val); - -out_err: - mutex_unlock(&priv->lock); - return err; -} - -static int rtl9300_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr, - int regnum, u16 value) -{ - struct rtl9300_mdio_chan *chan = bus->priv; - struct rtl9300_mdio_priv *priv; - struct regmap *regmap; - int port; - u32 val; - int err; - - priv = chan->priv; - regmap = priv->regmap; - - port = rtl9300_mdio_phy_to_port(bus, phy_id); - if (port < 0) - return port; - - mutex_lock(&priv->lock); - err = rtl9300_mdio_wait_ready(priv); - if (err) - goto out_err; - - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_0, BIT(port)); - if (err) - goto out_err; - - val = FIELD_PREP(PHY_CTRL_INDATA, value); - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_2, val); - if (err) - goto out_err; - - val = FIELD_PREP(PHY_CTRL_MMD_DEVAD, dev_addr) | - FIELD_PREP(PHY_CTRL_MMD_REG, regnum); - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_3, val); - if (err) - goto out_err; - - err = regmap_write(regmap, SMI_ACCESS_PHY_CTRL_1, - PHY_CTRL_TYPE_C45 | PHY_CTRL_WRITE | PHY_CTRL_CMD); - if (err) - goto out_err; - - err = regmap_read_poll_timeout(regmap, SMI_ACCESS_PHY_CTRL_1, - val, !(val & PHY_CTRL_CMD), 10, 100); - if (err) - goto out_err; - - if (val & PHY_CTRL_FAIL) { - err = -ENXIO; - goto out_err; - } - - mutex_unlock(&priv->lock); - return 0; - -out_err: - mutex_unlock(&priv->lock); - return err; -} - -static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv) +static int otto_emdio_9300_setup_controller(struct otto_emdio_priv *priv) { u32 glb_ctrl_mask = 0, glb_ctrl_val = 0; struct regmap *regmap = priv->regmap; - u32 port_addr[5] = { 0 }; - u32 poll_sel[2] = { 0 }; int i, err; - /* Associate the port with the SMI interface and PHY */ - for_each_set_bit(i, priv->valid_ports, MAX_PORTS) { - int pos; - - pos = (i % 6) * 5; - port_addr[i / 6] |= (priv->smi_addr[i] & 0x1f) << pos; - - pos = (i % 16) * 2; - poll_sel[i / 16] |= (priv->smi_bus[i] & 0x3) << pos; - } - /* Put the interfaces into C45 mode if required */ glb_ctrl_mask = GENMASK(19, 16); - for (i = 0; i < MAX_SMI_BUSSES; i++) + for (i = 0; i < priv->info->num_buses; i++) if (priv->smi_bus_is_c45[i]) - glb_ctrl_val |= GLB_CTRL_INTF_SEL(i); + glb_ctrl_val |= RTL9300_GLB_CTRL_INTF_SEL(i); - err = regmap_bulk_write(regmap, SMI_PORT0_5_ADDR_CTRL, - port_addr, 5); - if (err) - return err; - - err = regmap_bulk_write(regmap, SMI_PORT0_15_POLLING_SEL, - poll_sel, 2); - if (err) - return err; - - err = regmap_update_bits(regmap, SMI_GLB_CTRL, + err = regmap_update_bits(regmap, RTL9300_SMI_GLB_CTRL, glb_ctrl_mask, glb_ctrl_val); if (err) return err; @@ -350,29 +492,37 @@ static int rtl9300_mdiobus_init(struct rtl9300_mdio_priv *priv) return 0; } -static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_priv *priv, - struct fwnode_handle *node) +static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv) { - struct rtl9300_mdio_chan *chan; + int i, err; + + /* Put the interfaces into C45 mode if required */ + for (i = 0; i < priv->info->num_buses; i++) { + err = regmap_assign_bits(priv->regmap, RTL9310_SMI_GLB_CTRL1, + RTL9310_SMI_GLB_FMT_SEL_C45(i), + priv->smi_bus_is_c45[i]); + if (err) + return err; + } + + return 0; +} + +static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv, + struct fwnode_handle *node) +{ + struct otto_emdio_chan *chan; struct mii_bus *bus; u32 mdio_bus; int err; err = fwnode_property_read_u32(node, "reg", &mdio_bus); if (err) - return err; + return dev_err_probe(dev, err, "undefined smi bus number\n"); - /* The MDIO accesses from the kernel work with the PHY polling unit in - * the switch. We need to tell the PPU to operate either in GPHY (i.e. - * clause 22) or 10GPHY mode (i.e. clause 45). - * - * We select 10GPHY mode if there is at least one PHY that declares - * compatible = "ethernet-phy-ieee802.3-c45". This does mean we can't - * support both c45 and c22 on the same MDIO bus. - */ - fwnode_for_each_child_node_scoped(node, child) - if (fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45")) - priv->smi_bus_is_c45[mdio_bus] = true; + if (mdio_bus >= priv->info->num_buses) + return dev_err_probe(dev, -EINVAL, + "illegal (dangling) smi bus number %d\n", mdio_bus); bus = devm_mdiobus_alloc_size(dev, sizeof(*chan)); if (!bus) @@ -380,11 +530,11 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri bus->name = "Realtek Switch MDIO Bus"; if (priv->smi_bus_is_c45[mdio_bus]) { - bus->read_c45 = rtl9300_mdio_read_c45; - bus->write_c45 = rtl9300_mdio_write_c45; + bus->read_c45 = otto_emdio_read_c45; + bus->write_c45 = otto_emdio_write_c45; } else { - bus->read = rtl9300_mdio_read_c22; - bus->write = rtl9300_mdio_write_c22; + bus->read = otto_emdio_read_c22; + bus->write = otto_emdio_write_c22; } bus->parent = dev; chan = bus->priv; @@ -400,72 +550,113 @@ static int rtl9300_mdiobus_probe_one(struct device *dev, struct rtl9300_mdio_pri return 0; } +static struct device_node *otto_emdio_get_bus_node(struct device_node *dn) +{ + struct device_node *parent = of_get_parent(dn); + struct device_node *grandparent; + + if (parent && of_node_name_eq(parent, "ethernet-phy-package")) { + grandparent = of_get_parent(parent); + of_node_put(parent); + + return grandparent; + } + + return parent; +} + /* The mdio-controller is part of a switch block so we parse the sibling * ethernet-ports node and build a mapping of the switch port to MDIO bus/addr * based on the phy-handle. */ -static int rtl9300_mdiobus_map_ports(struct device *dev) +static int otto_emdio_map_ports(struct device *dev) { - struct rtl9300_mdio_priv *priv = dev_get_drvdata(dev); + struct device_node *ports_dn, *phy_dn, *bus_dn, *ctrl_dn; + struct otto_emdio_priv *priv = dev_get_drvdata(dev); struct device *parent = dev->parent; - int err; + int addr, err = 0; + u32 bus, pn; - struct fwnode_handle *ports __free(fwnode_handle) = - device_get_named_child_node(parent, "ethernet-ports"); - if (!ports) + ports_dn = of_get_child_by_name(parent->of_node, "ethernet-ports"); + if (!ports_dn) return dev_err_probe(dev, -EINVAL, "%pfwP missing ethernet-ports\n", dev_fwnode(parent)); - fwnode_for_each_child_node_scoped(ports, port) { - struct device_node *mdio_dn; - u32 addr; - u32 bus; - u32 pn; - - struct device_node *phy_dn __free(device_node) = - of_parse_phandle(to_of_node(port), "phy-handle", 0); + for_each_available_child_of_node_scoped(ports_dn, port_dn) { + ctrl_dn = NULL; + bus_dn = NULL; + phy_dn = of_parse_phandle(port_dn, "phy-handle", 0); /* skip ports without phys */ if (!phy_dn) continue; - mdio_dn = phy_dn->parent; + bus_dn = otto_emdio_get_bus_node(phy_dn); + if (!bus_dn) + goto put_nodes; + + ctrl_dn = of_get_parent(bus_dn); /* only map ports that are connected to this mdio-controller */ - if (mdio_dn->parent != dev->of_node) - continue; + if (ctrl_dn != dev->of_node) + goto put_nodes; - err = fwnode_property_read_u32(port, "reg", &pn); + err = of_property_read_u32(port_dn, "reg", &pn); if (err) - return err; + goto put_nodes; - if (pn >= MAX_PORTS) - return dev_err_probe(dev, -EINVAL, "illegal port number %d\n", pn); + if (pn >= priv->info->num_ports) { + err = dev_err_probe(dev, -EINVAL, "illegal port number %d\n", pn); + goto put_nodes; + } - if (test_bit(pn, priv->valid_ports)) - return dev_err_probe(dev, -EINVAL, "duplicated port number %d\n", pn); + if (test_bit(pn, priv->valid_ports)) { + err = dev_err_probe(dev, -EINVAL, "duplicated port number %d\n", pn); + goto put_nodes; + } - err = of_property_read_u32(mdio_dn, "reg", &bus); + err = of_property_read_u32(bus_dn, "reg", &bus); if (err) - return err; + goto put_nodes; - if (bus >= MAX_SMI_BUSSES) - return dev_err_probe(dev, -EINVAL, "illegal smi bus number %d\n", bus); + if (bus >= priv->info->num_buses) { + err = dev_err_probe(dev, -EINVAL, "illegal smi bus number %d\n", bus); + goto put_nodes; + } - err = of_property_read_u32(phy_dn, "reg", &addr); - if (err) - return err; + addr = of_mdio_parse_addr(dev, phy_dn); + if (addr < 0) { + err = addr; + goto put_nodes; + } + + /* + * The MDIO accesses from the kernel work with the PHY polling unit in the + * switch. The PPU either operates in GPHY (i.e. clause 22) or 10GPHY mode + * (i.e. clause 45). Select 10GPHY mode if there is at least one PHY that + * declares compatible = "ethernet-phy-ieee802.3-c45". + */ + if (of_device_is_compatible(phy_dn, "ethernet-phy-ieee802.3-c45")) + priv->smi_bus_is_c45[bus] = true; __set_bit(pn, priv->valid_ports); priv->smi_bus[pn] = bus; priv->smi_addr[pn] = addr; +put_nodes: + of_node_put(bus_dn); + of_node_put(phy_dn); + of_node_put(ctrl_dn); + if (err) + break; } - return 0; + of_node_put(ports_dn); + + return err; } -static int rtl9300_mdiobus_probe(struct platform_device *pdev) +static int otto_emdio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct rtl9300_mdio_priv *priv; + struct otto_emdio_priv *priv; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -476,44 +667,99 @@ static int rtl9300_mdiobus_probe(struct platform_device *pdev) if (err) return err; + priv->info = device_get_match_data(dev); priv->regmap = syscon_node_to_regmap(dev->parent->of_node); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); platform_set_drvdata(pdev, priv); - err = rtl9300_mdiobus_map_ports(dev); + err = otto_emdio_map_ports(dev); if (err) return err; + err = otto_emdio_setup_topology(priv); + if (err) + return err; + + if (priv->info->setup_controller) { + err = priv->info->setup_controller(priv); + if (err) + return dev_err_probe(dev, err, "failed to setup MDIO bus controller\n"); + } + device_for_each_child_node_scoped(dev, child) { - err = rtl9300_mdiobus_probe_one(dev, priv, child); + err = otto_emdio_probe_one(dev, priv, child); if (err) return err; } - err = rtl9300_mdiobus_init(priv); - if (err) - return dev_err_probe(dev, err, "failed to initialise MDIO bus controller\n"); - return 0; } -static const struct of_device_id rtl9300_mdio_ids[] = { - { .compatible = "realtek,rtl9301-mdio" }, +static const struct otto_emdio_info otto_emdio_9300_info = { + .addr_map_base = RTL9300_SMI_PORT0_5_ADDR_CTRL, + .bus_map_base = RTL9300_SMI_PORT0_15_POLLING_SEL, + .cmd_fail = RTL9300_PHY_CTRL_FAIL, + .cmd_read = RTL9300_PHY_CTRL_READ, + .cmd_write = RTL9300_PHY_CTRL_WRITE, + .cmd_regs = { + .c22_data = RTL9300_SMI_ACCESS_PHY_CTRL_1, + .c45_data = RTL9300_SMI_ACCESS_PHY_CTRL_3, + .io_data = RTL9300_SMI_ACCESS_PHY_CTRL_2, + .port_mask_low = RTL9300_SMI_ACCESS_PHY_CTRL_0, + }, + .num_buses = RTL9300_NUM_BUSES, + .num_ports = RTL9300_NUM_PORTS, + .num_pages = RTL9300_NUM_PAGES, + .setup_controller = otto_emdio_9300_setup_controller, + .read_c22 = otto_emdio_9300_read_c22, + .read_c45 = otto_emdio_9300_read_c45, + .write_c22 = otto_emdio_9300_write_c22, + .write_c45 = otto_emdio_9300_write_c45, +}; + +static const struct otto_emdio_info otto_emdio_9310_info = { + .addr_map_base = RTL9310_SMI_PORT_ADDR_CTRL, + .bus_map_base = RTL9310_SMI_PORT_POLLING_SEL, + .cmd_fail = RTL9310_PHY_CTRL_FAIL, + .cmd_read = RTL9310_PHY_CTRL_READ, + .cmd_write = RTL9310_PHY_CTRL_WRITE, + .cmd_regs = { + .broadcast = RTL9310_SMI_INDRT_ACCESS_BC_PHYID_CTRL, + .c22_data = RTL9310_SMI_INDRT_ACCESS_CTRL_0, + .c45_data = RTL9310_SMI_INDRT_ACCESS_MMD_CTRL, + .ext_page = RTL9310_SMI_INDRT_ACCESS_CTRL_1, + .io_data = RTL9310_SMI_INDRT_ACCESS_CTRL_3, + .port_mask_low = RTL9310_SMI_INDRT_ACCESS_CTRL_2_LOW, + .port_mask_high = RTL9310_SMI_INDRT_ACCESS_CTRL_2_HIGH, + }, + .num_buses = RTL9310_NUM_BUSES, + .num_pages = RTL9310_NUM_PAGES, + .num_ports = RTL9310_NUM_PORTS, + .setup_controller = otto_emdio_9310_setup_controller, + .read_c22 = otto_emdio_9310_read_c22, + .read_c45 = otto_emdio_9310_read_c45, + .write_c22 = otto_emdio_9310_write_c22, + .write_c45 = otto_emdio_9310_write_c45, +}; + +static const struct of_device_id otto_emdio_ids[] = { + { .compatible = "realtek,rtl9301-mdio", .data = &otto_emdio_9300_info }, + { .compatible = "realtek,rtl9311-mdio", .data = &otto_emdio_9310_info }, {} }; -MODULE_DEVICE_TABLE(of, rtl9300_mdio_ids); +MODULE_DEVICE_TABLE(of, otto_emdio_ids); -static struct platform_driver rtl9300_mdio_driver = { - .probe = rtl9300_mdiobus_probe, +static struct platform_driver otto_emdio_driver = { + .probe = otto_emdio_probe, .driver = { .name = "mdio-rtl9300", - .of_match_table = rtl9300_mdio_ids, + .of_match_table = otto_emdio_ids, }, }; -module_platform_driver(rtl9300_mdio_driver); +module_platform_driver(otto_emdio_driver); MODULE_DESCRIPTION("RTL9300 MDIO driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 57dd6821a8aa..a159cb293981 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -32,8 +32,13 @@ #include #include #include +#include +#include +#include #include #include +#include +#include #include #include #include @@ -45,6 +50,7 @@ MODULE_AUTHOR("Matt Mackall "); MODULE_DESCRIPTION("Console driver for network interfaces"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("NETDEV_INTERNAL"); #define MAX_PARAM_LENGTH 256 #define MAX_EXTRADATA_ENTRY_LEN 256 @@ -277,6 +283,13 @@ static bool bound_by_mac(struct netconsole_target *nt) return is_valid_ether_addr(nt->np.dev_mac); } +static void netcons_release_dev(struct netconsole_target *nt) +{ + do_netpoll_cleanup(&nt->np); + if (bound_by_mac(nt)) + memset(&nt->np.dev_name, 0, IFNAMSIZ); +} + /* Attempts to resume logging to a deactivated target. */ static void resume_target(struct netconsole_target *nt) { @@ -329,6 +342,24 @@ static void process_resume_target(struct work_struct *work) resume_target(nt); + /* netpoll_setup() took a net_device reference and dropped the RTNL + * before returning, all while this target was off target_list and + * thus invisible to netconsole_netdev_event(). If the device was + * unregistered in that window the NETDEV_UNREGISTER notifier could not + * tear this target down, which would leak the reference and hang + * unregister_netdevice(). Re-check under the RTNL before re-publishing: + * taking it across the check and the list_add() serialises against the + * notifier (which also runs under the RTNL), so the device is either + * still registered (the notifier will find the re-added target) or + * already unregistering (we drop the reference here). + */ + rtnl_lock(); + if (nt->state == STATE_ENABLED && nt->np.dev && + nt->np.dev->reg_state != NETREG_REGISTERED) { + netcons_release_dev(nt); + nt->state = STATE_DISABLED; + } + /* At this point the target is either enabled or disabled and * was cleaned up before getting deactivated. Either way, add it * back to target list. @@ -336,6 +367,7 @@ static void process_resume_target(struct work_struct *work) spin_lock_irqsave(&target_list_lock, flags); list_add(&nt->list, &target_list); spin_unlock_irqrestore(&target_list_lock, flags); + rtnl_unlock(); out_unlock: dynamic_netconsole_mutex_unlock(); @@ -383,9 +415,7 @@ static void netconsole_process_cleanups_core(void) list_for_each_entry_safe(nt, tmp, &target_cleanup_list, list) { /* all entries in the cleanup_list needs to be disabled */ WARN_ON_ONCE(nt->state == STATE_ENABLED); - do_netpoll_cleanup(&nt->np); - if (bound_by_mac(nt)) - memset(&nt->np.dev_name, 0, IFNAMSIZ); + netcons_release_dev(nt); /* moved the cleaned target to target_list. Need to hold both * locks */ @@ -1258,7 +1288,7 @@ static void userdatum_release(struct config_item *item) kfree(to_userdatum(item)); } -static struct configfs_item_operations userdatum_ops = { +static const struct configfs_item_operations userdatum_ops = { .release = userdatum_release, }; @@ -1313,7 +1343,7 @@ static struct configfs_attribute *userdata_attrs[] = { NULL, }; -static struct configfs_group_operations userdata_ops = { +static const struct configfs_group_operations userdata_ops = { .make_item = userdatum_make_item, .drop_item = userdatum_drop, }; @@ -1364,7 +1394,7 @@ static void netconsole_target_release(struct config_item *item) kfree(nt); } -static struct configfs_item_operations netconsole_target_item_ops = { +static const struct configfs_item_operations netconsole_target_item_ops = { .release = netconsole_target_release, }; @@ -1443,10 +1473,21 @@ static void drop_netconsole_target(struct config_group *group, { struct netconsole_target *nt = to_target(item); unsigned long flags; + bool needs_cleanup; dynamic_netconsole_mutex_lock(); + mutex_lock(&target_cleanup_list_lock); spin_lock_irqsave(&target_list_lock, flags); + /* A STATE_DEACTIVATED target may have been moved to + * target_cleanup_list by netconsole_netdev_event() but not yet + * processed by netconsole_process_cleanups_core(). Unlinking it below + * hides it from the cleanup worker, so this path has to clean it up + * itself. Record that the target still owns a netpoll before the + * state is downgraded. + */ + needs_cleanup = nt->state == STATE_ENABLED || + nt->state == STATE_DEACTIVATED; /* Disable deactivated target to prevent races between resume attempt * and target removal. */ @@ -1454,6 +1495,7 @@ static void drop_netconsole_target(struct config_group *group, nt->state = STATE_DISABLED; list_del(&nt->list); spin_unlock_irqrestore(&target_list_lock, flags); + mutex_unlock(&target_cleanup_list_lock); dynamic_netconsole_mutex_unlock(); @@ -1467,14 +1509,16 @@ static void drop_netconsole_target(struct config_group *group, /* * The target may have never been enabled, or was manually disabled * before being removed so netpoll may have already been cleaned up. + * netpoll_cleanup() is idempotent (it skips when np->dev is NULL), so + * it is safe even if the cleanup worker already tore the netpoll down. */ - if (nt->state == STATE_ENABLED) + if (needs_cleanup) netpoll_cleanup(&nt->np); config_item_put(&nt->group.cg_item); } -static struct configfs_group_operations netconsole_subsys_group_ops = { +static const struct configfs_group_operations netconsole_subsys_group_ops = { .make_group = make_netconsole_target, .drop_item = drop_netconsole_target, }; @@ -1648,6 +1692,208 @@ static struct notifier_block netconsole_netdev_notifier = { .notifier_call = netconsole_netdev_event, }; +/* Pop a pre-allocated skb from the pool and request a refill. + * + * The pool is refilled with MAX_SKB_SIZE buffers, so a pooled skb cannot + * satisfy a larger request. Return NULL in that case rather than handing + * back a too-small skb that would later trip skb_over_panic() in skb_put(); + * the caller still polls and retries, and alloc_skb() itself can satisfy the + * oversized request once memory frees up. + * + * The refill is requested via schedule_work(), which takes the workqueue + * pool locks and is therefore not NMI-safe. Skip the refill when called + * from NMI context; the next non-NMI caller will top the pool back up. + */ +static struct sk_buff *netcons_skb_pop(struct netpoll *np, int len) +{ + struct sk_buff *skb; + + if (len > MAX_SKB_SIZE) { + /* net_warn_ratelimited() pulls in printk machinery that is not + * NMI-safe and could recurse into the nbcon console we are + * servicing, so only warn outside NMI. + */ + if (!in_nmi()) + net_warn_ratelimited("netconsole: dropping message, requested skb len %d exceeds pool buffer size %zu on %s\n", + len, (size_t)MAX_SKB_SIZE, + np->dev->name); + return NULL; + } + + skb = skb_dequeue(&np->skb_pool); + if (!in_nmi()) + schedule_work(&np->refill_wq); + + return skb; +} + +static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve) +{ + int count = 0; + struct sk_buff *skb; + + netpoll_zap_completion_queue(); +repeat: + + skb = alloc_skb(len, GFP_ATOMIC); + if (!skb) + skb = netcons_skb_pop(np, len); + + if (!skb) { + if (++count < 10) { + netpoll_poll_dev(np->dev); + goto repeat; + } + return NULL; + } + + refcount_set(&skb->users, 1); + skb_reserve(skb, reserve); + return skb; +} + +static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb, + int len) +{ + struct udphdr *udph; + int udp_len; + + udp_len = len + sizeof(struct udphdr); + udph = udp_hdr(skb); + + /* check needs to be set, since it will be consumed in csum_partial */ + udph->check = 0; + if (np->ipv6) + udph->check = csum_ipv6_magic(&np->local_ip.in6, + &np->remote_ip.in6, + udp_len, IPPROTO_UDP, + csum_partial(udph, udp_len, 0)); + else + udph->check = csum_tcpudp_magic(np->local_ip.ip, + np->remote_ip.ip, + udp_len, IPPROTO_UDP, + csum_partial(udph, udp_len, 0)); + if (udph->check == 0) + udph->check = CSUM_MANGLED_0; +} + +static void push_udp(struct netpoll *np, struct sk_buff *skb, int len) +{ + struct udphdr *udph; + int udp_len; + + udp_len = len + sizeof(struct udphdr); + + skb_push(skb, sizeof(struct udphdr)); + skb_reset_transport_header(skb); + + udph = udp_hdr(skb); + udph->source = htons(np->local_port); + udph->dest = htons(np->remote_port); + udph->len = htons(udp_len); + + netpoll_udp_checksum(np, skb, len); +} + +static void push_eth(struct netpoll *np, struct sk_buff *skb) +{ + struct ethhdr *eth; + + eth = skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + ether_addr_copy(eth->h_source, np->dev->dev_addr); + ether_addr_copy(eth->h_dest, np->remote_mac); + if (np->ipv6) + eth->h_proto = htons(ETH_P_IPV6); + else + eth->h_proto = htons(ETH_P_IP); +} + +static void push_ipv4(struct netpoll *np, struct sk_buff *skb, int len) +{ + static atomic_t ip_ident; + struct iphdr *iph; + int ip_len; + + ip_len = len + sizeof(struct udphdr) + sizeof(struct iphdr); + + skb_push(skb, sizeof(struct iphdr)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); + + /* iph->version = 4; iph->ihl = 5; */ + *(unsigned char *)iph = 0x45; + iph->tos = 0; + put_unaligned(htons(ip_len), &iph->tot_len); + iph->id = htons(atomic_inc_return(&ip_ident)); + iph->frag_off = 0; + iph->ttl = 64; + iph->protocol = IPPROTO_UDP; + iph->check = 0; + put_unaligned(np->local_ip.ip, &iph->saddr); + put_unaligned(np->remote_ip.ip, &iph->daddr); + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + skb->protocol = htons(ETH_P_IP); +} + +static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len) +{ + struct ipv6hdr *ip6h; + + skb_push(skb, sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + ip6h = ipv6_hdr(skb); + + /* ip6h->version = 6; ip6h->priority = 0; */ + *(unsigned char *)ip6h = 0x60; + ip6h->flow_lbl[0] = 0; + ip6h->flow_lbl[1] = 0; + ip6h->flow_lbl[2] = 0; + + ip6h->payload_len = htons(sizeof(struct udphdr) + len); + ip6h->nexthdr = IPPROTO_UDP; + ip6h->hop_limit = 32; + ip6h->saddr = np->local_ip.in6; + ip6h->daddr = np->remote_ip.in6; + + skb->protocol = htons(ETH_P_IPV6); +} + +static int netpoll_send_udp(struct netpoll *np, const char *msg, int len) +{ + int total_len, ip_len, udp_len; + struct sk_buff *skb; + + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + WARN_ON_ONCE(!irqs_disabled()); + + udp_len = len + sizeof(struct udphdr); + if (np->ipv6) + ip_len = udp_len + sizeof(struct ipv6hdr); + else + ip_len = udp_len + sizeof(struct iphdr); + + total_len = ip_len + LL_RESERVED_SPACE(np->dev); + + skb = find_skb(np, total_len + np->dev->needed_tailroom, + total_len - len); + if (!skb) + return -ENOMEM; + + skb_copy_to_linear_data(skb, msg, len); + skb_put(skb, len); + + push_udp(np, skb, len); + if (np->ipv6) + push_ipv6(np, skb, len); + else + push_ipv4(np, skb, len); + push_eth(np, skb); + skb->dev = np->dev; + + return (int)netpoll_send_skb(np, skb); +} + /** * send_udp - Wrapper for netpoll_send_udp that counts errors * @nt: target to send message to diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index 8eebcc933ddb..16aa88278398 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c @@ -123,12 +123,6 @@ int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, struct netdevsim *ns = cb_priv; struct bpf_prog *oldprog; - if (type != TC_SETUP_CLSBPF) { - NSIM_EA(cls_bpf->common.extack, - "only offload of BPF classifiers supported"); - return -EOPNOTSUPP; - } - if (!tc_cls_can_offload_and_chain0(ns->netdev, &cls_bpf->common)) return -EOPNOTSUPP; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index f00fc2f9ebde..aed9ad5f1b43 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -597,11 +597,11 @@ static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev, value.vu32 = nsim_dev->max_macs; devl_param_driverinit_value_set(devlink, DEVLINK_PARAM_GENERIC_ID_MAX_MACS, - value); + &value); value.vbool = nsim_dev->test1; devl_param_driverinit_value_set(devlink, NSIM_DEVLINK_PARAM_ID_TEST1, - value); + &value); } static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink) diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c index 36a201533aae..9350ba48eb81 100644 --- a/drivers/net/netdevsim/ethtool.c +++ b/drivers/net/netdevsim/ethtool.c @@ -209,6 +209,7 @@ static const struct ethtool_ops nsim_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_ALL_PARAMS, .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | ETHTOOL_RING_USE_HDS_THRS, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS, .get_pause_stats = nsim_get_pause_stats, .get_pauseparam = nsim_get_pauseparam, .set_pauseparam = nsim_set_pauseparam, diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 1a42bdbfaa41..55bcdefadc9b 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -1562,14 +1562,11 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, data->devlink = devlink; nsim_dev = devlink_priv(devlink); - err = nsim_fib_debugfs_init(data, nsim_dev); - if (err) - goto err_data_free; mutex_init(&data->nh_lock); err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params); if (err) - goto err_debugfs_exit; + goto err_nh_lock_destroy; mutex_init(&data->fib_lock); INIT_LIST_HEAD(&data->fib_rt_list); @@ -1600,6 +1597,10 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, goto err_nexthop_nb_unregister; } + err = nsim_fib_debugfs_init(data, nsim_dev); + if (err) + goto err_fib_notifier_unregister; + devl_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV4_FIB, nsim_fib_ipv4_resource_occ_get, @@ -1622,6 +1623,8 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, data); return data; +err_fib_notifier_unregister: + unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); err_nexthop_nb_unregister: unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); err_rhashtable_fib_destroy: @@ -1633,10 +1636,8 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, data); mutex_destroy(&data->fib_lock); -err_debugfs_exit: +err_nh_lock_destroy: mutex_destroy(&data->nh_lock); - nsim_fib_debugfs_exit(data); -err_data_free: kfree(data); return ERR_PTR(err); } @@ -1653,6 +1654,7 @@ void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) NSIM_RESOURCE_IPV4_FIB_RULES); devl_resource_occ_get_unregister(devlink, NSIM_RESOURCE_IPV4_FIB); + nsim_fib_debugfs_exit(data); unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); cancel_work_sync(&data->fib_flush_work); @@ -1665,6 +1667,5 @@ void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) WARN_ON_ONCE(!list_empty(&data->fib_rt_list)); mutex_destroy(&data->fib_lock); mutex_destroy(&data->nh_lock); - nsim_fib_debugfs_exit(data); kfree(data); } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index a750768912b5..27e5f109f933 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -185,10 +185,11 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static void nsim_set_rx_mode(struct net_device *dev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int nsim_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { + return 0; } static int nsim_change_mtu(struct net_device *dev, int new_mtu) diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index d909c4160ea1..4c9cc96dcec3 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -115,11 +114,10 @@ struct netdevsim { int rq_reset_mode; struct { - u64_stats_t rx_packets; - u64_stats_t rx_bytes; - u64_stats_t tx_packets; - u64_stats_t tx_bytes; - struct u64_stats_sync syncp; + atomic64_t rx_packets; + atomic64_t rx_bytes; + atomic64_t tx_packets; + atomic64_t tx_bytes; struct psp_dev __rcu *dev; struct dentry *rereg; struct mutex rereg_lock; diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c index 6936ecb8173e..59c990fdc79e 100644 --- a/drivers/net/netdevsim/psp.c +++ b/drivers/net/netdevsim/psp.c @@ -22,6 +22,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, struct psp_dev *peer_psd; struct psp_assoc *pas; struct net *net; + int psp_len; void **ptr; rcu_read_lock(); @@ -48,6 +49,10 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, goto out_unlock; } + psp_len = skb->len - skb_inner_transport_offset(skb); + atomic64_inc(&ns->psp.tx_packets); + atomic64_add(psp_len, &ns->psp.tx_bytes); + /* Now pretend we just received this frame */ peer_psd = rcu_dereference(peer_ns->psp.dev); if (peer_psd && peer_psd->config.versions & (1 << pas->version)) { @@ -72,14 +77,8 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, refcount_inc(&(*psp_ext)->refcnt); skb->decrypted = 1; - u64_stats_update_begin(&ns->psp.syncp); - u64_stats_inc(&ns->psp.tx_packets); - u64_stats_inc(&ns->psp.rx_packets); - u64_stats_add(&ns->psp.tx_bytes, - skb->len - skb_inner_transport_offset(skb)); - u64_stats_add(&ns->psp.rx_bytes, - skb->len - skb_inner_transport_offset(skb)); - u64_stats_update_end(&ns->psp.syncp); + atomic64_inc(&peer_ns->psp.rx_packets); + atomic64_add(psp_len, &peer_ns->psp.rx_bytes); } else { struct ipv6hdr *ip6h __maybe_unused; struct iphdr *iph; @@ -132,14 +131,15 @@ nsim_rx_spi_alloc(struct psp_dev *psd, u32 version, struct netlink_ext_ack *extack) { struct netdevsim *ns = psd->drv_priv; - unsigned int new; int i; - new = ++ns->psp.spi & PSP_SPI_KEY_ID; - if (psd->generation & 1) - new |= PSP_SPI_KEY_PHASE; + /* Check if incrementing the spi would change the phase bit */ + if ((ns->psp.spi & PSP_SPI_KEY_ID) == PSP_SPI_KEY_ID) { + NL_SET_ERR_MSG(extack, "SPI space exhausted"); + return -ENOSPC; + } - assoc->spi = cpu_to_be32(new); + assoc->spi = cpu_to_be32(++ns->psp.spi); assoc->key[0] = psd->generation; for (i = 1; i < PSP_MAX_KEY; i++) assoc->key[i] = ns->psp.spi + i; @@ -162,6 +162,16 @@ static int nsim_assoc_add(struct psp_dev *psd, struct psp_assoc *pas, static int nsim_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *extack) { + struct netdevsim *ns = psd->drv_priv; + + /* Flip key phase and reset SPI to 0 within that space + * (will be pre-incremented, as 0 is an invalid SPI). + */ + if (ns->psp.spi & PSP_SPI_KEY_PHASE) + ns->psp.spi = 0; + else + ns->psp.spi = PSP_SPI_KEY_PHASE; + return 0; } @@ -177,20 +187,16 @@ static void nsim_assoc_del(struct psp_dev *psd, struct psp_assoc *pas) static void nsim_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats) { struct netdevsim *ns = psd->drv_priv; - unsigned int start; /* WARNING: do *not* blindly zero stats in real drivers! * All required stats must be reported by the device! */ memset(stats, 0, sizeof(struct psp_dev_stats)); - do { - start = u64_stats_fetch_begin(&ns->psp.syncp); - stats->rx_bytes = u64_stats_read(&ns->psp.rx_bytes); - stats->rx_packets = u64_stats_read(&ns->psp.rx_packets); - stats->tx_bytes = u64_stats_read(&ns->psp.tx_bytes); - stats->tx_packets = u64_stats_read(&ns->psp.tx_packets); - } while (u64_stats_fetch_retry(&ns->psp.syncp, start)); + stats->rx_bytes = atomic64_read(&ns->psp.rx_bytes); + stats->rx_packets = atomic64_read(&ns->psp.rx_packets); + stats->tx_bytes = atomic64_read(&ns->psp.tx_bytes); + stats->tx_packets = atomic64_read(&ns->psp.tx_packets); } static struct psp_dev_ops nsim_psp_ops = { diff --git a/drivers/net/netdevsim/tc.c b/drivers/net/netdevsim/tc.c index 8f013a5895a2..a415e02a6df1 100644 --- a/drivers/net/netdevsim/tc.c +++ b/drivers/net/netdevsim/tc.c @@ -9,7 +9,22 @@ static int nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { - return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv); + struct flow_cls_common_offload *common = type_data; + int err = 0; + + switch (type) { + case TC_SETUP_CLSBPF: + err = nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv); + break; + case TC_SETUP_CLSFLOWER: + break; + default: + NSIM_EA(common->extack, "offload type not supported"); + err = -EOPNOTSUPP; + break; + } + + return err; } static void nsim_taprio_stats(struct tc_taprio_qopt_stats *stats) @@ -73,7 +88,10 @@ nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) &nsim_block_cb_list, nsim_setup_tc_block_cb, ns, ns, true); + case TC_SETUP_FT: + return 0; default: return -EOPNOTSUPP; } } +ALLOW_ERROR_INJECTION(nsim_setup_tc, ERRNO); diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c index 5e2eecc3165d..a3931cd82132 100644 --- a/drivers/net/netkit.c +++ b/drivers/net/netkit.c @@ -186,11 +186,12 @@ static int netkit_get_iflink(const struct net_device *dev) return iflink; } -static void netkit_set_multicast(struct net_device *dev, - struct netdev_hw_addr_list *uc, - struct netdev_hw_addr_list *mc) +static int netkit_set_multicast(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { /* Nothing to do, we receive whatever gets pushed to us! */ + return 0; } static int netkit_set_macaddr(struct net_device *dev, void *sa) @@ -466,6 +467,7 @@ static void netkit_setup(struct net_device *dev) dev->priv_flags |= IFF_NO_QUEUE; dev->priv_flags |= IFF_DISABLE_NETPOLL; dev->lltx = true; + dev->netmem_tx = NETMEM_TX_NO_DMA; dev->netdev_ops = &netkit_netdev_ops; dev->ethtool_ops = &netkit_ethtool_ops; diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index 8811aa9eedeb..7f69e8890b5b 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -399,7 +399,7 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock, if (!old_data) { /* socket is currently unused - we can take it */ rcu_read_unlock(); - setup_udp_tunnel_sock(sock_net(ovpn_sock->sk), sock, &cfg); + setup_udp_tunnel_sock(sock_net(ovpn_sock->sk), sock->sk, &cfg); return 0; } diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c index 28e6bc4a1f14..d8e4d60f5834 100644 --- a/drivers/net/pfcp.c +++ b/drivers/net/pfcp.c @@ -18,7 +18,7 @@ struct pfcp_dev { struct list_head list; - struct socket *sock; + struct sock *sk; struct net_device *dev; struct net *net; @@ -104,8 +104,8 @@ static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb) static void pfcp_del_sock(struct pfcp_dev *pfcp) { - udp_tunnel_sock_release(pfcp->sock); - pfcp->sock = NULL; + udp_tunnel_sock_release(pfcp->sk); + pfcp->sk = NULL; } static void pfcp_dev_uninit(struct net_device *dev) @@ -148,10 +148,11 @@ static void pfcp_link_setup(struct net_device *dev) dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->priv_flags |= IFF_NO_QUEUE; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; netif_keep_dst(dev); } -static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp) +static struct sock *pfcp_create_sock(struct pfcp_dev *pfcp) { struct udp_tunnel_sock_cfg tuncfg = {}; struct udp_port_cfg udp_conf = { @@ -172,16 +173,16 @@ static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp) tuncfg.encap_rcv = pfcp_encap_recv; tuncfg.encap_type = 1; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); - return sock; + return sock->sk; } static int pfcp_add_sock(struct pfcp_dev *pfcp) { - pfcp->sock = pfcp_create_sock(pfcp); + pfcp->sk = pfcp_create_sock(pfcp); - return PTR_ERR_OR_ZERO(pfcp->sock); + return PTR_ERR_OR_ZERO(pfcp->sk); } static int pfcp_newlink(struct net_device *dev, @@ -216,6 +217,7 @@ static int pfcp_newlink(struct net_device *dev, exit_del_pfcp_sock: pfcp_del_sock(pfcp); + synchronize_rcu(); exit_err: pfcp->net = NULL; return err; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index b5ee338b620d..099f25dceabb 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -90,12 +90,24 @@ config AS21XXX_PHY AS21210PB1 that all register with the PHY ID 0x7500 0x7500 before the firmware is loaded. +config AIR_AN8801_PHY + tristate "Airoha AN8801 Gigabit PHY" + select AIR_NET_PHYLIB + help + Currently supports the Airoha AN8801R PHY. + config AIR_EN8811H_PHY tristate "Airoha EN8811H 2.5 Gigabit PHY" + select AIR_NET_PHYLIB select PHY_COMMON_PROPS help Currently supports the Airoha EN8811H PHY. +config AIR_NET_PHYLIB + tristate + help + Airoha Ethernet PHY common library + config AMD_PHY tristate "AMD and Altima PHYs" help @@ -238,7 +250,7 @@ config LXT_PHY config INTEL_XWAY_PHY tristate "Intel XWAY PHYs" help - Supports the Intel XWAY (former Lantiq) 11G and 22E PHYs. + Supports the Intel XWAY (former Lantiq) 11G and 22F PHYs. These PHYs are marked as standalone chips under the names PEF 7061, PEF 7071 and PEF 7072 or integrated into the Intel SoCs xRX200, xRX300, xRX330, xRX350 and xRX550. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 05e4878af27a..de660ae94945 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -29,7 +29,9 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) obj-$(CONFIG_ADIN_PHY) += adin.o obj-$(CONFIG_ADIN1100_PHY) += adin1100.o +obj-$(CONFIG_AIR_AN8801_PHY) += air_an8801.o obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o +obj-$(CONFIG_AIR_NET_PHYLIB) += air_phy_lib.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ diff --git a/drivers/net/phy/air_an8801.c b/drivers/net/phy/air_an8801.c new file mode 100644 index 000000000000..f97c78e43023 --- /dev/null +++ b/drivers/net/phy/air_an8801.c @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the Airoha AN8801 Gigabit PHY. + * + * Copyright (C) 2025 Airoha Technology Corp. + * Copyright (C) 2025 Collabora Ltd. + * AngeloGioacchino Del Regno + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "air_phy_lib.h" + +#define AN8801R_PHY_ID 0xc0ff0421 + +/* MII Registers */ + +/* MII Registers - Airoha Page 1 */ +#define AN8801_EXT_REG_PHY 0x14 +#define AN8801_EXT_PHY_STATUS0 GENMASK(1, 0) +#define AN8801_EXT_PHY_DOWNSHIFT_CTL GENMASK(3, 2) /* 2 to 5 1G auto-neg attempts (0..3) */ +#define AN8801_EXT_PHY_DOWNSHIFT_EN BIT(4) +#define AN8801_EXT_PHY_CTRL0 BIT(5) +#define AN8801_EXT_PHY_STATUS1 GENMASK(8, 6) +#define AN8801_EXT_PHY_CTRL1 GENMASK(14, 9) + +/* MII Registers - Airoha Page 4 */ +#define AN8801_PBUS_ACCESS BIT(28) +#define AN8801_PBUS_EPHY_ACCESS BIT(24) +#define AN8801_PBUS_CL22_ACCESS BIT(23) + +/* BPBUS Registers */ +#define AN8801_BPBUS_REG_LED_GPIO 0x54 +#define AN8801_BPBUS_REG_LED_ID_SEL 0x58 +#define LED_ID_GPIO_SEL(led, gpio) ((led) << ((gpio) * 3)) +#define LED_ID_GPIO_SEL_MASK(gpio) (0x7 << ((gpio) * 3)) +#define AN8801_BPBUS_REG_GPIO_MODE 0x70 +#define AN8801_BPBUS_REG_PHY_IRQ_GPIO 0x7c +#define AN8801_PHY_IRQ_GPIO_NUM_MASK GENMASK(19, 16) +#define AN8801_PHY_IRQ_GPIO_NUM 1 + +#define AN8801_BPBUS_REG_CKO 0x1a4 +#define AN8801_CKO_OUTPUT_MODE_AUTO 3 + +#define AN8801_BPBUS_REG_LINK_MODE 0x5054 +#define AN8801_BPBUS_LINK_MODE_1000 BIT(0) + +#define AN8801_BPBUS_REG_BYPASS_PTP 0x21c004 +#define AN8801_BYP_PTP_SGMII_TO_GPHY BIT(8) +#define AN8801_BYP_PTP_RGMII_TO_GPHY BIT(0) + +#define AN8801_BPBUS_REG_TXDLY_STEP 0x21c024 +#define RGMII_DELAY_STEP_MASK GENMASK(2, 0) +#define RGMII_DELAY_NO_STEP 0 +#define RGMII_DELAY_STEP_1 1 +#define RGMII_DELAY_STEP_2 2 +#define RGMII_DELAY_STEP_3 3 +#define RGMII_DELAY_STEP_4 4 +#define RGMII_DELAY_STEP_5 5 +#define RGMII_DELAY_STEP_6 6 +#define RGMII_DELAY_STEP_7 7 +#define RGMII_TXDELAY_FORCE_MODE BIT(24) + +/* Default RGMII TX delay setting, corresponding to a 1.883ns delay */ +#define AN8801_RGMII_TXDELAY_DEFAULT RGMII_DELAY_STEP_4 + +#define AN8801_BPBUS_REG_RXDLY_STEP 0x21c02c +#define RGMII_RXDELAY_ALIGN BIT(4) +#define RGMII_RXDELAY_FORCE_MODE BIT(24) + +/* Default RGMII RX delay setting, corresponding to a 1.992ns delay, + * when align bit is set or -0.008ns otherwise. + */ +#define AN8801_RGMII_RXDELAY_DEFAULT RGMII_DELAY_NO_STEP + +#define AN8801_BPBUS_REG_EFIFO_CTL(x) (0x270004 + (0x100 * (x))) /* 0..2 */ +#define AN8801_EFIFO_ALL_EN GENMASK(7, 0) +#define AN8801_EFIFO_RX_EN BIT(0) +#define AN8801_EFIFO_TX_EN BIT(1) +#define AN8801_EFIFO_RX_CLK_EN BIT(2) +#define AN8801_EFIFO_TX_CLK_EN BIT(3) +#define AN8801_EFIFO_RX_EEE_EN BIT(4) +#define AN8801_EFIFO_TX_EEE_EN BIT(5) +#define AN8801_EFIFO_RX_ODD_NIBBLE_EN BIT(6) +#define AN8801_EFIFO_TX_ODD_NIBBLE_EN BIT(7) + +#define AN8801_BPBUS_REG_WOL_MAC_16_47 0x285114 +#define AN8801_BPBUS_REG_WOL_MAC_0_15 0x285118 + +#define AN8801_BPBUS_REG_WAKEUP_CTL1 0x285400 +#define AN8801_WOL_WAKE_MAGIC_EN GENMASK(3, 1) +#define AN8801_WOL_WAKE_LNKCHG_EN BIT(4) + +#define AN8801_BPBUS_REG_WAKEUP_CTL2 0x285404 +#define AN8801_WAKE_OUT_TYPE_PULSE BIT(0) /* Set/Unset: Pulse/Static */ +#define AN8801_WAKE_OUT_POLARITY_NEG BIT(1) /* Set/Unset: Negative/Positive */ +#define AN8801_WAKE_OUT_WIDTH GENMASK(3, 2) +#define AN8801_WAKE_OUT_84MS 0 +#define AN8801_WAKE_OUT_168MS 1 +#define AN8801_WAKE_OUT_336MS 2 +#define AN8801_WAKE_OUT_672MS 3 +#define AN8801_WAKE_OUT_EN BIT(4) +#define AN8801_PME_WAKEUP_CLR BIT(8) + +#define AN8801_BPBUS_REG_WAKE_IRQ_EN 0x285700 +#define AN8801_BPBUS_REG_WAKE_IRQ_STS 0x285704 +#define AN8801_IRQ_WAKE_LNKCHG BIT(0) /* Wake on link change */ +#define AN8801_IRQ_WAKE_UNIPKT BIT(1) /* Wake on unicast packet */ +#define AN8801_IRQ_WAKE_MULPKT BIT(2) /* Wake on multicast packet */ +#define AN8801_IRQ_WAKE_BCPKT BIT(3) /* Wake on broadcast packet */ +#define AN8801_IRQ_WAKE_MAGICPKT BIT(4) /* Wake on magic packet */ +#define AN8801_IRQ_WAKE_ALL GENMASK(4, 0) + +/* MDIO_MMD_VEND1 Registers */ +#define AN8801_PHY_TX_PAIR_DLY_SEL_GBE 0x13 +#define AN8801_PHY_PAIR_DLY_SEL_A_GBE GENMASK(14, 12) +#define AN8801_PHY_PAIR_DLY_SEL_B_GBE GENMASK(10, 8) +#define AN8801_PHY_PAIR_DLY_SEL_C_GBE GENMASK(6, 4) +#define AN8801_PHY_PAIR_DLY_SEL_D_GBE GENMASK(2, 0) +#define AN8801_PHY_RXADC_CTRL 0xd8 +#define AN8801_PHY_RXADC_SAMP_PHSEL_A BIT(12) +#define AN8801_PHY_RXADC_SAMP_PHSEL_B BIT(8) +#define AN8801_PHY_RXADC_SAMP_PHSEL_C BIT(4) +#define AN8801_PHY_RXADC_SAMP_PHSEL_D BIT(0) +#define AN8801_PHY_RXADC_REV_0 0xd9 +#define AN8801_PHY_RXADC_REV_MASK_A GENMASK(15, 8) +#define AN8801_PHY_RXADC_REV_MASK_B GENMASK(7, 0) +#define AN8801_PHY_RXADC_REV_1 0xda +#define AN8801_PHY_RXADC_REV_MASK_C GENMASK(15, 8) +#define AN8801_PHY_RXADC_REV_MASK_D GENMASK(7, 0) + +/* MDIO_MMD_VEND2 Registers */ +#define LED_BCR 0x21 +#define LED_BCR_MODE_MASK GENMASK(1, 0) +#define LED_BCR_TIME_TEST BIT(2) +#define LED_BCR_CLK_EN BIT(3) +#define LED_BCR_EVT_ALL BIT(4) +#define LED_BCR_EXT_CTRL BIT(15) +#define LED_BCR_MODE_DISABLE 0 +#define LED_BCR_MODE_2LED 1 +#define LED_BCR_MODE_3LED_1 2 +#define LED_BCR_MODE_3LED_2 3 + +#define LED_ON_DUR 0x22 +#define LED_ON_DUR_MASK GENMASK(15, 0) + +#define LED_BLINK_DUR 0x23 +#define LED_BLINK_DUR_MASK GENMASK(15, 0) + +#define LED_ON_CTRL(i) (0x24 + ((i) * 2)) +#define LED_ON_EVT_MASK GENMASK(6, 0) +#define LED_ON_EVT_LINK_1000M BIT(0) +#define LED_ON_EVT_LINK_100M BIT(1) +#define LED_ON_EVT_LINK_10M BIT(2) +#define LED_ON_EVT_LINK_DN BIT(3) +#define LED_ON_EVT_FDX BIT(4) +#define LED_ON_EVT_HDX BIT(5) +#define LED_ON_EVT_FORCE BIT(6) +#define LED_ON_POL BIT(14) +#define LED_ON_EN BIT(15) + +#define LED_BLINK_CTRL(i) (0x25 + ((i) * 2)) +#define LED_BLINK_EVT_MASK GENMASK(9, 0) +#define LED_BLINK_EVT_1000M_TX BIT(0) +#define LED_BLINK_EVT_1000M_RX BIT(1) +#define LED_BLINK_EVT_100M_TX BIT(2) +#define LED_BLINK_EVT_100M_RX BIT(3) +#define LED_BLINK_EVT_10M_TX BIT(4) +#define LED_BLINK_EVT_10M_RX BIT(5) +#define LED_BLINK_EVT_COLLISION BIT(6) +#define LED_BLINK_EVT_RX_CRC_ERR BIT(7) +#define LED_BLINK_EVT_RX_IDLE_ERR BIT(8) +#define LED_BLINK_EVT_FORCE BIT(9) + +#define AN8801R_NUM_LEDS 3 +#define AN8801_PERIOD_SHIFT 15 +#define AN8801_PERIOD_UNIT 32768 /* (1 << AN8801_PERIOD_SHIFT) */ +#define AN8801_MAX_PERIOD_MS 2147 + +#define AN8801_REG_PHY_INTERNAL0 0x600 +#define AN8801_REG_PHY_INTERNAL1 0x601 +#define AN8801_PHY_INTFUNC_MASK GENMASK(15, 0) /* PHY internal functions */ + +enum an8801r_led_fn { + AN8801R_LED_FN_NONE, + AN8801R_LED_FN_LINK, + AN8801R_LED_FN_ACTIVITY, + AN8801R_LED_FN_MAX, +}; + +struct an8801r_priv { + bool wake_magic_enabled; + bool wake_lnkchg_enabled; +}; + +static const unsigned long an8801r_led_trig = BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_1000) | + BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_RX_ERR) | + BIT(TRIGGER_NETDEV_TX); + +static int an8801_buckpbus_reg_rmw(struct phy_device *phydev, + u32 addr, u32 mask, u32 set) +{ + return air_phy_buckpbus_reg_modify(phydev, + addr | AN8801_PBUS_ACCESS, + mask, set); +} + +static int an8801_buckpbus_reg_set_bits(struct phy_device *phydev, + u32 addr, u32 mask) +{ + return air_phy_buckpbus_reg_modify(phydev, + addr | AN8801_PBUS_ACCESS, + mask, mask); +} + +static int an8801_buckpbus_reg_clear_bits(struct phy_device *phydev, + u32 addr, u32 mask) +{ + return air_phy_buckpbus_reg_modify(phydev, + addr | AN8801_PBUS_ACCESS, + mask, 0); +} + +static int an8801_buckpbus_reg_write(struct phy_device *phydev, u32 addr, + u32 data) +{ + return air_phy_buckpbus_reg_write(phydev, + addr | AN8801_PBUS_ACCESS, + data); +} + +static int an8801_buckpbus_reg_read(struct phy_device *phydev, u32 addr, + u32 *data) +{ + return air_phy_buckpbus_reg_read(phydev, + addr | AN8801_PBUS_ACCESS, + data); +} + +static u32 an8801r_led_blink_ms_to_hw(unsigned long req_ms) +{ + u32 req_ns, regval; + + if (req_ms > AN8801_MAX_PERIOD_MS) + req_ms = AN8801_MAX_PERIOD_MS; + + req_ns = req_ms * NSEC_PER_MSEC; + + /* Round to the nearest period unit... */ + regval = req_ns + (AN8801_PERIOD_UNIT / 2); + + /* ...and now divide by the full period */ + regval >>= AN8801_PERIOD_SHIFT; + + return regval; +} + +static int an8801r_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u32 hw_delay_on, hw_delay_off; + u16 blink_dur; + bool blink; + int ret; + + if (index >= AN8801R_NUM_LEDS) + return -EINVAL; + + if (delay_on && delay_off) { + blink = true; + + if (*delay_on == 0 || *delay_off == 0) { + *delay_on = 64; + *delay_off = 64; + } + + hw_delay_on = an8801r_led_blink_ms_to_hw(*delay_on); + hw_delay_off = an8801r_led_blink_ms_to_hw(*delay_off); + } else { + blink = false; + } + + if (blink) { + blink_dur = (u16)min(hw_delay_on + hw_delay_off, + LED_BLINK_DUR_MASK); + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_DUR, + blink_dur); + if (ret) + goto error; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_DUR, + hw_delay_on); + if (ret) + goto error; + } + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index), + LED_BLINK_EVT_MASK, + blink ? LED_BLINK_EVT_FORCE : 0); + if (ret) + return ret; + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EVT_MASK, + 0); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, blink ? LED_ON_EN : 0); + +error: + phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, 0); + return ret; +} + +static int an8801r_led_brightness_set(struct phy_device *phydev, u8 index, + enum led_brightness value) +{ + int ret; + + if (index >= AN8801R_NUM_LEDS) + return -EINVAL; + + /* Disable blink first if previously enabled */ + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index), + LED_BLINK_EVT_MASK, 0); + if (ret) + return ret; + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EVT_MASK, + (value == LED_OFF) ? 0 : LED_ON_EVT_FORCE); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, (value == LED_OFF) ? 0 : LED_ON_EN); +} + +static int an8801r_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + int on, blink; + + if (index >= AN8801R_NUM_LEDS) + return -EINVAL; + + on = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index)); + if (on < 0) + return on; + + blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index)); + if (blink < 0) + return blink; + + if (FIELD_GET(LED_ON_EVT_LINK_10M, on)) + __set_bit(TRIGGER_NETDEV_LINK_10, rules); + + if (FIELD_GET(LED_ON_EVT_LINK_100M, on)) + __set_bit(TRIGGER_NETDEV_LINK_100, rules); + + if (FIELD_GET(LED_ON_EVT_LINK_1000M, on)) + __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + + if (FIELD_GET(LED_ON_EVT_LINK_10M, on) && + FIELD_GET(LED_ON_EVT_LINK_100M, on) && + FIELD_GET(LED_ON_EVT_LINK_1000M, on)) + __set_bit(TRIGGER_NETDEV_LINK, rules); + + if (FIELD_GET(LED_BLINK_EVT_10M_RX, blink) || + FIELD_GET(LED_BLINK_EVT_100M_RX, blink) || + FIELD_GET(LED_BLINK_EVT_1000M_RX, blink)) + __set_bit(TRIGGER_NETDEV_RX, rules); + + if (FIELD_GET(LED_BLINK_EVT_10M_TX, blink) || + FIELD_GET(LED_BLINK_EVT_100M_TX, blink) || + FIELD_GET(LED_BLINK_EVT_1000M_TX, blink)) + __set_bit(TRIGGER_NETDEV_TX, rules); + + if (FIELD_GET(LED_BLINK_EVT_RX_CRC_ERR, blink)) + __set_bit(TRIGGER_NETDEV_RX_ERR, rules); + + return 0; +} + +static int an8801r_led_trig_to_hw(unsigned long rules, u16 *on, u16 *blink) +{ + /* All combinations of the supported triggers are allowed */ + if (rules & ~an8801r_led_trig) + return -EOPNOTSUPP; + + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) + *on |= LED_ON_EVT_LINK_10M; + + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + *on |= LED_ON_EVT_LINK_100M; + + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + *on |= LED_ON_EVT_LINK_1000M; + + if (test_bit(TRIGGER_NETDEV_LINK, &rules)) { + *on |= LED_ON_EVT_LINK_10M; + *on |= LED_ON_EVT_LINK_100M; + *on |= LED_ON_EVT_LINK_1000M; + } + + if (test_bit(TRIGGER_NETDEV_RX, &rules)) { + *blink |= LED_BLINK_EVT_10M_RX; + *blink |= LED_BLINK_EVT_100M_RX; + *blink |= LED_BLINK_EVT_1000M_RX; + } + + if (test_bit(TRIGGER_NETDEV_TX, &rules)) { + *blink |= LED_BLINK_EVT_10M_TX; + *blink |= LED_BLINK_EVT_100M_TX; + *blink |= LED_BLINK_EVT_1000M_TX; + } + + if (test_bit(TRIGGER_NETDEV_RX_ERR, &rules)) + *blink |= LED_BLINK_EVT_RX_CRC_ERR; + + return 0; +} + +static int an8801r_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 on = 0, blink = 0; + + if (index >= AN8801R_NUM_LEDS) + return -EINVAL; + + return an8801r_led_trig_to_hw(rules, &on, &blink); +} + +static int an8801r_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 on = 0, blink = 0; + int ret; + + if (index >= AN8801R_NUM_LEDS) + return -EINVAL; + + ret = an8801r_led_trig_to_hw(rules, &on, &blink); + if (ret) + return ret; + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EVT_MASK, on); + if (ret) + return ret; + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index), + LED_BLINK_EVT_MASK, blink); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, (on | blink) ? LED_ON_EN : 0); +} + +static int an8801r_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) +{ + bool active_high = true; + unsigned long mode; + + if (index >= AN8801R_NUM_LEDS) + return -EINVAL; + + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_HIGH: + break; + case PHY_LED_ACTIVE_LOW: + active_high = false; + break; + default: + return -EINVAL; + } + } + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_POL, active_high ? LED_ON_POL : 0); +} + +static int an8801r_led_init(struct phy_device *phydev, u8 *led_cfg) +{ + int led_id, ret; + + /* Set LED BCR Enable */ + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, LED_BCR, + LED_BCR_EXT_CTRL | LED_BCR_CLK_EN); + if (ret) + return ret; + + for (led_id = 0; led_id < AN8801R_NUM_LEDS; led_id++) { + unsigned long led_trigger = 0; + u32 led_gpio = led_id + 1; + + switch (led_cfg[led_id]) { + case AN8801R_LED_FN_LINK: + led_trigger = BIT(TRIGGER_NETDEV_LINK); + break; + case AN8801R_LED_FN_ACTIVITY: + led_trigger = BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX); + break; + default: + led_trigger = 0; + break; + } + + ret = an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_LED_GPIO, + BIT(led_gpio)); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_rmw(phydev, + AN8801_BPBUS_REG_LED_ID_SEL, + LED_ID_GPIO_SEL_MASK(led_gpio), + LED_ID_GPIO_SEL(led_id, + led_gpio)); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_GPIO_MODE, + BIT(led_gpio)); + if (ret) + return ret; + + if (!led_trigger) + continue; + + ret = an8801r_led_hw_control_set(phydev, led_id, led_trigger); + if (ret) + return ret; + } + + return ret; +} + +static int an8801r_reset_wake(struct phy_device *phydev) +{ + struct an8801r_priv *priv = phydev->priv; + u32 reg_val = 0; + int ret; + + /* Enable wakeup clear and disable wake up output */ + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL2, + AN8801_PME_WAKEUP_CLR | + AN8801_WAKE_OUT_POLARITY_NEG); + if (ret) + return ret; + + /* Clear WAKEUP_CTL1 register before enabling the wakeup events + * again + */ + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1, + 0); + if (ret) + return ret; + + if (priv->wake_magic_enabled) + reg_val |= AN8801_WOL_WAKE_MAGIC_EN; + + if (priv->wake_lnkchg_enabled) + reg_val |= AN8801_WOL_WAKE_LNKCHG_EN; + + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1, + reg_val); + if (ret) + return ret; + + /* Disable wake up clear and re-enable wake up output */ + return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL2, + AN8801_WAKE_OUT_POLARITY_NEG | + AN8801_WAKE_OUT_EN); +} + +static int an8801r_ack_interrupt(struct phy_device *phydev) +{ + int ret; + + /* Reset wake status */ + ret = an8801r_reset_wake(phydev); + if (ret) + return ret; + + /* Clear the interrupts by writing the reg */ + return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKE_IRQ_STS, + AN8801_IRQ_WAKE_ALL); +} + +static int an8801r_config_intr(struct phy_device *phydev) +{ + int ret; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + u32 val = FIELD_PREP(AN8801_PHY_IRQ_GPIO_NUM_MASK, + AN8801_PHY_IRQ_GPIO_NUM); + + ret = an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_PHY_IRQ_GPIO, + val); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + + } else { + ret = an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_PHY_IRQ_GPIO, + 0); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + } + + return an8801r_ack_interrupt(phydev); +} + +static irqreturn_t an8801r_handle_interrupt(struct phy_device *phydev) +{ + u32 irq_status = 0; + bool irq_handled = false; + int ret; + + ret = an8801_buckpbus_reg_read(phydev, AN8801_BPBUS_REG_WAKE_IRQ_STS, + &irq_status); + if (ret) + return IRQ_NONE; + + ret = an8801r_ack_interrupt(phydev); + if (ret) + return IRQ_NONE; + + if (irq_status & AN8801_IRQ_WAKE_MAGICPKT) { + pm_wakeup_event(&phydev->mdio.dev, 0); + irq_handled = true; + } + + if (irq_status & AN8801_IRQ_WAKE_LNKCHG) { + phy_trigger_machine(phydev); + irq_handled = true; + } + + return irq_handled ? IRQ_HANDLED : IRQ_NONE; +} + +static void an8801r_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + u32 reg_val; + int ret; + + /* If the PHY is not capable of waking the system, then WoL can not + * be supported. + */ + if (!device_can_wakeup(&phydev->mdio.dev)) { + wol->supported = 0; + return; + } + + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + ret = an8801_buckpbus_reg_read(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1, + ®_val); + if (ret) + return; + + if (reg_val & AN8801_WOL_WAKE_MAGIC_EN) + wol->wolopts |= WAKE_MAGIC; + else + wol->wolopts &= ~WAKE_MAGIC; +} + +static int an8801r_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + struct an8801r_priv *priv = phydev->priv; + struct net_device *attach_dev; + const u8 *macaddr; + u32 reg_val; + int ret; + + if (!device_can_wakeup(&phydev->mdio.dev)) + return -EOPNOTSUPP; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + if (wol->wolopts & WAKE_MAGIC) { + attach_dev = phydev->attached_dev; + if (!attach_dev) + return -ENODEV; + + macaddr = (const u8 *)attach_dev->dev_addr; + if (!is_valid_ether_addr(macaddr)) + return -EINVAL; + + /* MAC bits 16..47 */ + reg_val = (macaddr[2] << 24) | (macaddr[3] << 16); + reg_val |= (macaddr[4] << 8) | (macaddr[5]); + + ret = an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_WOL_MAC_16_47, + reg_val); + if (ret) + return ret; + + /* MAC bits 0..15 */ + reg_val = (macaddr[0] << 8) | (macaddr[1]); + + ret = an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_WOL_MAC_0_15, + reg_val); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKEUP_CTL1, + AN8801_WOL_WAKE_MAGIC_EN); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_MAGICPKT); + if (ret) + return ret; + + } else { + ret = an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKEUP_CTL1, + AN8801_WOL_WAKE_MAGIC_EN); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_MAGICPKT); + if (ret) + return ret; + } + + priv->wake_magic_enabled = !!(wol->wolopts & WAKE_MAGIC); + + return device_set_wakeup_enable(&phydev->mdio.dev, + priv->wake_magic_enabled); +} + +static int an8801r_of_init_leds(struct phy_device *phydev, u8 *led_cfg) +{ + struct device *dev = &phydev->mdio.dev; + struct device_node *np = dev->of_node; + struct device_node *leds; + u32 function_enum_idx; + int ret = 0; + + if (!np) + return 0; + + /* If devicetree is present, leds configuration is required */ + leds = of_get_child_by_name(np, "leds"); + if (!leds) + return 0; + + for_each_available_child_of_node_scoped(leds, led) { + u32 led_idx; + + ret = of_property_read_u32(led, "reg", &led_idx); + if (ret) + goto out; + + if (led_idx >= AN8801R_NUM_LEDS) { + ret = -EINVAL; + goto out; + } + + ret = of_property_read_u32(led, "function-enumerator", + &function_enum_idx); + if (ret) { + function_enum_idx = AN8801R_LED_FN_NONE; + ret = 0; + } + + if (function_enum_idx >= AN8801R_LED_FN_MAX) { + ret = -EINVAL; + goto out; + } + + led_cfg[led_idx] = function_enum_idx; + } +out: + of_node_put(leds); + return ret; +} + +static int an8801r_rgmii_rxdelay(struct phy_device *phydev, bool enable, + u16 delay_steps) +{ + u32 reg_val; + + if (delay_steps > RGMII_DELAY_STEP_MASK) + return -EINVAL; + + if (enable) { + /* Set force mode bit to enable RX delay insertion */ + reg_val = delay_steps | RGMII_RXDELAY_FORCE_MODE; + + /* Set align bit to add extra offset for RX delay */ + reg_val |= RGMII_RXDELAY_ALIGN; + } else { + reg_val = 0; + } + + return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_RXDLY_STEP, + reg_val); +} + +static int an8801r_rgmii_txdelay(struct phy_device *phydev, bool enable, + u16 delay_steps) +{ + u32 reg_val; + + if (delay_steps > RGMII_DELAY_STEP_MASK) + return -EINVAL; + + if (enable) { + /* Set force mode bit to enable TX delay insertion */ + reg_val = delay_steps | RGMII_TXDELAY_FORCE_MODE; + } else { + reg_val = 0; + } + + return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_TXDLY_STEP, + reg_val); +} + +static int an8801r_rgmii_delay_config(struct phy_device *phydev) +{ + bool enable_delay; + u16 delay_step; + int ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + enable_delay = true; + delay_step = AN8801_RGMII_TXDELAY_DEFAULT; + } else { + enable_delay = false; + delay_step = RGMII_DELAY_NO_STEP; + } + + ret = an8801r_rgmii_txdelay(phydev, enable_delay, delay_step); + if (ret) + return ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + enable_delay = true; + delay_step = AN8801_RGMII_RXDELAY_DEFAULT; + } else { + enable_delay = false; + delay_step = RGMII_DELAY_NO_STEP; + } + + return an8801r_rgmii_rxdelay(phydev, enable_delay, delay_step); +} + +static int an8801r_config_init(struct phy_device *phydev) +{ + u8 led_default_function[AN8801R_NUM_LEDS] = { 0 }; + int ret; + + ret = an8801r_of_init_leds(phydev, led_default_function); + if (ret) + return ret; + + /* Disable Low Power Mode (LPM) */ + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL0, + FIELD_PREP(AN8801_PHY_INTFUNC_MASK, 0x1e)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL1, + FIELD_PREP(AN8801_PHY_INTFUNC_MASK, 0x2)); + if (ret) + return ret; + + /* Set the PHY to perform auto-downshift after 3 auto-negotiation + * attempts + */ + ret = phy_write_paged(phydev, AIR_PHY_PAGE_EXTENDED_1, + AN8801_EXT_REG_PHY, + FIELD_PREP(AN8801_EXT_PHY_CTRL1, 0x1d) | + FIELD_PREP(AN8801_EXT_PHY_DOWNSHIFT_CTL, 1) | + AN8801_EXT_PHY_DOWNSHIFT_EN); + if (ret < 0) + return ret; + + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_BYPASS_PTP, + AN8801_BYP_PTP_RGMII_TO_GPHY); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(0), + AN8801_EFIFO_RX_EN | + AN8801_EFIFO_TX_EN | + AN8801_EFIFO_RX_CLK_EN | + AN8801_EFIFO_TX_CLK_EN | + AN8801_EFIFO_RX_EEE_EN | + AN8801_EFIFO_TX_EEE_EN); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(1), + AN8801_EFIFO_ALL_EN); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(2), + AN8801_EFIFO_ALL_EN); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, + AN8801_PHY_TX_PAIR_DLY_SEL_GBE, + FIELD_PREP(AN8801_PHY_PAIR_DLY_SEL_A_GBE, 4) | + FIELD_PREP(AN8801_PHY_PAIR_DLY_SEL_C_GBE, 4)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_CTRL, + AN8801_PHY_RXADC_SAMP_PHSEL_A | + AN8801_PHY_RXADC_SAMP_PHSEL_C); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_REV_0, + FIELD_PREP(AN8801_PHY_RXADC_REV_MASK_A, 1)); + if (ret) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_REV_1, + FIELD_PREP(AN8801_PHY_RXADC_REV_MASK_C, 1)); + if (ret) + return ret; + + ret = an8801r_rgmii_delay_config(phydev); + if (ret) + return ret; + + ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_CKO, + AN8801_CKO_OUTPUT_MODE_AUTO); + if (ret) + return ret; + + ret = an8801r_led_init(phydev, led_default_function); + if (ret) { + phydev_err(phydev, "Cannot initialize LEDs: %d\n", ret); + return ret; + } + + return 0; +} + +static int an8801r_read_status(struct phy_device *phydev) +{ + int prev_speed, ret; + u32 val; + + prev_speed = phydev->speed; + + ret = genphy_read_status(phydev); + if (ret) + return ret; + + if (!phydev->link) { + phydev->speed = SPEED_UNKNOWN; + return 0; + } + + if (prev_speed != phydev->speed) { + /* Ensure that PHY switches to 1G speed when available, + * by configuring the function mode for either 1G or 100M/10M + * operation. + * Therefore, set the link mode register, after read_status + * determines the link speed. + */ + val = phydev->speed == SPEED_1000 ? + AN8801_BPBUS_LINK_MODE_1000 : 0; + + return an8801_buckpbus_reg_rmw(phydev, + AN8801_BPBUS_REG_LINK_MODE, + AN8801_BPBUS_LINK_MODE_1000, + val); + } + + return 0; +} + +static int an8801r_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct an8801r_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wake_lnkchg_enabled = true; + + phydev->priv = priv; + + /* Mark this PHY as wakeup capable and register the interrupt as a + * wakeup IRQ if the PHY is marked as a wakeup source in devicetree, + * and the interrupt is valid. + */ + if (of_property_read_bool(dev->of_node, "wakeup-source") && + phy_interrupt_is_valid(phydev)) { + device_set_wakeup_capable(dev, true); + devm_pm_set_wake_irq(dev, phydev->irq); + } + + return 0; +} + +static int an8801r_suspend(struct phy_device *phydev) +{ + struct an8801r_priv *priv = phydev->priv; + int ret; + + /* If the PHY may wake up by a Wake-on-LAN (WOL) event, disable the link + * interrupt to only keep the WOL magic interrupt enabled + */ + if (device_may_wakeup(&phydev->mdio.dev)) { + priv->wake_lnkchg_enabled = false; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + ret = an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + } + + /* Reset WOL status */ + ret = an8801r_reset_wake(phydev); + if (ret) + return ret; + } + + if (!phydev->wol_enabled) + return genphy_suspend(phydev); + + return 0; +} + +static int an8801r_resume(struct phy_device *phydev) +{ + struct an8801r_priv *priv = phydev->priv; + int ret; + + ret = genphy_resume(phydev); + if (ret) + return ret; + + /* Restore the interrupt enable so phylib can receive link + * state interrupts. + */ + if (device_may_wakeup(&phydev->mdio.dev)) { + priv->wake_lnkchg_enabled = true; + + ret = an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKEUP_CTL1, + AN8801_WOL_WAKE_LNKCHG_EN); + if (ret) + return ret; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + ret = an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + } + } + + return ret; +} + +static struct phy_driver airoha_driver[] = { +{ + PHY_ID_MATCH_MODEL(AN8801R_PHY_ID), + .name = "Airoha AN8801R", + .probe = an8801r_probe, + .config_init = an8801r_config_init, + .suspend = an8801r_suspend, + .resume = an8801r_resume, + .config_aneg = genphy_config_aneg, + .read_status = an8801r_read_status, + .config_intr = an8801r_config_intr, + .handle_interrupt = an8801r_handle_interrupt, + .set_wol = an8801r_set_wol, + .get_wol = an8801r_get_wol, + .read_page = air_phy_read_page, + .write_page = air_phy_write_page, + .flags = PHY_ALWAYS_CALL_SUSPEND, + .led_brightness_set = an8801r_led_brightness_set, + .led_blink_set = an8801r_led_blink_set, + .led_hw_is_supported = an8801r_led_hw_is_supported, + .led_hw_control_set = an8801r_led_hw_control_set, + .led_hw_control_get = an8801r_led_hw_control_get, + .led_polarity_set = an8801r_led_polarity_set, +} }; +module_phy_driver(airoha_driver); + +static struct mdio_device_id __maybe_unused an8801_tbl[] = { + { PHY_ID_MATCH_MODEL(AN8801R_PHY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(mdio, an8801_tbl); + +MODULE_DESCRIPTION("Airoha AN8801 PHY driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index a86129ce693c..edd49c193e47 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -22,6 +22,8 @@ #include #include +#include "air_phy_lib.h" + #define EN8811H_PHY_ID 0x03a2a411 #define AN8811HB_PHY_ID 0xc0ff04a0 @@ -41,23 +43,6 @@ #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc -#define AIR_EXT_PAGE_ACCESS 0x1f -#define AIR_PHY_PAGE_STANDARD 0x0000 -#define AIR_PHY_PAGE_EXTENDED_4 0x0004 - -/* MII Registers Page 4*/ -#define AIR_BPBUS_MODE 0x10 -#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 -#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) -#define AIR_BPBUS_WR_ADDR_HIGH 0x11 -#define AIR_BPBUS_WR_ADDR_LOW 0x12 -#define AIR_BPBUS_WR_DATA_HIGH 0x13 -#define AIR_BPBUS_WR_DATA_LOW 0x14 -#define AIR_BPBUS_RD_ADDR_HIGH 0x15 -#define AIR_BPBUS_RD_ADDR_LOW 0x16 -#define AIR_BPBUS_RD_DATA_HIGH 0x17 -#define AIR_BPBUS_RD_DATA_LOW 0x18 - /* Registers on MDIO_MMD_VEND1 */ #define EN8811H_PHY_FW_STATUS 0x8009 #define EN8811H_PHY_READY 0x02 @@ -260,16 +245,6 @@ static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) | BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX); -static int air_phy_read_page(struct phy_device *phydev) -{ - return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); -} - -static int air_phy_write_page(struct phy_device *phydev, int page) -{ - return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); -} - static int __air_pbus_reg_write(struct mdio_device *mdiodev, u32 pbus_reg, u32 pbus_data) { @@ -295,183 +270,6 @@ static int __air_pbus_reg_write(struct mdio_device *mdiodev, upper_16_bits(pbus_data)); } -static int __air_buckpbus_reg_write(struct phy_device *phydev, - u32 pbus_address, u32 pbus_data) -{ - int ret; - - ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, - upper_16_bits(pbus_data)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, - lower_16_bits(pbus_data)); - if (ret < 0) - return ret; - - return 0; -} - -static int air_buckpbus_reg_write(struct phy_device *phydev, - u32 pbus_address, u32 pbus_data) -{ - int saved_page; - int ret = 0; - - saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >= 0) { - ret = __air_buckpbus_reg_write(phydev, pbus_address, - pbus_data); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - pbus_address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - -static int __air_buckpbus_reg_read(struct phy_device *phydev, - u32 pbus_address, u32 *pbus_data) -{ - int pbus_data_low, pbus_data_high; - int ret; - - ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); - if (pbus_data_high < 0) - return pbus_data_high; - - pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); - if (pbus_data_low < 0) - return pbus_data_low; - - *pbus_data = pbus_data_low | (pbus_data_high << 16); - return 0; -} - -static int air_buckpbus_reg_read(struct phy_device *phydev, - u32 pbus_address, u32 *pbus_data) -{ - int saved_page; - int ret = 0; - - saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >= 0) { - ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - pbus_address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - -static int __air_buckpbus_reg_modify(struct phy_device *phydev, - u32 pbus_address, u32 mask, u32 set) -{ - int pbus_data_low, pbus_data_high; - u32 pbus_data_old, pbus_data_new; - int ret; - - ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); - if (pbus_data_high < 0) - return pbus_data_high; - - pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); - if (pbus_data_low < 0) - return pbus_data_low; - - pbus_data_old = pbus_data_low | (pbus_data_high << 16); - pbus_data_new = (pbus_data_old & ~mask) | set; - if (pbus_data_new == pbus_data_old) - return 0; - - ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, - upper_16_bits(pbus_data_new)); - if (ret < 0) - return ret; - - ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, - lower_16_bits(pbus_data_new)); - if (ret < 0) - return ret; - - return 0; -} - -static int air_buckpbus_reg_modify(struct phy_device *phydev, - u32 pbus_address, u32 mask, u32 set) -{ - int saved_page; - int ret = 0; - - saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >= 0) { - ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, - set); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - pbus_address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - static int __air_write_buf(struct phy_device *phydev, u32 address, const struct firmware *fw) { @@ -530,8 +328,8 @@ static int en8811h_wait_mcu_ready(struct phy_device *phydev) { int ret, reg_value; - ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_FINISH); + ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); if (ret) return ret; @@ -556,28 +354,29 @@ static int an8811hb_check_crc(struct phy_device *phydev, u32 set1, int ret; /* Configure CRC */ - ret = air_buckpbus_reg_modify(phydev, set1, - AN8811HB_CRC_RD_EN, - AN8811HB_CRC_RD_EN); + ret = air_phy_buckpbus_reg_modify(phydev, set1, + AN8811HB_CRC_RD_EN, + AN8811HB_CRC_RD_EN); if (ret < 0) return ret; - air_buckpbus_reg_read(phydev, set1, &pbus_value); + air_phy_buckpbus_reg_read(phydev, set1, &pbus_value); do { msleep(300); - air_buckpbus_reg_read(phydev, mon2, &pbus_value); + air_phy_buckpbus_reg_read(phydev, mon2, &pbus_value); /* We do not know what errors this check is supposed * catch or what to do about a failure. So print the * result and continue like the vendor driver does. */ if (pbus_value & AN8811HB_CRC_ST) { - air_buckpbus_reg_read(phydev, mon3, &pbus_value); + air_phy_buckpbus_reg_read(phydev, mon3, &pbus_value); phydev_dbg(phydev, "CRC Check %s!\n", pbus_value & AN8811HB_CRC_CHECK_PASS ? "PASS" : "FAIL"); - return air_buckpbus_reg_modify(phydev, set1, - AN8811HB_CRC_RD_EN, 0); + return air_phy_buckpbus_reg_modify(phydev, set1, + AN8811HB_CRC_RD_EN, + 0); } } while (--retry); @@ -589,8 +388,8 @@ static void en8811h_print_fw_version(struct phy_device *phydev) { struct en8811h_priv *priv = phydev->priv; - air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, - &priv->firmware_version); + air_phy_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, + &priv->firmware_version); phydev_info(phydev, "MD32 firmware version: %08x\n", priv->firmware_version); } @@ -672,8 +471,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev) if (ret < 0) return ret; - ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); if (ret < 0) return ret; @@ -714,14 +513,14 @@ static int en8811h_load_firmware(struct phy_device *phydev) if (ret < 0) goto en8811h_load_firmware_rel1; - ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); if (ret < 0) goto en8811h_load_firmware_out; - ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, - EN8811H_FW_CTRL_2_LOADING); + ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, + EN8811H_FW_CTRL_2_LOADING); if (ret < 0) goto en8811h_load_firmware_out; @@ -733,8 +532,8 @@ static int en8811h_load_firmware(struct phy_device *phydev) if (ret < 0) goto en8811h_load_firmware_out; - ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, 0); + ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, 0); if (ret < 0) goto en8811h_load_firmware_out; @@ -770,8 +569,8 @@ static int en8811h_restart_mcu(struct phy_device *phydev) return ret; } - ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); if (ret < 0) return ret; @@ -1065,7 +864,7 @@ static unsigned long an8811hb_clk_recalc_rate(struct clk_hw *hw, u32 pbus_value; int ret; - ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value); + ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value); if (ret < 0) return ret; @@ -1077,9 +876,9 @@ static int an8811hb_clk_enable(struct clk_hw *hw) struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); struct phy_device *phydev = priv->phydev; - return air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, - AN8811HB_CLK_DRV_CKO_MASK, - AN8811HB_CLK_DRV_CKO_MASK); + return air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, + AN8811HB_CLK_DRV_CKO_MASK, + AN8811HB_CLK_DRV_CKO_MASK); } static void an8811hb_clk_disable(struct clk_hw *hw) @@ -1087,8 +886,8 @@ static void an8811hb_clk_disable(struct clk_hw *hw) struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); struct phy_device *phydev = priv->phydev; - air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, - AN8811HB_CLK_DRV_CKO_MASK, 0); + air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, + AN8811HB_CLK_DRV_CKO_MASK, 0); } static int an8811hb_clk_is_enabled(struct clk_hw *hw) @@ -1098,7 +897,7 @@ static int an8811hb_clk_is_enabled(struct clk_hw *hw) u32 pbus_value; int ret; - ret = air_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value); + ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value); if (ret < 0) return ret; @@ -1164,7 +963,7 @@ static unsigned long en8811h_clk_recalc_rate(struct clk_hw *hw, u32 pbus_value; int ret; - ret = air_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value); + ret = air_phy_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value); if (ret < 0) return ret; @@ -1176,9 +975,9 @@ static int en8811h_clk_enable(struct clk_hw *hw) struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); struct phy_device *phydev = priv->phydev; - return air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, - EN8811H_CLK_CGM_CKO, - EN8811H_CLK_CGM_CKO); + return air_phy_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, + EN8811H_CLK_CGM_CKO, + EN8811H_CLK_CGM_CKO); } static void en8811h_clk_disable(struct clk_hw *hw) @@ -1186,8 +985,8 @@ static void en8811h_clk_disable(struct clk_hw *hw) struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); struct phy_device *phydev = priv->phydev; - air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, - EN8811H_CLK_CGM_CKO, 0); + air_phy_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, + EN8811H_CLK_CGM_CKO, 0); } static int en8811h_clk_is_enabled(struct clk_hw *hw) @@ -1197,7 +996,7 @@ static int en8811h_clk_is_enabled(struct clk_hw *hw) u32 pbus_value; int ret; - ret = air_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value); + ret = air_phy_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value); if (ret < 0) return ret; @@ -1327,9 +1126,9 @@ static int an8811hb_probe(struct phy_device *phydev) goto err_dev_create; /* Configure led gpio pins as output */ - ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, - AN8811HB_GPIO_OUTPUT_345, - AN8811HB_GPIO_OUTPUT_345); + ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, + AN8811HB_GPIO_OUTPUT_345, + AN8811HB_GPIO_OUTPUT_345); if (ret < 0) goto err_dev_create; @@ -1375,9 +1174,9 @@ static int en8811h_probe(struct phy_device *phydev) return ret; /* Configure led gpio pins as output */ - ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, - EN8811H_GPIO_OUTPUT_345, - EN8811H_GPIO_OUTPUT_345); + ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, + EN8811H_GPIO_OUTPUT_345, + EN8811H_GPIO_OUTPUT_345); if (ret < 0) return ret; @@ -1397,9 +1196,9 @@ static int an8811hb_config_serdes_polarity(struct phy_device *phydev) return ret; if (pol == PHY_POL_NORMAL) pbus_value |= AN8811HB_RX_POLARITY_NORMAL; - ret = air_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY, - AN8811HB_RX_POLARITY_NORMAL, - pbus_value); + ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY, + AN8811HB_RX_POLARITY_NORMAL, + pbus_value); if (ret < 0) return ret; @@ -1410,9 +1209,9 @@ static int an8811hb_config_serdes_polarity(struct phy_device *phydev) pbus_value = 0; if (pol == PHY_POL_NORMAL) pbus_value |= AN8811HB_TX_POLARITY_NORMAL; - return air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY, - AN8811HB_TX_POLARITY_NORMAL, - pbus_value); + return air_phy_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY, + AN8811HB_TX_POLARITY_NORMAL, + pbus_value); } static int en8811h_config_serdes_polarity(struct phy_device *phydev) @@ -1446,9 +1245,10 @@ static int en8811h_config_serdes_polarity(struct phy_device *phydev) if (pol == PHY_POL_NORMAL) pbus_value |= EN8811H_POLARITY_TX_NORMAL; - return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, - EN8811H_POLARITY_RX_REVERSE | - EN8811H_POLARITY_TX_NORMAL, pbus_value); + return air_phy_buckpbus_reg_modify(phydev, EN8811H_POLARITY, + EN8811H_POLARITY_RX_REVERSE | + EN8811H_POLARITY_TX_NORMAL, + pbus_value); } static int an8811hb_config_init(struct phy_device *phydev) @@ -1600,8 +1400,8 @@ static int en8811h_read_status(struct phy_device *phydev) val & MDIO_AN_10GBT_STAT_LP2_5G); } else { /* Get link partner 2.5GBASE-T ability from vendor register */ - ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, - &pbus_value); + ret = air_phy_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, + &pbus_value); if (ret < 0) return ret; linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c new file mode 100644 index 000000000000..5141db19fa5e --- /dev/null +++ b/drivers/net/phy/air_phy_lib.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Airoha Ethernet PHY common library + * + * Copyright (C) 2026 Airoha Technology Corp. + * Copyright (C) 2026 Collabora Ltd. + * Louis-Alexis Eyraud + */ + +#include +#include +#include +#include + +#include "air_phy_lib.h" + +static int __air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ + int pbus_data_low, pbus_data_high; + int ret; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return pbus_data_low; + + *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +} + +static int __air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ + int ret; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data)); + if (ret < 0) + return ret; + + return 0; +} + +static int __air_buckpbus_reg_modify(struct phy_device *phydev, + u32 pbus_address, u32 mask, u32 set) +{ + int pbus_data_low, pbus_data_high; + u32 pbus_data_old, pbus_data_new; + int ret; + + ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return pbus_data_low; + + pbus_data_old = pbus_data_low | (pbus_data_high << 16); + pbus_data_new = (pbus_data_old & ~mask) | set; + if (pbus_data_new == pbus_data_old) + return 0; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data_new)); + if (ret < 0) + return ret; + + ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data_new)); + if (ret < 0) + return ret; + + return 0; +} + +int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address, + u32 *pbus_data) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} +EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_read); + +int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, + u32 pbus_data) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_buckpbus_reg_write(phydev, pbus_address, + pbus_data); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} +EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_write); + +int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address, + u32 mask, u32 set) +{ + int saved_page; + int ret = 0; + + saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, + set); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} +EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_modify); + +int air_phy_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); +} +EXPORT_SYMBOL_GPL(air_phy_read_page); + +int air_phy_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); +} +EXPORT_SYMBOL_GPL(air_phy_write_page); + +MODULE_DESCRIPTION("Airoha PHY Library"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Louis-Alexis Eyraud"); diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h new file mode 100644 index 000000000000..01bb32e7c7c9 --- /dev/null +++ b/drivers/net/phy/air_phy_lib.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2026 Airoha Technology Corp. + * Copyright (C) 2026 Collabora Ltd. + * Louis-Alexis Eyraud + */ + +#ifndef __AIR_PHY_LIB_H +#define __AIR_PHY_LIB_H + +#include + +#define AIR_EXT_PAGE_ACCESS 0x1f + +#define AIR_PHY_PAGE_STANDARD 0x0000 +#define AIR_PHY_PAGE_EXTENDED_1 0x0001 +#define AIR_PHY_PAGE_EXTENDED_4 0x0004 + +/* MII Registers Page 4*/ +#define AIR_BPBUS_MODE 0x10 +#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 +#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) +#define AIR_BPBUS_WR_ADDR_HIGH 0x11 +#define AIR_BPBUS_WR_ADDR_LOW 0x12 +#define AIR_BPBUS_WR_DATA_HIGH 0x13 +#define AIR_BPBUS_WR_DATA_LOW 0x14 +#define AIR_BPBUS_RD_ADDR_HIGH 0x15 +#define AIR_BPBUS_RD_ADDR_LOW 0x16 +#define AIR_BPBUS_RD_DATA_HIGH 0x17 +#define AIR_BPBUS_RD_DATA_LOW 0x18 + +int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address, + u32 mask, u32 set); +int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address, + u32 *pbus_data); +int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, + u32 pbus_data); +int air_phy_read_page(struct phy_device *phydev); +int air_phy_write_page(struct phy_device *phydev, int page); + +#endif /* __AIR_PHY_LIB_H */ diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index 41f3676c7f1e..e95d391a6dc2 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -385,15 +385,15 @@ static int aqr105_setup_forced(struct phy_device *phydev) vend |= MDIO_AN_VEND_PROV_1000BASET_HALF; break; case SPEED_2500: - adv |= (ADVERTISE_NPAGE | ADVERTISE_RESV); + adv |= (ADVERTISE_NPAGE | ADVERTISE_XNP); vend |= MDIO_AN_VEND_PROV_2500BASET_FULL; break; case SPEED_5000: - adv |= (ADVERTISE_NPAGE | ADVERTISE_RESV); + adv |= (ADVERTISE_NPAGE | ADVERTISE_XNP); vend |= MDIO_AN_VEND_PROV_5000BASET_FULL; break; case SPEED_10000: - adv |= (ADVERTISE_NPAGE | ADVERTISE_RESV); + adv |= (ADVERTISE_NPAGE | ADVERTISE_XNP); ctrl10 |= MDIO_AN_10GBT_CTRL_ADV10G; break; default: diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c index c012dfab3171..6fc86be9d593 100644 --- a/drivers/net/phy/dp83822.c +++ b/drivers/net/phy/dp83822.c @@ -4,6 +4,7 @@ * Copyright (C) 2017 Texas Instruments Inc. */ +#include #include #include #include @@ -984,13 +985,20 @@ static int dp83822_attach_mdi_port(struct phy_device *phydev, static int dp8382x_probe(struct phy_device *phydev) { + struct device *dev = &phydev->mdio.dev; struct dp83822_private *dp83822; + struct clk *clk; - dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822), - GFP_KERNEL); + dp83822 = devm_kzalloc(dev, sizeof(*dp83822), GFP_KERNEL); if (!dp83822) return -ENOMEM; + clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(clk)) { + return dev_err_probe(dev, PTR_ERR(clk), + "Failed to request ref clock\n"); + } + dp83822->tx_amplitude_100base_tx_index = -1; dp83822->mac_termination_index = -1; phydev->priv = dp83822; diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 3fb2293f568f..88255e92b4cd 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -103,6 +103,10 @@ #define DP83867_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) #define DP83867_PHYCR_SGMII_EN BIT(11) #define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10) +#define DP83867_PHYCR_MDIX_MASK GENMASK(6, 5) +#define DP83867_PHYCR_MDIX_MDI (0x0 << 5) +#define DP83867_PHYCR_MDIX_MDIX (0x1 << 5) +#define DP83867_PHYCR_MDIX_AUTO (0x3 << 5) /* RGMIIDCTL bits */ #define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf @@ -123,6 +127,10 @@ #define DP83867_PHYSTS_100 BIT(14) #define DP83867_PHYSTS_DUPLEX BIT(13) #define DP83867_PHYSTS_LINK BIT(10) +#define DP83867_PHYSTS_MDIX_CD BIT(9) +#define DP83867_PHYSTS_MDIX_AB BIT(8) +#define DP83867_PHYSTS_MDIX_MASK (DP83867_PHYSTS_MDIX_AB | \ + DP83867_PHYSTS_MDIX_CD) /* CFG2 bits */ #define DP83867_DOWNSHIFT_EN (BIT(8) | BIT(9)) @@ -391,6 +399,22 @@ static int dp83867_read_status(struct phy_device *phydev) else phydev->speed = SPEED_10; + if (!(status & DP83867_PHYSTS_LINK)) { + phydev->mdix = ETH_TP_MDI_INVALID; + } else { + switch (status & DP83867_PHYSTS_MDIX_MASK) { + case 0: + phydev->mdix = ETH_TP_MDI; + break; + case DP83867_PHYSTS_MDIX_MASK: + phydev->mdix = ETH_TP_MDI_X; + break; + default: + phydev->mdix = ETH_TP_MDI_INVALID; + break; + } + } + return 0; } @@ -714,6 +738,8 @@ static int dp83867_config_init(struct phy_device *phydev) struct dp83867_private *dp83867 = phydev->priv; int ret, val, bs; + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + /* Force speed optimization for the PHY even if it strapped */ ret = phy_modify(phydev, DP83867_CFG2, DP83867_DOWNSHIFT_EN, DP83867_DOWNSHIFT_EN); @@ -873,6 +899,39 @@ static int dp83867_config_init(struct phy_device *phydev) return 0; } +static int dp83867_config_mdix(struct phy_device *phydev, u8 ctrl) +{ + int val; + + switch (ctrl) { + case ETH_TP_MDI: + val = DP83867_PHYCR_MDIX_MDI; + break; + case ETH_TP_MDI_X: + val = DP83867_PHYCR_MDIX_MDIX; + break; + case ETH_TP_MDI_AUTO: + val = DP83867_PHYCR_MDIX_AUTO; + break; + default: + return -EINVAL; + } + + return phy_modify(phydev, MII_DP83867_PHYCTRL, + DP83867_PHYCR_MDIX_MASK, val); +} + +static int dp83867_config_aneg(struct phy_device *phydev) +{ + int ret; + + ret = dp83867_config_mdix(phydev, phydev->mdix_ctrl); + if (ret) + return ret; + + return genphy_config_aneg(phydev); +} + static int dp83867_phy_reset(struct phy_device *phydev) { int err; @@ -1127,6 +1186,7 @@ static struct phy_driver dp83867_driver[] = { .probe = dp83867_probe, .config_init = dp83867_config_init, + .config_aneg = dp83867_config_aneg, .soft_reset = dp83867_phy_reset, .read_status = dp83867_read_status, diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index 12ff4c1f285d..afbcec711744 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -10,11 +10,16 @@ #include #include +#define XWAY_MDIO_ERRCNT 0x15 /* error counter */ #define XWAY_MDIO_MIICTRL 0x17 /* mii control */ #define XWAY_MDIO_IMASK 0x19 /* interrupt mask */ #define XWAY_MDIO_ISTAT 0x1A /* interrupt status */ #define XWAY_MDIO_LED 0x1B /* led control */ +#define XWAY_MDIO_ERRCNT_SEL GENMASK(11, 8) +#define XWAY_MDIO_ERRCNT_COUNT GENMASK(7, 0) +#define XWAY_MDIO_ERRCNT_SEL_RXERR 0 + #define XWAY_MDIO_MIICTRL_RXSKEW_MASK GENMASK(14, 12) #define XWAY_MDIO_MIICTRL_TXSKEW_MASK GENMASK(10, 8) @@ -169,6 +174,10 @@ #define PHY_ID_PHY11G_VR9_1_2 0xD565A409 #define PHY_ID_PHY22F_VR9_1_2 0xD565A419 +struct xway_gphy_priv { + u64 rx_errors; +}; + static const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500, 3000, 3500}; @@ -299,6 +308,21 @@ static int xway_gphy_config_init(struct phy_device *phydev) if (err) return err; + /* Count MDI RX errors (SymbolErrorDuringCarrier) */ + return phy_write(phydev, XWAY_MDIO_ERRCNT, + FIELD_PREP(XWAY_MDIO_ERRCNT_SEL, XWAY_MDIO_ERRCNT_SEL_RXERR)); +} + +static int xway_gphy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct xway_gphy_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + phydev->priv = priv; + return 0; } @@ -532,6 +556,31 @@ static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index, return -EINVAL; } +static int xway_gphy_update_stats(struct phy_device *phydev) +{ + struct xway_gphy_priv *priv = phydev->priv; + int ret; + + /* XWAY_MDIO_ERRCNT: 8-bit read-clear counter, SEL set to RXERR */ + ret = phy_read(phydev, XWAY_MDIO_ERRCNT); + if (ret < 0) + return ret; + + priv->rx_errors += FIELD_GET(XWAY_MDIO_ERRCNT_COUNT, ret); + + return 0; +} + +static void xway_gphy_get_phy_stats(struct phy_device *phydev, + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats) +{ + struct xway_gphy_priv *priv = phydev->priv; + + eth_stats->SymbolErrorDuringCarrier = priv->rx_errors; + stats->rx_errors = priv->rx_errors; +} + static struct phy_driver xway_gphy[] = { { .phy_id = PHY_ID_PHY11G_1_3, @@ -539,6 +588,7 @@ static struct phy_driver xway_gphy[] = { .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -549,12 +599,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_1_3, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.3", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -565,12 +618,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_1_4, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -581,12 +637,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_1_4, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.4", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .config_aneg = xway_gphy14_config_aneg, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, @@ -597,12 +656,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_1_5, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -612,12 +674,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_1_5, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -627,12 +692,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_VR9_1_1, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (xRX v1.1 integrated)", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -642,12 +710,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_VR9_1_1, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (xRX v1.1 integrated)", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -657,12 +728,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY11G_VR9_1_2, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY11G (xRX v1.2 integrated)", /* PHY_GBIT_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -672,12 +746,15 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, { .phy_id = PHY_ID_PHY22F_VR9_1_2, .phy_id_mask = 0xffffffff, .name = "Intel XWAY PHY22F (xRX v1.2 integrated)", /* PHY_BASIC_FEATURES */ .config_init = xway_gphy_config_init, + .probe = xway_gphy_probe, .handle_interrupt = xway_gphy_handle_interrupt, .config_intr = xway_gphy_config_intr, .suspend = genphy_suspend, @@ -687,6 +764,8 @@ static struct phy_driver xway_gphy[] = { .led_hw_control_get = xway_gphy_led_hw_control_get, .led_hw_control_set = xway_gphy_led_hw_control_set, .led_polarity_set = xway_gphy_led_polarity_set, + .update_stats = xway_gphy_update_stats, + .get_phy_stats = xway_gphy_get_phy_stats, }, }; module_phy_driver(xway_gphy); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index e211a523c258..55df5efcfc86 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -287,6 +287,12 @@ /* PHY Control 2 / PHY Control (if no PHY Control 1) */ #define MII_KSZPHY_CTRL_2 0x1f #define MII_KSZPHY_CTRL MII_KSZPHY_CTRL_2 + +/* Vendor-specific Clause 22 register, virtualized by KSZ87xx embedded PHYs DSA driver */ +#define MII_KSZ87XX_SHORT_CABLE 0x1a +#define MII_KSZ87XX_LPF_BW 0x1b +#define MII_KSZ87XX_EQ_INIT 0x1c + /* bitmap of PHY register to set interrupt mode */ #define KSZ8081_CTRL2_HP_MDIX BIT(15) #define KSZ8081_CTRL2_MDI_MDI_X_SELECT BIT(14) @@ -940,6 +946,59 @@ static int ksz8795_match_phy_device(struct phy_device *phydev, return ksz8051_ksz8795_match_phy_device(phydev, false); } +static int ksz8795_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + int ret; + + switch (tuna->id) { + case ETHTOOL_PHY_SHORT_CABLE_PRESET: + ret = phy_read(phydev, MII_KSZ87XX_SHORT_CABLE); + if (ret < 0) + return ret; + *(u8 *)data = ret; + return 0; + case ETHTOOL_PHY_LPF_BW: + ret = phy_read(phydev, MII_KSZ87XX_LPF_BW); + if (ret < 0) + return ret; + *(u32 *)data = ret & 0xff; + return 0; + case ETHTOOL_PHY_DSP_EQ_INIT_VALUE: + ret = phy_read(phydev, MII_KSZ87XX_EQ_INIT); + if (ret < 0) + return ret; + *(u32 *)data = ret & 0xff; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int ksz8795_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) +{ + u32 val; + + switch (tuna->id) { + case ETHTOOL_PHY_SHORT_CABLE_PRESET: + return phy_write(phydev, MII_KSZ87XX_SHORT_CABLE, + *(const u8 *)data); + case ETHTOOL_PHY_LPF_BW: + val = *(const u32 *)data; + if (val > 0xff) + return -EINVAL; + return phy_write(phydev, MII_KSZ87XX_LPF_BW, (u8)val); + case ETHTOOL_PHY_DSP_EQ_INIT_VALUE: + val = *(const u32 *)data; + if (val > 0xff) + return -EINVAL; + return phy_write(phydev, MII_KSZ87XX_EQ_INIT, (u8)val); + default: + return -EOPNOTSUPP; + } +} + static int ksz9021_load_values_from_of(struct phy_device *phydev, const struct device_node *of_node, u16 reg, @@ -6961,6 +7020,8 @@ static struct phy_driver ksphy_driver[] = { /* PHY_BASIC_FEATURES */ .config_init = kszphy_config_init, .match_phy_device = ksz8795_match_phy_device, + .get_tunable = ksz8795_get_tunable, + .set_tunable = ksz8795_set_tunable, .suspend = genphy_suspend, .resume = genphy_resume, }, { diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 4d62f7b36212..5071605a1a11 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Motorcomm 8511/8521/8531/8531S/8821 PHY driver. + * Motorcomm 8511/8521/8522/8531/8531S/8821 PHY driver. * * Author: Peter Geis * Author: Frank @@ -10,10 +10,11 @@ #include #include #include -#include +#include #define PHY_ID_YT8511 0x0000010a #define PHY_ID_YT8521 0x0000011a +#define PHY_ID_YT8522 0x4f51e928 #define PHY_ID_YT8531 0x4f51e91b #define PHY_ID_YT8531S 0x4f51e91a #define PHY_ID_YT8821 0x4f51ea19 @@ -227,6 +228,13 @@ #define YT8521_LED_100_ON_EN BIT(5) #define YT8521_LED_10_ON_EN BIT(4) +#define YT8522_EXTREG_SLEEP_CONTROL 0x2027 +#define YT8522_EN_SLEEP_SW BIT(15) + +#define YT8522_EXTENDED_COMBO_CTRL 0x4000 +#define YT8522_RXDV_SEL BIT(4) +#define YT8522_RMII_EN BIT(1) + #define YTPHY_MISC_CONFIG_REG 0xA006 #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) #define YTPHY_MCR_FIBER_1000BX (0x1 << 0) @@ -843,12 +851,12 @@ static u32 ytphy_get_delay_reg_value(struct phy_device *phydev, u16 *rxc_dly_en, u32 dflt) { - struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; int tb_size_half = tb_size / 2; u32 val; int i; - if (of_property_read_u32(node, prop_name, &val)) + if (device_property_read_u32(dev, prop_name, &val)) goto err_dts_val; /* when rxc_dly_en is NULL, it is get the delay for tx, only half of @@ -974,7 +982,8 @@ static u32 yt8531_get_ldo_vol(struct phy_device *phydev) { u32 val; - val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + val = ytphy_read_ext(phydev, YT8521_CHIP_CONFIG_REG); + val = FIELD_GET(YT8531_RGMII_LDO_VOL_MASK, val); return val <= YT8531_LDO_VOL_1V8 ? val : YT8531_LDO_VOL_1V8; @@ -996,12 +1005,12 @@ static int yt8531_get_ds_map(struct phy_device *phydev, u32 cur) static int yt8531_set_ds(struct phy_device *phydev) { - struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; u32 ds_field_low, ds_field_hi, val; int ret, ds; /* set rgmii rx clk driver strength */ - if (!of_property_read_u32(node, "motorcomm,rx-clk-drv-microamp", &val)) { + if (!device_property_read_u32(dev, "motorcomm,rx-clk-drv-microamp", &val)) { ds = yt8531_get_ds_map(phydev, val); if (ds < 0) return dev_err_probe(&phydev->mdio.dev, ds, @@ -1010,15 +1019,16 @@ static int yt8531_set_ds(struct phy_device *phydev) ds = YT8531_RGMII_RX_DS_DEFAULT; } - ret = ytphy_modify_ext_with_lock(phydev, - YTPHY_PAD_DRIVE_STRENGTH_REG, - YT8531_RGMII_RXC_DS_MASK, - FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds)); + ret = ytphy_modify_ext(phydev, + YTPHY_PAD_DRIVE_STRENGTH_REG, + YT8531_RGMII_RXC_DS_MASK, + FIELD_PREP(YT8531_RGMII_RXC_DS_MASK, ds)); + if (ret < 0) return ret; /* set rgmii rx data driver strength */ - if (!of_property_read_u32(node, "motorcomm,rx-data-drv-microamp", &val)) { + if (!device_property_read_u32(dev, "motorcomm,rx-data-drv-microamp", &val)) { ds = yt8531_get_ds_map(phydev, val); if (ds < 0) return dev_err_probe(&phydev->mdio.dev, ds, @@ -1033,10 +1043,11 @@ static int yt8531_set_ds(struct phy_device *phydev) ds_field_low = FIELD_GET(GENMASK(1, 0), ds); ds_field_low = FIELD_PREP(YT8531_RGMII_RXD_DS_LOW_MASK, ds_field_low); - ret = ytphy_modify_ext_with_lock(phydev, - YTPHY_PAD_DRIVE_STRENGTH_REG, - YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK, - ds_field_low | ds_field_hi); + ret = ytphy_modify_ext(phydev, + YTPHY_PAD_DRIVE_STRENGTH_REG, + YT8531_RGMII_RXD_DS_LOW_MASK | YT8531_RGMII_RXD_DS_HI_MASK, + ds_field_low | ds_field_hi); + if (ret < 0) return ret; @@ -1051,7 +1062,6 @@ static int yt8531_set_ds(struct phy_device *phydev) */ static int yt8521_probe(struct phy_device *phydev) { - struct device_node *node = phydev->mdio.dev.of_node; struct device *dev = &phydev->mdio.dev; struct yt8521_priv *priv; int chip_config; @@ -1101,7 +1111,7 @@ static int yt8521_probe(struct phy_device *phydev) return ret; } - if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq)) + if (device_property_read_u32(dev, "motorcomm,clk-out-frequency-hz", &freq)) freq = YTPHY_DTS_OUTPUT_CLK_DIS; if (phydev->drv->phy_id == PHY_ID_YT8521) { @@ -1169,11 +1179,11 @@ static int yt8521_probe(struct phy_device *phydev) static int yt8531_probe(struct phy_device *phydev) { - struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; u16 mask, val; u32 freq; - if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq)) + if (device_property_read_u32(dev, "motorcomm,clk-out-frequency-hz", &freq)) freq = YTPHY_DTS_OUTPUT_CLK_DIS; switch (freq) { @@ -1665,7 +1675,7 @@ static int yt8521_resume(struct phy_device *phydev) */ static int yt8521_config_init(struct phy_device *phydev) { - struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; int old_page; int ret = 0; @@ -1680,7 +1690,7 @@ static int yt8521_config_init(struct phy_device *phydev) goto err_restore_page; } - if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) { + if (device_property_read_bool(dev, "motorcomm,auto-sleep-disabled")) { /* disable auto sleep */ ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, YT8521_ESC1R_SLEEP_SW, 0); @@ -1688,13 +1698,18 @@ static int yt8521_config_init(struct phy_device *phydev) goto err_restore_page; } - if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) { + if (device_property_read_bool(dev, "motorcomm,keep-pll-enabled")) { /* enable RXC clock when no wire plug */ ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG, YT8521_CGR_RX_CLK_EN, 0); if (ret < 0) goto err_restore_page; } + + if (phy_interface_is_rgmii(phydev) && + phydev_id_compare(phydev, PHY_ID_YT8531S)) + ret = yt8531_set_ds(phydev); + err_restore_page: return phy_restore_page(phydev, old_page, ret); } @@ -1801,14 +1816,14 @@ static int yt8521_led_hw_control_get(struct phy_device *phydev, u8 index, static int yt8531_config_init(struct phy_device *phydev) { - struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; int ret; ret = ytphy_rgmii_clk_delay_config_with_lock(phydev); if (ret < 0) return ret; - if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) { + if (device_property_read_bool(dev, "motorcomm,auto-sleep-disabled")) { /* disable auto sleep */ ret = ytphy_modify_ext_with_lock(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, @@ -1817,7 +1832,7 @@ static int yt8531_config_init(struct phy_device *phydev) return ret; } - if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) { + if (device_property_read_bool(dev, "motorcomm,keep-pll-enabled")) { /* enable RXC clock when no wire plug */ ret = ytphy_modify_ext_with_lock(phydev, YT8521_CLOCK_GATING_REG, @@ -1826,13 +1841,45 @@ static int yt8531_config_init(struct phy_device *phydev) return ret; } + phy_lock_mdio_bus(phydev); ret = yt8531_set_ds(phydev); + phy_unlock_mdio_bus(phydev); if (ret < 0) return ret; return 0; } +static int yt8522_config_init(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int ret, val; + + val = ytphy_read_ext_with_lock(phydev, YT8522_EXTENDED_COMBO_CTRL); + if (val < 0) + return val; + + if (val & YT8522_RMII_EN) { + val |= YT8522_RXDV_SEL; + ret = ytphy_write_ext_with_lock(phydev, + YT8522_EXTENDED_COMBO_CTRL, + val); + if (ret < 0) + return ret; + } + + if (device_property_read_bool(dev, "motorcomm,auto-sleep-disabled")) { + /* disable auto sleep */ + ret = ytphy_modify_ext_with_lock(phydev, + YT8522_EXTREG_SLEEP_CONTROL, + YT8522_EN_SLEEP_SW, 0); + if (ret < 0) + return ret; + } + + return 0; +} + /** * yt8531_link_change_notify() - Adjust the tx clock direction according to * the current speed and dts config. @@ -1844,7 +1891,7 @@ static int yt8531_config_init(struct phy_device *phydev) */ static void yt8531_link_change_notify(struct phy_device *phydev) { - struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; bool tx_clk_1000_inverted = false; bool tx_clk_100_inverted = false; bool tx_clk_10_inverted = false; @@ -1852,17 +1899,17 @@ static void yt8531_link_change_notify(struct phy_device *phydev) u16 val = 0; int ret; - if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled")) + if (device_property_read_bool(dev, "motorcomm,tx-clk-adj-enabled")) tx_clk_adj_enabled = true; if (!tx_clk_adj_enabled) return; - if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted")) + if (device_property_read_bool(dev, "motorcomm,tx-clk-10-inverted")) tx_clk_10_inverted = true; - if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted")) + if (device_property_read_bool(dev, "motorcomm,tx-clk-100-inverted")) tx_clk_100_inverted = true; - if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted")) + if (device_property_read_bool(dev, "motorcomm,tx-clk-1000-inverted")) tx_clk_1000_inverted = true; if (phydev->speed < 0) @@ -3042,6 +3089,14 @@ static struct phy_driver motorcomm_phy_drvs[] = { .led_hw_control_set = yt8521_led_hw_control_set, .led_hw_control_get = yt8521_led_hw_control_get, }, + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8522), + .name = "YT8522 100 Megabit Ethernet", + .config_aneg = genphy_config_aneg, + .config_init = yt8522_config_init, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531), .name = "YT8531 Gigabit Ethernet", @@ -3102,6 +3157,7 @@ MODULE_LICENSE("GPL"); static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8522) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8821) }, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 1511385b9b36..0615228459ef 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1938,6 +1938,9 @@ void phy_detach(struct phy_device *phydev) if (dev) { struct hwtstamp_provider *hwprov; + /* hwprov may technically be protected by ops lock but + * not for devices with a phydev, see phy_link_topo_add_phy() + */ hwprov = rtnl_dereference(dev->hwprov); /* Disable timestamp if it is the one selected */ if (hwprov && hwprov->phydev == phydev) { diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link_topology.c index 1f1eb5d59b38..4134de7ae313 100644 --- a/drivers/net/phy/phy_link_topology.c +++ b/drivers/net/phy/phy_link_topology.c @@ -10,6 +10,7 @@ #include #include #include +#include static int netdev_alloc_phy_link_topology(struct net_device *dev) { @@ -35,6 +36,15 @@ int phy_link_topo_add_phy(struct net_device *dev, struct phy_device_node *pdn; int ret; + /* ethtool ops may run without rtnl_lock, and rtnl_lock is what + * currently protects the PHY topology. No driver currently mixes + * the two, flag if someone tries. See also: + * - ethnl_req_get_phydev() + * - phy_detach() + */ + if (WARN_ON_ONCE(netdev_need_ops_lock(dev))) + return -EOPNOTSUPP; + if (!topo) { ret = netdev_alloc_phy_link_topology(dev); if (ret) diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c index 63726cf98cd4..ba4dc07752b6 100644 --- a/drivers/net/phy/qcom/at803x.c +++ b/drivers/net/phy/qcom/at803x.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1074,6 +1075,7 @@ static void ipq5018_link_change_notify(struct phy_device *phydev) static int ipq5018_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; + struct clk *rx_clk, *tx_clk; struct ipq5018_priv *priv; int ret; @@ -1084,6 +1086,16 @@ static int ipq5018_probe(struct phy_device *phydev) priv->set_short_cable_dac = of_property_read_bool(dev->of_node, "qcom,dac-preset-short-cable"); + rx_clk = devm_clk_get_enabled(dev, "rx"); + if (IS_ERR(rx_clk)) + return dev_err_probe(dev, PTR_ERR(rx_clk), + "failed to get and enable RX clock\n"); + + tx_clk = devm_clk_get_enabled(dev, "tx"); + if (IS_ERR(tx_clk)) + return dev_err_probe(dev, PTR_ERR(tx_clk), + "failed to get and enable TX clock\n"); + priv->rst = devm_reset_control_array_get_exclusive(dev); if (IS_ERR(priv->rst)) return dev_err_probe(dev, PTR_ERR(priv->rst), diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index 79c867ef64da..27268811f564 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -40,7 +40,12 @@ RTL8201F_ISR_LINK) #define RTL821x_INER 0x12 -#define RTL8211B_INER_INIT 0x6400 +#define RTL8211B_INER_SPEED BIT(14) +#define RTL8211B_INER_DUPLEX BIT(13) +#define RTL8211B_INER_LINK_STATUS BIT(10) +#define RTL8211B_INER_INIT (RTL8211B_INER_SPEED | \ + RTL8211B_INER_DUPLEX | \ + RTL8211B_INER_LINK_STATUS) #define RTL8211E_INER_LINK_STATUS BIT(10) #define RTL8211F_INER_PME BIT(7) #define RTL8211F_INER_LINK_STATUS BIT(4) @@ -165,6 +170,18 @@ #define RTL8221B_VND2_INSR 0xa4d4 +#define RTL822X_VND2_LED(x) (0xd032 + ((x) * 2)) +#define RTL822X_VND2_LCR_LINK_10 BIT(0) +#define RTL822X_VND2_LCR_LINK_100 BIT(1) +#define RTL822X_VND2_LCR_LINK_1000 BIT(2) +#define RTL822X_VND2_LCR_LINK_2500 BIT(5) + +#define RTL822X_VND2_LCR6 0xd040 +#define RTL822X_VND2_LED_ACT(x) BIT(x) + +#define RTL822X_VND2_LCR7 0xd044 +#define RTL822X_VND2_LED_POLAR(x) BIT(x) + #define RTL8224_MII_RTCT 0x11 #define RTL8224_MII_RTCT_ENABLE BIT(0) #define RTL8224_MII_RTCT_PAIR_A BIT(4) @@ -189,6 +206,21 @@ #define RTL8224_VND1_MDI_PAIR_SWAP 0xa90 #define RTL8224_VND1_MDI_POLARITY_SWAP 0xa94 +#define RTL8226_VND1_UNKNOWN_6A21 0x6a21 +#define RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN BIT(5) + +#define RTL8226_VND2_UNKNOWN_D068 0xd068 +#define RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG BIT(1) +#define RTL8226_VND2_UNKNOWN_D068_PAIR_SEL GENMASK(4, 3) +#define RTL8226_VND2_ADCCAL_OFFSET 0xd06a + +#define RTL8226_VND2_RG_LPF_CAP_XG_P0_P1 0xbd5a +#define RTL8226_VND2_RG_LPF_CAP_XG_P2_P3 0xbd5c +#define RTL8226_VND2_RG_LPF_CAP_P0_P1 0xbc18 +#define RTL8226_VND2_RG_LPF_CAP_P2_P3 0xbc1a +#define RTL8226_RG_LPF_CAP_PAIR_A_MASK GENMASK(4, 0) +#define RTL8226_RG_LPF_CAP_PAIR_B_MASK GENMASK(12, 8) + #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) @@ -1468,6 +1500,144 @@ static int rtl822xb_write_mmd(struct phy_device *phydev, int devnum, u16 reg, return write_ret; } +static int rtl8226_set_mdi_swap(struct phy_device *phydev, bool swap_enable) +{ + u16 val = swap_enable ? RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN : 0; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND1, RTL8226_VND1_UNKNOWN_6A21, + RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN, val); +} + +static int rtl8226_swap_rg_lpf_cap(struct phy_device *phydev, u32 reg_p0_p1, u32 reg_p2_p3) +{ + u16 val_p0, val_p1, val_p2, val_p3; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1); + if (ret < 0) + return ret; + + val_p0 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret); + val_p1 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret); + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3); + if (ret < 0) + return ret; + + val_p2 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret); + val_p3 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret); + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1, + RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK, + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p3) | + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p2)); + if (ret < 0) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3, + RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK, + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p1) | + FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p0)); +} + +static int rtl8226_patch_mdi_swap(struct phy_device *phydev, bool swap_enable) +{ + u16 adccal_offset[4]; + bool is_patched; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068); + if (ret < 0) + return ret; + + is_patched = !(ret & RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG); + + if (is_patched == swap_enable) { + /* Nothing to do */ + return 0; + } + + if (!swap_enable) { + /* Patching is only implemented one-way, see next comment. */ + phydev_err(phydev, "MDI swapping disabled, but PHY is already patched.\n"); + return -EINVAL; + } + + /* The exact meaning of these bits is unknown. We only know that bit 1 + * is used as a flag that swapping is already done. + */ + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, 0x7, 0x1); + if (ret < 0) + return ret; + + for (int i = 0; i < 4; i++) { + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, + RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, + FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i)); + if (ret < 0) + return ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET); + if (ret < 0) + return ret; + + adccal_offset[i] = ret; + } + + for (int i = 0; i < 4; i++) { + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, + RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, + FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i)); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET, + adccal_offset[3 - i]); + if (ret < 0) + return ret; + } + + ret = rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_XG_P0_P1, + RTL8226_VND2_RG_LPF_CAP_XG_P2_P3); + if (ret < 0) + return ret; + + return rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_P0_P1, + RTL8226_VND2_RG_LPF_CAP_P2_P3); +} + +static int rtl8226_config_mdi_order(struct phy_device *phydev) +{ + u32 order; + bool swap_enable; + int ret; + + ret = of_property_read_u32(phydev->mdio.dev.of_node, "enet-phy-pair-order", &order); + + /* Property not present, nothing to do */ + if (ret == -EINVAL || ret == -ENOSYS) + return 0; + + if (ret) + return ret; + + if (order & ~1) + return -EINVAL; + + swap_enable = !!(order & 1); + + ret = rtl8226_set_mdi_swap(phydev, swap_enable); + if (ret) + return ret; + + return rtl8226_patch_mdi_swap(phydev, swap_enable); +} + +static int rtl8226_probe(struct phy_device *phydev) +{ + return rtl8226_config_mdi_order(phydev); +} + static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) { bool has_2500, has_sgmii; @@ -1797,6 +1967,151 @@ static int rtl822xb_c45_read_status(struct phy_device *phydev) return 0; } +static int rtl822xb_led_brightness_set(struct phy_device *phydev, u8 index, + enum led_brightness value) +{ + int ret; + + if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + /* clear HW LED setup */ + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, + RTL822X_VND2_LED(index), 0); + if (ret < 0) + return ret; + + /* clear HW LED blink */ + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR6, + RTL822X_VND2_LED_ACT(index)); + if (ret < 0) + return ret; + + if (value != LED_OFF) + return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, + RTL822X_VND2_LCR7, + RTL822X_VND2_LED_POLAR(index)); + else + return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, + RTL822X_VND2_LCR7, + RTL822X_VND2_LED_POLAR(index)); +} + +static int rtl822xb_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + const unsigned long act_mask = BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX); + + const unsigned long link_mask = BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_1000) | + BIT(TRIGGER_NETDEV_LINK_2500); + + if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + /* Filter out any other unsupported triggers. */ + if (rules & ~(link_mask | act_mask)) + return -EOPNOTSUPP; + + /* RX and TX are not differentiated, they are not possible + * without combination with a link trigger. + */ + if ((rules & act_mask) && !(rules & link_mask)) + return -EOPNOTSUPP; + + return 0; +} + +static int rtl822xb_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + int val; + + if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LED(index)); + if (val < 0) + return val; + + if (val & RTL822X_VND2_LCR_LINK_10) + __set_bit(TRIGGER_NETDEV_LINK_10, rules); + + if (val & RTL822X_VND2_LCR_LINK_100) + __set_bit(TRIGGER_NETDEV_LINK_100, rules); + + if (val & RTL822X_VND2_LCR_LINK_1000) + __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + + if (val & RTL822X_VND2_LCR_LINK_2500) + __set_bit(TRIGGER_NETDEV_LINK_2500, rules); + + if ((val & RTL822X_VND2_LCR_LINK_10) && + (val & RTL822X_VND2_LCR_LINK_100) && + (val & RTL822X_VND2_LCR_LINK_1000) && + (val & RTL822X_VND2_LCR_LINK_2500)) + __set_bit(TRIGGER_NETDEV_LINK, rules); + + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR6); + if (val < 0) + return val; + + if (val & RTL822X_VND2_LED_ACT(index)) { + __set_bit(TRIGGER_NETDEV_RX, rules); + __set_bit(TRIGGER_NETDEV_TX, rules); + } + + return 0; +} + +static int rtl822xb_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 val = 0; + bool act; + int ret; + + if (index >= RTL8211x_LED_COUNT) + return -EINVAL; + + if (test_bit(TRIGGER_NETDEV_LINK, &rules) || + test_bit(TRIGGER_NETDEV_LINK_10, &rules)) + val |= RTL822X_VND2_LCR_LINK_10; + + if (test_bit(TRIGGER_NETDEV_LINK, &rules) || + test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + val |= RTL822X_VND2_LCR_LINK_100; + + if (test_bit(TRIGGER_NETDEV_LINK, &rules) || + test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + val |= RTL822X_VND2_LCR_LINK_1000; + + if (test_bit(TRIGGER_NETDEV_LINK, &rules) || + test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) + val |= RTL822X_VND2_LCR_LINK_2500; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, + RTL822X_VND2_LED(index), val); + if (ret < 0) + return ret; + + act = test_bit(TRIGGER_NETDEV_RX, &rules) || + test_bit(TRIGGER_NETDEV_TX, &rules); + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR6, + RTL822X_VND2_LED_ACT(index), act ? + RTL822X_VND2_LED_ACT(index) : 0); + if (ret < 0) + return ret; + + /* Reset polarity to default */ + return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_LCR7, + RTL822X_VND2_LED_POLAR(index)); +} + static int rtl8224_cable_test_start(struct phy_device *phydev) { u32 val; @@ -2522,6 +2837,7 @@ static struct phy_driver realtek_drvs[] = { .soft_reset = rtl822x_c45_soft_reset, .get_features = rtl822x_c45_get_features, .config_aneg = rtl822x_c45_config_aneg, + .probe = rtl8226_probe, .config_init = rtl822x_config_init, .inband_caps = rtl822x_inband_caps, .config_inband = rtl822x_config_inband, @@ -2565,6 +2881,10 @@ static struct phy_driver realtek_drvs[] = { .write_page = rtl821x_write_page, .read_mmd = rtl822xb_read_mmd, .write_mmd = rtl822xb_write_mmd, + .led_brightness_set = rtl822xb_led_brightness_set, + .led_hw_is_supported = rtl822xb_led_hw_is_supported, + .led_hw_control_get = rtl822xb_led_hw_control_get, + .led_hw_control_set = rtl822xb_led_hw_control_set, }, { .match_phy_device = rtl8221b_vm_cg_match_phy_device, .name = "RTL8221B-VM-CG 2.5Gbps PHY", @@ -2584,6 +2904,10 @@ static struct phy_driver realtek_drvs[] = { .write_page = rtl821x_write_page, .read_mmd = rtl822xb_read_mmd, .write_mmd = rtl822xb_write_mmd, + .led_brightness_set = rtl822xb_led_brightness_set, + .led_hw_is_supported = rtl822xb_led_hw_is_supported, + .led_hw_control_get = rtl822xb_led_hw_control_get, + .led_hw_control_set = rtl822xb_led_hw_control_set, }, { .match_phy_device = rtl8251b_c45_match_phy_device, .name = "RTL8251B 5Gbps PHY", diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index b94b9c433a21..03bfd8640db9 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "sfp.h" @@ -206,6 +207,16 @@ static const enum gpiod_flags gpio_flags[] = { #define T_PROBE_RETRY_SLOW msecs_to_jiffies(5000) #define R_PROBE_RETRY_SLOW 12 +/* Polling interval and consecutive-failure threshold for the I2C presence + * probe used on boards without a MOD_DEF0 GPIO (see sfp_i2c_get_state()). + * A single successful read asserts presence immediately; R_PROBE_ABSENT + * consecutive failures are required to declare a live module removed, to ride + * out a transient I2C error. Insertion is thus detected within + * T_PROBE_PRESENT and removal within T_PROBE_PRESENT * R_PROBE_ABSENT. + */ +#define T_PROBE_PRESENT msecs_to_jiffies(500) +#define R_PROBE_ABSENT 3 + /* SFP modules appear to always have their PHY configured for bus address * 0x56 (which with mdio-i2c, translates to a PHY address of 22). * RollBall SFPs access phy via SFP Enhanced Digital Diagnostic Interface @@ -249,6 +260,13 @@ struct sfp { bool need_poll; + /* I2C-probed presence, for boards without a MOD_DEF0 GPIO. + * Access rules: st_mutex held (updated from the poll/state machine). + */ + bool i2c_present; + u8 i2c_present_nak; + unsigned long i2c_present_next; + /* Access rules: * state_hw_drive: st_mutex held * state_hw_mask: st_mutex held @@ -579,10 +597,13 @@ static const struct sfp_quirk sfp_quirks[] = { // OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault), + SFP_QUIRK_F("OEM", "SFP-10G-T-I", sfp_fixup_rollball), SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc), SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g), SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex), SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex), + SFP_QUIRK_S("OEM", "SFP-2.5G-LH03-B", sfp_quirk_2500basex), + SFP_QUIRK_S("OEM", "SFP-2.5G-LH20-A", sfp_quirk_2500basex), SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc), SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc), SFP_QUIRK_F("Turris", "RTSFP-2.5G", sfp_fixup_rollball), @@ -756,50 +777,113 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, return ret == ARRAY_SIZE(msgs) ? len : 0; } -static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr, - void *buf, size_t len) +static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, + size_t len) { - union i2c_smbus_data smbus_data; + union i2c_smbus_data smbus_data = {0}; u8 bus_addr = a2 ? 0x51 : 0x50; + size_t this_len, transferred; + u32 functionality; u8 *data = buf; int ret; + functionality = i2c_get_functionality(sfp->i2c); + while (len) { - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, - I2C_SMBUS_READ, dev_addr, - I2C_SMBUS_BYTE_DATA, &smbus_data); - if (ret < 0) - return ret; + this_len = min(len, sfp->i2c_block_size); - *data = smbus_data.byte; + if (functionality & I2C_FUNC_SMBUS_READ_I2C_BLOCK) { + smbus_data.block[0] = this_len; + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); + if (ret < 0) + return ret; - len--; - data++; - dev_addr++; + transferred = min_t(size_t, smbus_data.block[0], this_len); + if (!transferred) + return -EIO; + + memcpy(data, &smbus_data.block[1], transferred); + } else if (this_len >= 2 && + (functionality & I2C_FUNC_SMBUS_READ_WORD_DATA)) { + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_WORD_DATA, &smbus_data); + if (ret < 0) + return ret; + + put_unaligned_le16(smbus_data.word, data); + transferred = 2; + } else { + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (ret < 0) + return ret; + + *data = smbus_data.byte; + transferred = 1; + } + + data += transferred; + len -= transferred; + dev_addr += transferred; } return data - (u8 *)buf; } -static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, - void *buf, size_t len) +static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, + size_t len) { union i2c_smbus_data smbus_data; u8 bus_addr = a2 ? 0x51 : 0x50; + size_t this_len, transferred; + u32 functionality; u8 *data = buf; int ret; - while (len) { - smbus_data.byte = *data; - ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, - I2C_SMBUS_WRITE, dev_addr, - I2C_SMBUS_BYTE_DATA, &smbus_data); - if (ret) - return ret; + functionality = i2c_get_functionality(sfp->i2c); - len--; - data++; - dev_addr++; + while (len) { + this_len = min(len, sfp->i2c_block_size); + + if (functionality & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) { + smbus_data.block[0] = this_len; + memcpy(&smbus_data.block[1], data, this_len); + + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); + if (ret < 0) + return ret; + + transferred = this_len; + } else if (this_len >= 2 && + (functionality & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { + smbus_data.word = get_unaligned_le16(data); + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_WORD_DATA, &smbus_data); + if (ret < 0) + return ret; + + transferred = 2; + } else { + smbus_data.byte = *data; + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (ret < 0) + return ret; + + transferred = 1; + } + + data += transferred; + len -= transferred; + dev_addr += transferred; } return data - (u8 *)buf; @@ -807,21 +891,48 @@ static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) { + size_t max_block_size; + sfp->i2c = i2c; if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { sfp->read = sfp_i2c_read; sfp->write = sfp_i2c_write; - sfp->i2c_max_block_size = SFP_EEPROM_BLOCK_SIZE; - } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { - sfp->read = sfp_smbus_byte_read; - sfp->write = sfp_smbus_byte_write; - sfp->i2c_max_block_size = 1; + max_block_size = SFP_EEPROM_BLOCK_SIZE; + } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA) || + i2c_check_functionality(i2c, I2C_FUNC_SMBUS_I2C_BLOCK)) { + /* Either protocol alone covers any length: I2C-block carries + * 1..32 bytes per xfer, byte iterates one byte at a time. + */ + sfp->read = sfp_smbus_read; + sfp->write = sfp_smbus_write; + + if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_I2C_BLOCK)) + max_block_size = SFP_EEPROM_BLOCK_SIZE; + else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_WORD_DATA)) + max_block_size = 2; + else + max_block_size = 1; + } else if (WARN_ONCE(i2c_check_functionality(i2c, I2C_FUNC_SMBUS_WORD_DATA), + "SMBus word-only adapter; odd-length transfers will fail\n")) { + /* Word-only: even-length xfers work; odd-length xfers fall + * to BYTE, which the adapter does not advertise and will + * likely fail. + */ + sfp->read = sfp_smbus_read; + sfp->write = sfp_smbus_write; + max_block_size = 2; } else { sfp->i2c = NULL; return -EINVAL; } + if (i2c->quirks && i2c->quirks->max_read_len) + max_block_size = min(max_block_size, i2c->quirks->max_read_len); + if (i2c->quirks && i2c->quirks->max_write_len) + max_block_size = min(max_block_size, i2c->quirks->max_write_len); + + sfp->i2c_max_block_size = max_block_size; sfp->i2c_block_size = sfp->i2c_max_block_size; return 0; } @@ -861,6 +972,45 @@ static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) return sfp->read(sfp, a2, addr, buf, len); } +/* Probe whether a module is physically present by attempting a single-byte + * I2C read of the EEPROM identifier (an empty cage NAKs). Used as the presence + * source on boards that do not wire MOD_DEF0 to a GPIO. + */ +static bool sfp_module_present_i2c(struct sfp *sfp) +{ + u8 id; + + return sfp_read(sfp, false, SFP_PHYS_ID, &id, sizeof(id)) == sizeof(id); +} + +/* get_state variant for boards without a MOD_DEF0 GPIO. Instead of assuming + * the module is always present, derive SFP_F_PRESENT from a throttled I2C + * probe so that hot-insertion and removal are detected. A single ACK asserts + * presence; R_PROBE_ABSENT consecutive failures clear it, to ride out a + * transient I2C error on a live module. + */ +static unsigned int sfp_i2c_get_state(struct sfp *sfp) +{ + unsigned int state = sfp_gpio_get_state(sfp); + + if (time_after_eq(jiffies, sfp->i2c_present_next)) { + if (sfp_module_present_i2c(sfp)) { + sfp->i2c_present = true; + sfp->i2c_present_nak = 0; + } else if (sfp->i2c_present && + ++sfp->i2c_present_nak >= R_PROBE_ABSENT) { + sfp->i2c_present = false; + sfp->i2c_present_nak = 0; + } + sfp->i2c_present_next = jiffies + T_PROBE_PRESENT; + } + + if (sfp->i2c_present) + state |= SFP_F_PRESENT; + + return state; +} + static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) { return sfp->write(sfp, a2, addr, buf, len); @@ -2023,10 +2173,17 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) static int sfp_sm_add_mdio_bus(struct sfp *sfp) { - if (sfp->mdio_protocol != MDIO_I2C_NONE) - return sfp_i2c_mdiobus_create(sfp); + int ret; - return 0; + if (sfp->mdio_protocol == MDIO_I2C_NONE) + return 0; + + ret = sfp_i2c_mdiobus_create(sfp); + if (ret == -ENODEV) { + sfp->mdio_protocol = MDIO_I2C_NONE; + return 0; + } + return ret; } /* Probe a SFP for a PHY device if the module supports copper - the PHY @@ -3159,9 +3316,30 @@ static int sfp_probe(struct platform_device *pdev) sfp->get_state = sfp_gpio_get_state; sfp->set_state = sfp_gpio_set_state; - /* Modules that have no detect signal are always present */ - if (!(sfp->gpio[GPIO_MODDEF0])) - sfp->get_state = sff_gpio_get_state; + /* An SFP cage with no MOD_DEF0 GPIO has no hardware presence signal. + * Assuming the module is always present traps an empty cage in + * MOD_ERROR and never detects hot-insertion, so derive presence from a + * throttled I2C probe and poll for changes instead. sfp_i2c_configure() + * has already set i2c_max_block_size; seed i2c_block_size so the + * presence read does not issue a zero-length transfer before the first + * EEPROM read. Seed i2c_present_next to jiffies so the first probe + * happens immediately (a zero value would be in the past relative to + * the negative INITIAL_JIFFIES at boot and delay detection). + * + * A soldered-down module (sff,sff) has no presence signal and is + * genuinely always present, so it keeps the always-present behaviour; + * the I2C probe is gated on the cage type advertising SFP_F_PRESENT. + */ + if (!sfp->gpio[GPIO_MODDEF0]) { + if (sff->gpios & SFP_F_PRESENT) { + sfp->get_state = sfp_i2c_get_state; + sfp->i2c_block_size = sfp->i2c_max_block_size; + sfp->i2c_present_next = jiffies; + sfp->need_poll = true; + } else { + sfp->get_state = sff_gpio_get_state; + } + } device_property_read_u32(&pdev->dev, "maximum-power-milliwatt", &sfp->max_power_mW); diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig index 753354b4e36c..c592648ebd41 100644 --- a/drivers/net/ppp/Kconfig +++ b/drivers/net/ppp/Kconfig @@ -110,8 +110,12 @@ config PPPOATM which can lead to bad results if the ATM peer loses state and changes its encapsulation unilaterally. +config PPPOX + tristate + config PPPOE tristate "PPP over Ethernet" + select PPPOX help Support for PPP over Ethernet. @@ -157,6 +161,7 @@ config PPPOE_HASH_BITS config PPTP tristate "PPP over IPv4 (PPTP)" depends on NET_IPGRE_DEMUX + select PPPOX help Support for PPP over IPv4.(Point-to-Point Tunneling Protocol) @@ -168,6 +173,7 @@ config PPTP config PPPOL2TP tristate "PPP over L2TP" depends on L2TP + select PPPOX help Support for PPP-over-L2TP socket family. L2TP is a protocol used by ISPs and enterprises to tunnel PPP traffic over UDP diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile index 16c457d6b324..a815e1497179 100644 --- a/drivers/net/ppp/Makefile +++ b/drivers/net/ppp/Makefile @@ -9,6 +9,6 @@ obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate.o obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o -obj-$(CONFIG_PPPOE) += pppox.o pppoe.o -obj-$(CONFIG_PPPOL2TP) += pppox.o -obj-$(CONFIG_PPTP) += pppox.o pptp.o +obj-$(CONFIG_PPPOX) += pppox.o +obj-$(CONFIG_PPPOE) += pppoe.o +obj-$(CONFIG_PPTP) += pptp.o diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index bdd61c504a1c..4a018acb5262 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -77,6 +77,7 @@ #include #include #include +#include #include @@ -136,15 +137,15 @@ static inline int cmp_addr(struct pppoe_addr *a, __be16 sid, char *addr) #error 8 must be a multiple of PPPOE_HASH_BITS #endif -static int hash_item(__be16 sid, unsigned char *addr) +static u8 hash_item(__be16 sid, const u8 addr[ETH_ALEN]) { - unsigned char hash = 0; + const u16 *addr16 = (const u16 *)addr; unsigned int i; + u16 hash16; + u8 hash; - for (i = 0; i < ETH_ALEN; i++) - hash ^= addr[i]; - for (i = 0; i < sizeof(sid_t) * 8; i += 8) - hash ^= (__force __u32)sid >> i; + hash16 = addr16[0] ^ addr16[1] ^ addr16[2] ^ (__force u16)sid; + hash = (hash16 >> 8) ^ hash16; for (i = 8; (i >>= 1) >= PPPOE_HASH_BITS;) hash ^= hash >> i; @@ -409,7 +410,7 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, if (ppp_skb_is_compressed_proto(skb)) goto drop; - if (pskb_trim_rcsum(skb, len)) + if (!skb_is_gso(skb) && pskb_trim_rcsum(skb, len)) goto drop; ph = pppoe_hdr(skb); @@ -1103,6 +1104,160 @@ static struct pernet_operations pppoe_net_ops = { .size = sizeof(struct pppoe_net), }; +static u16 +compare_pppoe_header(const struct pppoe_hdr *phdr, + const struct pppoe_hdr *phdr2) +{ + __be16 proto = *(const __be16 *)(phdr + 1); + __be16 proto2 = *(const __be16 *)(phdr2 + 1); + + return (__force u16)((phdr->sid ^ phdr2->sid) | (proto ^ proto2)); +} + +static __be16 pppoe_hdr_proto(const struct pppoe_hdr *phdr) +{ + __be16 proto = *(const __be16 *)(phdr + 1); + + switch (proto) { + case cpu_to_be16(PPP_IP): + return cpu_to_be16(ETH_P_IP); +#if IS_ENABLED(CONFIG_IPV6) + case cpu_to_be16(PPP_IPV6): + return cpu_to_be16(ETH_P_IPV6); +#endif + default: + return 0; + } +} + +static struct sk_buff *pppoe_gro_receive(struct list_head *head, + struct sk_buff *skb) +{ + const struct packet_offload *ptype; + unsigned int hlen, off_pppoe; + const struct pppoe_hdr *phdr; + struct sk_buff *pp = NULL; + struct sk_buff *p; + int flush = 1; + __be16 type; + + off_pppoe = skb_gro_offset(skb); + hlen = off_pppoe + PPPOE_SES_HLEN; + phdr = skb_gro_header(skb, hlen, off_pppoe); + if (unlikely(!phdr)) + goto out; + + /* filter for session packets (type:1, ver:1, code:0) */ + if (*(const __be16 *)phdr != cpu_to_be16(0x1100)) + goto out; + + /* ignore packets with padding or invalid length */ + if (skb_gro_len(skb) != be16_to_cpu(phdr->length) + sizeof(*phdr)) + goto out; + + type = pppoe_hdr_proto(phdr); + ptype = gro_find_receive_by_type(type); + if (!ptype) + goto out; + + flush = 0; + + list_for_each_entry(p, head, list) { + const struct pppoe_hdr *phdr2; + + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + phdr2 = (const struct pppoe_hdr *)(p->data + off_pppoe); + if (compare_pppoe_header(phdr, phdr2)) + NAPI_GRO_CB(p)->same_flow = 0; + } + + skb_gro_pull(skb, PPPOE_SES_HLEN); + skb_gro_postpull_rcsum(skb, phdr, PPPOE_SES_HLEN); + + pp = indirect_call_gro_receive_inet(ptype->callbacks.gro_receive, + ipv6_gro_receive, inet_gro_receive, + head, skb); + +out: + skb_gro_flush_final(skb, pp, flush); + + return pp; +} + +static int pppoe_gro_complete(struct sk_buff *skb, int nhoff) +{ + struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb->data + nhoff); + __be16 type = pppoe_hdr_proto(phdr); + struct packet_offload *ptype; + unsigned int len; + + ptype = gro_find_complete_by_type(type); + if (!ptype) + return -ENOENT; + + len = skb->len - (nhoff + sizeof(*phdr)); + len = min(len, 0xFFFFU); + phdr->length = cpu_to_be16(len); + + return INDIRECT_CALL_INET(ptype->callbacks.gro_complete, + ipv6_gro_complete, inet_gro_complete, + skb, nhoff + PPPOE_SES_HLEN); +} + +static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct packet_offload *ptype; + struct pppoe_hdr *phdr; + __be16 orig_type, type; + int len, nhoff; + + skb_reset_network_header(skb); + nhoff = skb_network_header(skb) - skb_mac_header(skb); + + if (unlikely(!pskb_may_pull(skb, PPPOE_SES_HLEN))) + goto out; + + phdr = (struct pppoe_hdr *)skb_network_header(skb); + type = pppoe_hdr_proto(phdr); + ptype = gro_find_complete_by_type(type); + if (!ptype) + goto out; + + orig_type = skb->protocol; + __skb_pull(skb, PPPOE_SES_HLEN); + features &= ~NETIF_F_GSO_SOFTWARE; + segs = ptype->callbacks.gso_segment(skb, features); + if (IS_ERR_OR_NULL(segs)) + goto out; + + skb = segs; + do { + phdr = (struct pppoe_hdr *)(skb_mac_header(skb) + nhoff); + len = skb->len - (nhoff + sizeof(*phdr)); + phdr->length = cpu_to_be16(len); + skb->network_header = (u8 *)phdr - skb->head; + skb->protocol = orig_type; + skb_reset_mac_len(skb); + } while ((skb = skb->next)); + +out: + return segs; +} + +static struct packet_offload pppoe_packet_offload __read_mostly = { + .type = cpu_to_be16(ETH_P_PPP_SES), + .priority = 20, + .callbacks = { + .gro_receive = pppoe_gro_receive, + .gro_complete = pppoe_gro_complete, + .gso_segment = pppoe_gso_segment, + }, +}; + static int __init pppoe_init(void) { int err; @@ -1119,6 +1274,8 @@ static int __init pppoe_init(void) if (err) goto out_unregister_pppoe_proto; + if (IS_ENABLED(CONFIG_INET)) + dev_add_offload(&pppoe_packet_offload); dev_add_pack(&pppoes_ptype); dev_add_pack(&pppoed_ptype); register_netdevice_notifier(&pppoe_notifier); @@ -1138,6 +1295,8 @@ static void __exit pppoe_exit(void) unregister_netdevice_notifier(&pppoe_notifier); dev_remove_pack(&pppoed_ptype); dev_remove_pack(&pppoes_ptype); + if (IS_ENABLED(CONFIG_INET)) + dev_remove_offload(&pppoe_packet_offload); unregister_pppox_proto(PX_PROTO_OE); proto_unregister(&pppoe_sk_proto); unregister_pernet_device(&pppoe_net_ops); diff --git a/drivers/net/pse-pd/pd692x0.c b/drivers/net/pse-pd/pd692x0.c index 4a3c852780f5..cb377d5ba7af 100644 --- a/drivers/net/pse-pd/pd692x0.c +++ b/drivers/net/pse-pd/pd692x0.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -1757,6 +1758,7 @@ static int pd692x0_i2c_probe(struct i2c_client *client) static const char * const regulators[] = { "vdd", "vdda" }; struct pd692x0_msg msg, buf = {0}, zero = {0}; struct device *dev = &client->dev; + struct gpio_desc *disable_ports; struct pd692x0_msg_ver ver; struct pd692x0_priv *priv; struct fw_upload *fwl; @@ -1780,6 +1782,11 @@ static int pd692x0_i2c_probe(struct i2c_client *client) priv->client = client; i2c_set_clientdata(client, priv); + disable_ports = devm_gpiod_get_optional(dev, "disable-ports", GPIOD_OUT_LOW); + if (IS_ERR(disable_ports)) + return dev_err_probe(&client->dev, PTR_ERR(disable_ports), + "Failed to get disable ports GPIO\n"); + ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf)); if (ret != sizeof(buf)) { dev_err(dev, "Failed to get device status\n"); @@ -1853,7 +1860,7 @@ static void pd692x0_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id pd692x0_id[] = { - { PD692X0_PSE_NAME }, + { .name = PD692X0_PSE_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, pd692x0_id); diff --git a/drivers/net/pse-pd/si3474.c b/drivers/net/pse-pd/si3474.c index aa07ffbce54d..1845b9c51cf7 100644 --- a/drivers/net/pse-pd/si3474.c +++ b/drivers/net/pse-pd/si3474.c @@ -550,8 +550,8 @@ static int si3474_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id si3474_id[] = { - { "si3474" }, - {} + { .name = "si3474" }, + { } }; MODULE_DEVICE_TABLE(i2c, si3474_id); diff --git a/drivers/net/pse-pd/tps23881.c b/drivers/net/pse-pd/tps23881.c index d40cb35a2547..49d6389da067 100644 --- a/drivers/net/pse-pd/tps23881.c +++ b/drivers/net/pse-pd/tps23881.c @@ -1528,8 +1528,8 @@ static int tps23881_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id tps23881_id[] = { - { "tps23881", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881] }, - { "tps23881b", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881B] }, + { .name = "tps23881", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881] }, + { .name = "tps23881b", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881B] }, { } }; MODULE_DEVICE_TABLE(i2c, tps23881_id); diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index f51388d50307..feaa75fbf8fc 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -1375,7 +1375,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev, list_add_tail_rcu(&port->list, &team->port_list); team_port_enable(team, port); netdev_compute_master_upper_features(dev, true); + netdev_lock_ops(port_dev); __team_port_change_port_added(port, !!netif_oper_up(port_dev)); + netdev_unlock_ops(port_dev); __team_options_change_check(team); netdev_info(dev, "Port device %s added\n", portname); @@ -3090,7 +3092,7 @@ static void __team_port_change_send(struct team_port *port, bool linkup) if (linkup) { struct ethtool_link_ksettings ecmd; - err = __ethtool_get_link_ksettings(port->dev, &ecmd); + err = netif_get_link_ksettings(port->dev, &ecmd); if (!err) { port->state.speed = ecmd.base.speed; port->state.duplex = ecmd.base.duplex; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index fed9dfdfcc3b..ffbe6f13fb1f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -145,6 +145,8 @@ struct tun_file { struct list_head next; struct tun_struct *detached; struct ptr_ring tx_ring; + /* Protected by tx_ring.consumer_lock */ + int cons_cnt; struct xdp_rxq_info xdp_rxq; }; @@ -588,8 +590,13 @@ static void __tun_detach(struct tun_file *tfile, bool clean) rcu_assign_pointer(tun->tfiles[index], tun->tfiles[tun->numqueues - 1]); ntfile = rtnl_dereference(tun->tfiles[index]); + spin_lock(&ntfile->tx_ring.consumer_lock); ntfile->queue_index = index; ntfile->xdp_rxq.queue_index = index; + ntfile->cons_cnt = 0; + if (__ptr_ring_empty(&ntfile->tx_ring)) + netif_wake_subqueue(tun->dev, index); + spin_unlock(&ntfile->tx_ring.consumer_lock); rcu_assign_pointer(tun->tfiles[tun->numqueues - 1], NULL); @@ -730,6 +737,9 @@ static int tun_attach(struct tun_struct *tun, struct file *file, goto out; } + spin_lock(&tfile->tx_ring.consumer_lock); + tfile->cons_cnt = 0; + spin_unlock(&tfile->tx_ring.consumer_lock); tfile->queue_index = tun->numqueues; tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN; @@ -1008,6 +1018,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) struct netdev_queue *queue; struct tun_file *tfile; int len = skb->len; + int ret; rcu_read_lock(); tfile = rcu_dereference(tun->tfiles[txq]); @@ -1062,13 +1073,33 @@ 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)) { + queue = netdev_get_tx_queue(dev, txq); + + spin_lock(&tfile->tx_ring.producer_lock); + ret = __ptr_ring_produce(&tfile->tx_ring, skb); + if (!qdisc_txq_has_no_queue(queue) && + __ptr_ring_check_produce(&tfile->tx_ring) == -ENOSPC) { + netif_tx_stop_queue(queue); + /* Paired with smp_mb() in __tun_wake_queue() */ + smp_mb__after_atomic(); + if (!__ptr_ring_check_produce(&tfile->tx_ring)) + netif_tx_wake_queue(queue); + } + spin_unlock(&tfile->tx_ring.producer_lock); + + if (ret) { + /* This should be a rare case if a qdisc is present, but + * can happen due to lltx. + * Since skb_tx_timestamp(), skb_orphan(), + * run_ebpf_filter() and pskb_trim() could have tinkered + * with the SKB, returning NETDEV_TX_BUSY is unsafe and + * we must drop instead. + */ drop_reason = SKB_DROP_REASON_FULL_RING; goto drop; } /* dev->lltx requires to do our own update of trans_start */ - queue = netdev_get_tx_queue(dev, txq); txq_trans_cond_update(queue); /* Notify and wake up reader process */ @@ -2116,13 +2147,46 @@ static ssize_t tun_put_user(struct tun_struct *tun, return total; } -static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err) +/* Callers must hold ring.consumer_lock */ +static void __tun_wake_queue(struct tun_struct *tun, + struct tun_file *tfile, int consumed) +{ + struct netdev_queue *txq = netdev_get_tx_queue(tun->dev, + tfile->queue_index); + + /* Paired with smp_mb__after_atomic() in tun_net_xmit() */ + smp_mb(); + if (netif_tx_queue_stopped(txq)) { + tfile->cons_cnt += consumed; + if (tfile->cons_cnt >= tfile->tx_ring.size / 2 || + __ptr_ring_empty(&tfile->tx_ring)) { + netif_tx_wake_queue(txq); + tfile->cons_cnt = 0; + } + } +} + +static void *tun_ring_consume(struct tun_struct *tun, struct tun_file *tfile) +{ + void *ptr; + + spin_lock(&tfile->tx_ring.consumer_lock); + ptr = __ptr_ring_consume(&tfile->tx_ring); + if (ptr) + __tun_wake_queue(tun, tfile, 1); + + spin_unlock(&tfile->tx_ring.consumer_lock); + return ptr; +} + +static void *tun_ring_recv(struct tun_struct *tun, struct tun_file *tfile, + int noblock, int *err) { DECLARE_WAITQUEUE(wait, current); void *ptr = NULL; int error = 0; - ptr = ptr_ring_consume(&tfile->tx_ring); + ptr = tun_ring_consume(tun, tfile); if (ptr) goto out; if (noblock) { @@ -2134,7 +2198,7 @@ static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err) while (1) { set_current_state(TASK_INTERRUPTIBLE); - ptr = ptr_ring_consume(&tfile->tx_ring); + ptr = tun_ring_consume(tun, tfile); if (ptr) break; if (signal_pending(current)) { @@ -2171,7 +2235,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, if (!ptr) { /* Read frames from ring */ - ptr = tun_ring_recv(tfile, noblock, &err); + ptr = tun_ring_recv(tun, tfile, noblock, &err); if (!ptr) return err; } @@ -3626,6 +3690,16 @@ static int tun_queue_resize(struct tun_struct *tun) dev->tx_queue_len, GFP_KERNEL, tun_ptr_free); + if (!ret) { + for (i = 0; i < tun->numqueues; i++) { + tfile = rtnl_dereference(tun->tfiles[i]); + spin_lock(&tfile->tx_ring.consumer_lock); + netif_wake_subqueue(tun->dev, tfile->queue_index); + tfile->cons_cnt = 0; + spin_unlock(&tfile->tx_ring.consumer_lock); + } + } + kfree(rings); return ret; } @@ -3734,6 +3808,29 @@ struct ptr_ring *tun_get_tx_ring(struct file *file) } EXPORT_SYMBOL_GPL(tun_get_tx_ring); +/* Callers must hold ring.consumer_lock */ +void tun_wake_queue(struct file *file, int consumed) +{ + struct tun_file *tfile; + struct tun_struct *tun; + + if (file->f_op != &tun_fops) + return; + + tfile = file->private_data; + if (!tfile) + return; + + rcu_read_lock(); + + tun = rcu_dereference(tfile->tun); + if (tun) + __tun_wake_queue(tun, tfile, consumed); + + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(tun_wake_queue); + module_init(tun_init); module_exit(tun_cleanup); MODULE_DESCRIPTION(DRV_DESCRIPTION); diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index db85f40734d7..8700eeb8e22d 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1328,14 +1328,18 @@ static void __init parse_id(char *id) unsigned int vendor_id = 0, device_id = 0, flags = 0, i = 0; char *token, *name = NULL; - if ((token = strsep(&id, ":")) != NULL) + token = strsep(&id, ":"); + if (token) name = token; /* name now points to a null terminated string*/ - if ((token = strsep(&id, ":")) != NULL) - vendor_id = simple_strtoul(token, NULL, 16); - if ((token = strsep(&id, ":")) != NULL) - device_id = simple_strtoul(token, NULL, 16); - flags = simple_strtoul(id, NULL, 16); + token = strsep(&id, ":"); + if (token && kstrtouint(token, 16, &vendor_id)) + return; + token = strsep(&id, ":"); + if (token && kstrtouint(token, 16, &device_id)) + return; + if (id && *id && kstrtouint(id, 16, &flags)) + return; pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n", driver_name, name, vendor_id, device_id, flags); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 05acac10cd2b..94cdb61dca83 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1452,6 +1452,8 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x1546, 0x1342, 4)}, /* u-blox LARA-L6 */ {QMI_QUIRK_SET_DTR(0x33f8, 0x0104, 4)}, /* Rolling RW101 RMNET */ {QMI_FIXED_INTF(0x2dee, 0x4d22, 5)}, /* MeiG Smart SRM825L */ + {QMI_QUIRK_SET_DTR(0x2dee, 0x4d63, 0)}, /* MeiG SRM813Q w/ Modem(PPP) */ + {QMI_QUIRK_SET_DTR(0x2dee, 0x4d64, 0)}, /* MeiG SRM813Q w/ NMEA */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index b1268553cd70..f61686433031 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -621,6 +621,7 @@ enum spd_duplex { FORCE_1000M_FULL, NWAY_2500M_FULL, NWAY_5000M_FULL, + NWAY_10000M_FULL, }; /* OCP_ALDPS_CONFIG */ @@ -742,6 +743,7 @@ enum spd_duplex { #define BP4_SUPER_ONLY 0x1578 /* RTL_VER_04 only */ enum rtl_register_content { + _10000bps = BIT(14), _5000bps = BIT(12), _2500bps = BIT(10), _1250bps = BIT(9), @@ -757,6 +759,8 @@ enum rtl_register_content { #define is_speed_2500(_speed) (((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS)) #define is_speed_5000(_speed) (((_speed) & (_5000bps | LINK_STATUS)) == (_5000bps | LINK_STATUS)) +#define is_speed_10000(_speed) (((_speed) & (_10000bps | LINK_STATUS)) \ + == (_10000bps | LINK_STATUS)) #define is_flow_control(_speed) (((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow)) #define RTL8152_MAX_TX 4 @@ -1008,6 +1012,7 @@ struct r8152 { u32 support_2500full:1; u32 support_5000full:1; + u32 support_10000full:1; u32 lenovo_macpassthru:1; u32 dell_tb_rx_agg_bug:1; u16 ocp_base; @@ -1242,6 +1247,7 @@ enum rtl_version { RTL_VER_14, RTL_VER_15, RTL_VER_16, + RTL_VER_17, RTL_VER_MAX }; @@ -1260,6 +1266,7 @@ enum tx_csum_stat { #define RTL_ADVERTISED_1000_FULL BIT(5) #define RTL_ADVERTISED_2500_FULL BIT(6) #define RTL_ADVERTISED_5000_FULL BIT(7) +#define RTL_ADVERTISED_10000_FULL BIT(8) /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). * The RTL chips use a 64 element hash table based on the Ethernet CRC. @@ -3426,6 +3433,7 @@ static void rtl8152_nic_reset(struct r8152 *tp) break; case RTL_VER_16: + case RTL_VER_17: ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_RE | CR_TE); break; @@ -3465,6 +3473,9 @@ static void rtl_eee_plus_en(struct r8152 *tp, bool enable) static void rtl_set_eee_plus(struct r8152 *tp) { + if (tp->version == RTL_VER_17) + return rtl_eee_plus_en(tp, false); + if (rtl8152_get_speed(tp) & _10bps) rtl_eee_plus_en(tp, true); else @@ -3650,6 +3661,7 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp) case RTL_VER_13: case RTL_VER_15: case RTL_VER_16: + case RTL_VER_17: ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, 640 / 8); ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR, @@ -3694,6 +3706,7 @@ static void r8153_set_rx_early_size(struct r8152 *tp) ocp_data / 8); break; case RTL_VER_16: + case RTL_VER_17: ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data / 16); break; @@ -4542,6 +4555,7 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type) break; case RTL_VER_14: case RTL_VER_16: + case RTL_VER_17: default: ocp_write_word(tp, type, USB_BP2_EN, 0); bp_num = 16; @@ -4649,10 +4663,11 @@ static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_ case RTL_VER_11: case RTL_VER_12: case RTL_VER_14: - case RTL_VER_16: goto out; case RTL_VER_13: case RTL_VER_15: + case RTL_VER_16: + case RTL_VER_17: default: break; } @@ -5812,6 +5827,7 @@ static void rtl_eee_enable(struct r8152 *tp, bool enable) case RTL_VER_13: case RTL_VER_15: case RTL_VER_16: + case RTL_VER_17: if (enable) { r8156_eee_en(tp, true); ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv); @@ -6402,7 +6418,7 @@ static int rtl8156_enable(struct r8152 *tp) set_tx_qlen(tp); rtl_set_eee_plus(tp); - if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_16) + if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_17) ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, RX_AGGR_NUM_MASK); r8153_set_rx_early_timeout(tp); @@ -6513,6 +6529,9 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex, if (tp->support_5000full) support |= RTL_ADVERTISED_5000_FULL; + + if (tp->support_10000full) + support |= RTL_ADVERTISED_10000_FULL; } advertising &= support; @@ -6559,9 +6578,10 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex, r8152_mdio_write(tp, MII_CTRL1000, new1); } - if (tp->support_2500full || tp->support_5000full) { + if (tp->support_2500full || tp->support_5000full || tp->support_10000full) { orig = ocp_reg_read(tp, OCP_10GBT_CTRL); - new1 = orig & ~(MDIO_AN_10GBT_CTRL_ADV2_5G | MDIO_AN_10GBT_CTRL_ADV5G); + new1 = orig & ~(MDIO_AN_10GBT_CTRL_ADV2_5G | MDIO_AN_10GBT_CTRL_ADV5G + | MDIO_AN_10GBT_CTRL_ADV10G); if (advertising & RTL_ADVERTISED_2500_FULL) { new1 |= MDIO_AN_10GBT_CTRL_ADV2_5G; @@ -6573,6 +6593,11 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex, tp->ups_info.speed_duplex = NWAY_5000M_FULL; } + if (advertising & RTL_ADVERTISED_10000_FULL) { + new1 |= MDIO_AN_10GBT_CTRL_ADV10G; + tp->ups_info.speed_duplex = NWAY_10000M_FULL; + } + if (orig != new1) ocp_reg_write(tp, OCP_10GBT_CTRL, new1); } @@ -6802,7 +6827,7 @@ static void rtl8156_up(struct r8152 *tp) return; r8153b_u1u2en(tp, false); - if (tp->version != RTL_VER_16) + if (tp->version < RTL_VER_16) r8153_u2p3en(tp, false); r8153_aldps_en(tp, false); @@ -6816,7 +6841,7 @@ static void rtl8156_up(struct r8152 *tp) ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, NOW_IS_OOB); - if (tp->version == RTL_VER_16) + if (tp->version >= RTL_VER_16) ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR1, BIT(3)); ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, MCU_BORW_EN); @@ -6839,12 +6864,9 @@ static void rtl8156_up(struct r8152 *tp) ocp_word_w0w1(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, RXFIFO_FULL_MASK, 0x08); - ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, - PLA_MCU_SPDWN_EN); - ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN); - if (tp->version != RTL_VER_16) + if (tp->version < RTL_VER_16) ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_SPEED_OPTION, RG_PWRDN_EN | ALL_SPEED_OFF); @@ -6856,10 +6878,10 @@ static void rtl8156_up(struct r8152 *tp) } r8153_aldps_en(tp, true); - if (tp->version != RTL_VER_16) + if (tp->version < RTL_VER_16) r8153_u2p3en(tp, true); - if (tp->version != RTL_VER_16 && tp->udev->speed >= USB_SPEED_SUPER) + if (tp->version < RTL_VER_16 && tp->udev->speed >= USB_SPEED_SUPER) r8153b_u1u2en(tp, true); } @@ -6874,7 +6896,7 @@ static void rtl8156_down(struct r8152 *tp) PLA_MCU_SPDWN_EN); r8153b_u1u2en(tp, false); - if (tp->version != RTL_VER_16) { + if (tp->version < RTL_VER_16) { r8153_u2p3en(tp, false); r8153b_power_cut_en(tp, false); } @@ -7952,43 +7974,33 @@ static void r8156b_hw_phy_cfg(struct r8152 *tp) static void r8157_hw_phy_cfg(struct r8152 *tp) { - u32 ocp_data; u16 data; - int ret; r8156b_wait_loading_flash(tp); - ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); - if (ocp_data & PCUT_STATUS) { - ocp_data &= ~PCUT_STATUS; - ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); - } + ocp_word_test_and_clr_bits(tp, MCU_TYPE_USB, USB_MISC_0, PCUT_STATUS); data = r8153_phy_status(tp, 0); switch (data) { case PHY_STAT_EXT_INIT: + rtl8152_apply_firmware(tp, true); ocp_reg_clr_bits(tp, 0xa466, BIT(0)); ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1)); break; case PHY_STAT_LAN_ON: case PHY_STAT_PWRDN: default: + rtl8152_apply_firmware(tp, false); break; } - data = r8152_mdio_read(tp, MII_BMCR); - if (data & BMCR_PDOWN) { - data &= ~BMCR_PDOWN; - r8152_mdio_write(tp, MII_BMCR, data); - } + r8152_mdio_test_and_clr_bit(tp, MII_BMCR, BMCR_PDOWN); r8153_aldps_en(tp, false); rtl_eee_enable(tp, false); - ret = r8153_phy_status(tp, PHY_STAT_LAN_ON); - if (ret < 0) - return; - WARN_ON_ONCE(ret != PHY_STAT_LAN_ON); + data = r8153_phy_status(tp, PHY_STAT_LAN_ON); + WARN_ON_ONCE(data != PHY_STAT_LAN_ON); /* PFM mode */ ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_PHY_PWR, PFM_PWM_SWITCH); @@ -7996,7 +8008,7 @@ static void r8157_hw_phy_cfg(struct r8152 *tp) /* Advanced Power Saving parameter */ ocp_reg_set_bits(tp, 0xa430, BIT(0) | BIT(1)); - /* aldpsce force mode */ + /* Disable ALDPS force mode */ ocp_reg_clr_bits(tp, 0xa44a, BIT(2)); switch (tp->version) { @@ -8120,6 +8132,190 @@ static void r8157_hw_phy_cfg(struct r8152 *tp) sram2_write_w0w1(tp, 0x807c, 0xff00, 0x5000); sram2_write_w0w1(tp, 0x809d, 0xff00, 0x5000); break; + + case RTL_VER_17: + /* Disable bypass turn off clk in ALDPS */ + ocp_byte_clr_bits(tp, MCU_TYPE_PLA, 0xd3c8, BIT(0)); + + /* Power level tuning + * test mode power level + */ + sram_write_w0w1(tp, 0x8415, 0xff00, 0x9300); + /* normal link power level 10G, 5G, 2.5G */ + sram_write_w0w1(tp, 0x81a3, 0xff00, 0x0f00); + sram_write_w0w1(tp, 0x81ae, 0xff00, 0x0f00); + sram_write_w0w1(tp, 0x81b9, 0xff00, 0xb900); + /* normal link TX filter */ + sram2_write_w0w1(tp, 0x83b0, 0x0e00, 0); + sram2_write_w0w1(tp, 0x83c5, 0x0e00, 0); + sram2_write_w0w1(tp, 0x83da, 0x0e00, 0); + sram2_write_w0w1(tp, 0x83ef, 0x0e00, 0); + + /* AFE power saving for 2.5G & 5G */ + sram_write(tp, 0x8173, 0x8620); + sram_write(tp, 0x8175, 0x8671); + + sram_write_w0w1(tp, 0x817c, 0, BIT(13)); + sram_write_w0w1(tp, 0x8187, 0, BIT(13)); + sram_write_w0w1(tp, 0x8192, 0, BIT(13)); + sram_write_w0w1(tp, 0x819d, 0, BIT(13)); + sram_write_w0w1(tp, 0x81a8, BIT(13), 0); + sram_write_w0w1(tp, 0x81b3, BIT(13), 0); + sram_write_w0w1(tp, 0x81be, 0, BIT(13)); + + sram_write_w0w1(tp, 0x817d, 0xff00, 0xa600); + sram_write_w0w1(tp, 0x8188, 0xff00, 0xa600); + sram_write_w0w1(tp, 0x8193, 0xff00, 0xa600); + sram_write_w0w1(tp, 0x819e, 0xff00, 0xa600); + sram_write_w0w1(tp, 0x81a9, 0xff00, 0x1400); + sram_write_w0w1(tp, 0x81b4, 0xff00, 0x1400); + sram_write_w0w1(tp, 0x81bf, 0xff00, 0xa600); + + /* RFI parameter + * disable preset FBE + */ + ocp_reg_clr_bits(tp, 0xaeaa, BIT(5) | BIT(3)); + /* modify PGA for 5G&10G */ + sram2_write(tp, 0x84f0, 0x201c); + sram2_write(tp, 0x84f2, 0x3117); + /* RFI parameter */ + ocp_reg_write(tp, 0xaec6, 0x0000); + ocp_reg_write(tp, 0xae20, 0xffff); + ocp_reg_write(tp, 0xaece, 0xffff); + ocp_reg_write(tp, 0xaed2, 0xffff); + ocp_reg_write(tp, 0xaec8, 0x0000); + ocp_reg_clr_bits(tp, 0xaed0, BIT(0)); + ocp_reg_write(tp, 0xadb8, 0x0150); + sram2_write_w0w1(tp, 0x8197, 0xff00, 0x5000); + sram2_write_w0w1(tp, 0x8231, 0xff00, 0x5000); + sram2_write_w0w1(tp, 0x82cb, 0xff00, 0x5000); + sram2_write_w0w1(tp, 0x82cd, 0xff00, 0x5700); + sram2_write_w0w1(tp, 0x8233, 0xff00, 0x5700); + sram2_write_w0w1(tp, 0x8199, 0xff00, 0x5700); + + sram2_write(tp, 0x815a, 0x0150); + sram2_write(tp, 0x81f4, 0x0150); + sram2_write(tp, 0x828e, 0x0150); + sram2_write(tp, 0x81b1, 0x0000); + sram2_write(tp, 0x824b, 0x0000); + sram2_write(tp, 0x82e5, 0x0000); + + sram2_write_w0w1(tp, 0x84f7, 0xff00, 0x2800); + ocp_reg_set_bits(tp, 0xaec2, BIT(12)); + sram2_write_w0w1(tp, 0x81b3, 0xff00, 0xad00); + sram2_write_w0w1(tp, 0x824d, 0xff00, 0xad00); + sram2_write_w0w1(tp, 0x82e7, 0xff00, 0xad00); + ocp_reg_w0w1(tp, 0xae4e, 0x000f, 0x0001); + sram2_write_w0w1(tp, 0x82ce, 0xf000, 0x4000); + + /* 5G shift sel, default = '04' + * 10G shift sel, default = '03' + */ + sram2_write_w0w1(tp, 0x83a5, 0xff00, 0x0400); + sram2_write_w0w1(tp, 0x83a6, 0xff00, 0x0400); + sram2_write_w0w1(tp, 0x83a7, 0xff00, 0x0400); + sram2_write_w0w1(tp, 0x83a8, 0xff00, 0x0400); + + /* XG INRX parameters + * RC coefficients + */ + sram2_write(tp, 0x84ac, 0x0000); + sram2_write(tp, 0x84ae, 0x0000); + sram2_write(tp, 0x84b0, 0xf818); + sram2_write_w0w1(tp, 0x84b2, 0xff00, 0x6000); + /* Training AAGC PAR (with uc2 patch) */ + sram2_write(tp, 0x8ffc, 0x6008); + sram2_write(tp, 0x8ffe, 0xf450); + /* DAC BGK */ + sram2_write_w0w1(tp, 0x8015, 0, BIT(9)); + sram2_write_w0w1(tp, 0x8016, 0, BIT(11)); + sram2_write_w0w1(tp, 0x8fe6, 0xff00, 0x0800); + sram2_write(tp, 0x8fe4, 0x2114); + /* 10G PBO table */ + sram2_write(tp, 0x8647, 0xa7b1); + sram2_write(tp, 0x8649, 0xbbca); + sram2_write_w0w1(tp, 0x864b, 0xff00, 0xdc00); + /* 2.5G ado power window size */ + sram2_write_w0w1(tp, 0x8154, 0xc000, 0x4000); + sram2_write_w0w1(tp, 0x8158, 0xc000, 0); + /* 10G lock far */ + sram2_write(tp, 0x826c, 0xffff); + sram2_write(tp, 0x826e, 0xffff); + /* XG INRX parameter */ + sram2_write_w0w1(tp, 0x8872, 0xff00, 0x0e00); + sram_write_w0w1(tp, 0x8012, 0, BIT(11)); + sram_write_w0w1(tp, 0x8012, 0, BIT(14)); + ocp_reg_set_bits(tp, 0xb576, BIT(0)); + sram_write_w0w1(tp, 0x834a, 0xff00, 0x0700); + sram2_write_w0w1(tp, 0x8217, 0x3f00, 0x2a00); + sram_write_w0w1(tp, 0x81b1, 0xff00, 0x0b00); + sram2_write_w0w1(tp, 0x8fed, 0xff00, 0x4e00); + /* Slave about EC mu of datamode AAGC and DAC BG */ + sram2_write_w0w1(tp, 0x88ac, 0xff00, 0x2300); + /* improve UBE */ + ocp_reg_set_bits(tp, 0xbf0c, 0x7 << 11); + /* close Sparse NEC, improve connect 5EUU cable performance */ + sram2_write_w0w1(tp, 0x88de, 0xff00, 0); + /* 5G slave compatibility issue */ + sram2_write(tp, 0x80b4, 0x5195); + + /* XG Test Mode + * xgtstm_map_tbl for mdi_cap_sel + */ + sram_write(tp, 0x8370, 0x8671); + sram_write(tp, 0x8372, 0x86c8); + /* xgtstm_amp_map_tbl for REG_IBX_UP_SHIFT_L */ + sram_write(tp, 0x8401, 0x86c8); + sram_write(tp, 0x8403, 0x86da); + sram_write_w0w1(tp, 0x8406, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x8408, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x840a, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x840c, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x840e, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x8410, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x8412, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x8414, 0x1800, 0x1000); + sram_write_w0w1(tp, 0x8416, 0x1800, 0x1000); + + /* Cable Test Patch */ + sram_write(tp, 0x82bd, 0x1f40); + + /* Thermal sensor parameters */ + ocp_reg_w0w1(tp, 0xbfb4, 0x07ff, 0x0328); + ocp_reg_write(tp, 0xbfb6, 0x3e14); + + /* spdchg_gtx_shape_100M */ + ocp_reg_write(tp, OCP_SRAM_ADDR, 0x81c4); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x003b); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x0086); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00b7); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00db); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x00c3); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x0078); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x0047); + ocp_reg_write(tp, OCP_SRAM_DATA, 0x0023); + + /* lsbmsk_parameters + * RL6961_lsbmsk_parameter_250207 + */ + sram2_write(tp, 0x88d7, 0x01a0); + sram2_write(tp, 0x88d9, 0x01a0); + sram2_write(tp, 0x8ffa, 0x002a); + + sram2_write(tp, 0x8fee, 0xffdf); + sram2_write(tp, 0x8ff0, 0xffff); + sram2_write(tp, 0x8ff2, 0x0a4a); + sram2_write(tp, 0x8ff4, 0xaa5a); + sram2_write(tp, 0x8ff6, 0x0a4a); + sram2_write(tp, 0x8ff8, 0xaa5a); + + sram2_write_w0w1(tp, 0x88d5, 0xff00, 0x0200); + break; + default: break; } @@ -8155,6 +8351,18 @@ static void r8157_hw_phy_cfg(struct r8152 *tp) set_bit(PHY_RESET, &tp->flags); } +static int r8159_wait_backup_restore(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); + if (!(ocp_data & PCUT_STATUS)) + return 0; + + return poll_timeout_us(ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL), + ocp_data & BACKUP_RESTRORE, 200, 20000, false); +} + static void r8156_init(struct r8152 *tp) { u32 ocp_data; @@ -8164,14 +8372,14 @@ static void r8156_init(struct r8152 *tp) if (test_bit(RTL8152_INACCESSIBLE, &tp->flags)) return; - if (tp->version == RTL_VER_16) { + if (tp->version == RTL_VER_16 || tp->version == RTL_VER_17) { ocp_byte_set_bits(tp, MCU_TYPE_USB, 0xcffe, BIT(3)); ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xd3ca, BIT(0)); } ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_ECM_OP, EN_ALL_SPEED); - if (tp->version != RTL_VER_16) + if (tp->version < RTL_VER_16) ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0); ocp_word_set_bits(tp, MCU_TYPE_USB, USB_ECM_OPTION, BYPASS_MAC_RESET); @@ -8185,6 +8393,7 @@ static void r8156_init(struct r8152 *tp) case RTL_VER_13: case RTL_VER_15: case RTL_VER_16: + case RTL_VER_17: r8156b_wait_loading_flash(tp); break; default: @@ -8201,6 +8410,12 @@ static void r8156_init(struct r8152 *tp) return; } + if (tp->version == RTL_VER_17 && r8159_wait_backup_restore(tp)) { + rtl_set_inaccessible(tp); + dev_err(&tp->intf->dev, "init failed, backup-restore timed out\n"); + return; + } + data = r8153_phy_status(tp, 0); if (data == PHY_STAT_EXT_INIT) { ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1)); @@ -8216,7 +8431,7 @@ static void r8156_init(struct r8152 *tp) data = r8153_phy_status(tp, PHY_STAT_LAN_ON); - if (tp->version == RTL_VER_16) + if (tp->version >= RTL_VER_16) r8157_u2p3en(tp, false); else r8153_u2p3en(tp, false); @@ -8227,7 +8442,7 @@ static void r8156_init(struct r8152 *tp) /* U1/U2/L1 idle timer. 500 us */ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); - if (tp->version == RTL_VER_16) + if (tp->version >= RTL_VER_16) r8157_power_cut_en(tp, false); else r8153b_power_cut_en(tp, false); @@ -8260,7 +8475,7 @@ static void r8156_init(struct r8152 *tp) r8156_mac_clk_spd(tp, true); - if (tp->version != RTL_VER_16) + if (tp->version < RTL_VER_16) ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS); @@ -8273,8 +8488,13 @@ static void r8156_init(struct r8152 *tp) set_bit(GREEN_ETHERNET, &tp->flags); - /* rx aggregation / 16 bytes Rx descriptor */ - if (tp->version == RTL_VER_16) + /* RX aggregation / 16 bytes RX descriptor + * BIT(11) is specific to RTL8159, with unknown meaning + */ + if (tp->version == RTL_VER_17) + ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, + RX_AGG_DISABLE | RX_DESC_16B | BIT(11)); + else if (tp->version == RTL_VER_16) ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_DESC_16B); else ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_ZERO_EN); @@ -8282,7 +8502,7 @@ static void r8156_init(struct r8152 *tp) if (tp->version < RTL_VER_12) ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ACT_ODMA); - if (tp->version == RTL_VER_16) { + if (tp->version >= RTL_VER_16) { /* Disable Rx Zero Len */ rtl_bmu_clr_bits(tp, 0x2300, BIT(3)); /* TX descriptor Signature */ @@ -8723,7 +8943,10 @@ int rtl8152_get_link_ksettings(struct net_device *netdev, linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, cmd->link_modes.supported, tp->support_5000full); - if (tp->support_2500full || tp->support_5000full) { + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + cmd->link_modes.supported, tp->support_10000full); + + if (tp->support_2500full || tp->support_5000full || tp->support_10000full) { u16 ocp_10gbt_ctrl = ocp_reg_read(tp, OCP_10GBT_CTRL); u16 ocp_10gbt_stat = ocp_reg_read(tp, OCP_10GBT_STAT); @@ -8752,6 +8975,19 @@ int rtl8152_get_link_ksettings(struct net_device *netdev, if (is_speed_5000(rtl8152_get_speed(tp))) cmd->base.speed = SPEED_5000; } + + if (tp->support_10000full) { + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + cmd->link_modes.advertising, + ocp_10gbt_ctrl & MDIO_AN_10GBT_CTRL_ADV10G); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + cmd->link_modes.lp_advertising, + ocp_10gbt_stat & MDIO_AN_10GBT_STAT_LP10G); + + if (is_speed_10000(rtl8152_get_speed(tp))) + cmd->base.speed = SPEED_10000; + } } mutex_unlock(&tp->control); @@ -8805,6 +9041,10 @@ static int rtl8152_set_link_ksettings(struct net_device *dev, cmd->link_modes.advertising)) advertising |= RTL_ADVERTISED_5000_FULL; + if (test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + cmd->link_modes.advertising)) + advertising |= RTL_ADVERTISED_10000_FULL; + mutex_lock(&tp->control); ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed, @@ -8968,6 +9208,13 @@ static int r8153_get_eee(struct r8152 *tp, struct ethtool_keee *eee) linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, common); } + if (tp->support_10000full) { + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, eee->supported); + + if (speed & _10000bps) + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, common); + } + eee->eee_enabled = tp->eee_en; if (speed & _1000bps) @@ -9643,6 +9890,29 @@ static int rtl_ops_init(struct r8152 *tp) r8157_desc_init(tp); break; + case RTL_VER_17: + tp->eee_en = true; + tp->eee_adv = MDIO_EEE_100TX | MDIO_EEE_1000T | MDIO_EEE_10GT; + tp->eee_adv2 = MDIO_EEE_2_5GT | MDIO_EEE_5GT; + ops->init = r8156_init; + ops->enable = rtl8156_enable; + ops->disable = rtl8153_disable; + ops->up = rtl8156_up; + ops->down = rtl8156_down; + ops->unload = rtl8153_unload; + ops->eee_get = r8153_get_eee; + ops->eee_set = r8152_set_eee; + ops->in_nway = rtl8153_in_nway; + ops->hw_phy_cfg = r8157_hw_phy_cfg; + ops->autosuspend_en = rtl8157_runtime_enable; + ops->change_mtu = rtl8156_change_mtu; + tp->rx_buf_sz = 48 * 1024; + tp->support_2500full = 1; + tp->support_5000full = 1; + tp->support_10000full = 1; + r8157_desc_init(tp); + break; + default: ret = -ENODEV; dev_err(&tp->intf->dev, "Unknown Device\n"); @@ -9659,6 +9929,8 @@ static int rtl_ops_init(struct r8152 *tp) #define FIRMWARE_8153C_1 "rtl_nic/rtl8153c-1.fw" #define FIRMWARE_8156A_2 "rtl_nic/rtl8156a-2.fw" #define FIRMWARE_8156B_2 "rtl_nic/rtl8156b-2.fw" +#define FIRMWARE_8157_1 "rtl_nic/rtl8157-1.fw" +#define FIRMWARE_8159_1 "rtl_nic/rtl8159-1.fw" MODULE_FIRMWARE(FIRMWARE_8153A_2); MODULE_FIRMWARE(FIRMWARE_8153A_3); @@ -9667,6 +9939,8 @@ MODULE_FIRMWARE(FIRMWARE_8153B_2); MODULE_FIRMWARE(FIRMWARE_8153C_1); MODULE_FIRMWARE(FIRMWARE_8156A_2); MODULE_FIRMWARE(FIRMWARE_8156B_2); +MODULE_FIRMWARE(FIRMWARE_8157_1); +MODULE_FIRMWARE(FIRMWARE_8159_1); static int rtl_fw_init(struct r8152 *tp) { @@ -9705,6 +9979,12 @@ static int rtl_fw_init(struct r8152 *tp) rtl_fw->pre_fw = r8153b_pre_firmware_1; rtl_fw->post_fw = r8153c_post_firmware_1; break; + case RTL_VER_16: + rtl_fw->fw_name = FIRMWARE_8157_1; + break; + case RTL_VER_17: + rtl_fw->fw_name = FIRMWARE_8159_1; + break; default: break; } @@ -9796,6 +10076,9 @@ static u8 __rtl_get_hw_ver(struct usb_device *udev) case 0x1030: version = RTL_VER_16; break; + case 0x2020: + version = RTL_VER_17; + break; default: version = RTL_VER_UNKNOWN; dev_info(&udev->dev, "Unknown version 0x%04x\n", ocp_data); @@ -9953,6 +10236,7 @@ static int rtl8152_probe_once(struct usb_interface *intf, case RTL_VER_13: case RTL_VER_15: case RTL_VER_16: + case RTL_VER_17: netdev->max_mtu = size_to_mtu(16 * 1024); break; case RTL_VER_01: @@ -9987,6 +10271,11 @@ static int rtl8152_probe_once(struct usb_interface *intf, tp->speed = SPEED_5000; tp->advertising |= RTL_ADVERTISED_5000_FULL; } + if (tp->support_10000full && + tp->udev->speed >= USB_SPEED_SUPER) { + tp->speed = SPEED_10000; + tp->advertising |= RTL_ADVERTISED_10000_FULL; + } tp->advertising |= RTL_ADVERTISED_1000_FULL; } tp->duplex = DUPLEX_FULL; @@ -10113,6 +10402,7 @@ static const struct usb_device_id rtl8152_table[] = { { USB_DEVICE(VENDOR_ID_REALTEK, 0x8155) }, { USB_DEVICE(VENDOR_ID_REALTEK, 0x8156) }, { USB_DEVICE(VENDOR_ID_REALTEK, 0x8157) }, + { USB_DEVICE(VENDOR_ID_REALTEK, 0x815a) }, /* Microsoft */ { USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab) }, diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index 7b3739b29c8f..5e39d05a2d7b 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -630,6 +630,17 @@ static const struct driver_info zte_rndis_info = { .tx_fixup = rndis_tx_fixup, }; +static const struct driver_info rndis_info_lowpower = { + .description = "RNDIS device", + .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, + .bind = rndis_bind, + .unbind = rndis_unbind, + .status = rndis_status, + .rx_fixup = rndis_rx_fixup, + .tx_fixup = rndis_tx_fixup, + .manage_power = usbnet_manage_power, +}; + /*-------------------------------------------------------------------------*/ static const struct usb_device_id products [] = { @@ -657,6 +668,11 @@ static const struct usb_device_id products [] = { /* RNDIS is MSFT's un-official variant of CDC ACM */ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), .driver_info = (unsigned long) &rndis_info, +}, { + /* Telit Cinterion LE310X1 RNDIS */ + USB_DEVICE_AND_INTERFACE_INFO(0x1bc7, 0x7030, + USB_CLASS_WIRELESS_CONTROLLER, 1, 3), + .driver_info = (unsigned long)&rndis_info_lowpower, }, { /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index b72ba0803392..25518635b7b7 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -149,10 +149,8 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf) return tmp; } - dev->in = usb_rcvbulkpipe(dev->udev, - in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - dev->out = usb_sndbulkpipe(dev->udev, - out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + dev->in = usb_rcvbulkpipe(dev->udev, usb_endpoint_num(&in->desc)); + dev->out = usb_sndbulkpipe(dev->udev, usb_endpoint_num(&out->desc)); dev->status = status; return 0; } @@ -232,9 +230,7 @@ static int init_status(struct usbnet *dev, struct usb_interface *intf) if (!dev->driver_info->status) return 0; - pipe = usb_rcvintpipe(dev->udev, - dev->status->desc.bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK); + pipe = usb_rcvintpipe(dev->udev, usb_endpoint_num(&dev->status->desc)); maxp = usb_maxpacket(dev->udev, pipe); /* avoid 1 msec chatter: min 8 msec poll rate */ diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f4adcfee7a80..7d2eeb9b1226 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -6222,6 +6222,19 @@ static void virtnet_free_irq_moder(struct virtnet_info *vi) rtnl_unlock(); } +static netdev_features_t virtnet_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + /* Inner csum offload is only available for GSO packets. */ + if (skb->encapsulation && + (!skb_is_gso(skb) || netif_needs_gso(skb, features))) + return features & ~NETIF_F_CSUM_MASK; + + /* Passthru. */ + return features; +} + static const struct net_device_ops virtnet_netdev = { .ndo_open = virtnet_open, .ndo_stop = virtnet_close, @@ -6235,7 +6248,7 @@ static const struct net_device_ops virtnet_netdev = { .ndo_bpf = virtnet_xdp, .ndo_xdp_xmit = virtnet_xdp_xmit, .ndo_xsk_wakeup = virtnet_xsk_wakeup, - .ndo_features_check = passthru_features_check, + .ndo_features_check = virtnet_features_check, .ndo_get_phys_port_name = virtnet_get_phys_port_name, .ndo_set_features = virtnet_set_features, .ndo_tx_timeout = virtnet_tx_timeout, diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index b5b1253ac08b..67c367cc5662 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -88,10 +88,10 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family, flags &= VXLAN_F_RCV_FLAGS; hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) { - if (inet_sk(vs->sock->sk)->inet_sport == port && + if (inet_sk(vs->sk)->inet_sport == port && vxlan_get_sk_family(vs) == family && vs->flags == flags && - vs->sock->sk->sk_bound_dev_if == ifindex) + vs->sk->sk_bound_dev_if == ifindex) return vs; } return NULL; @@ -657,14 +657,18 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk, struct sk_buff *skb, struct gro_remcsum *grc) { - struct sk_buff *p; struct vxlanhdr *vh, *vh2; unsigned int hlen, off_vx; - struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk); + struct vxlan_sock *vs; + struct sk_buff *p; __be32 flags; skb_gro_remcsum_init(grc); + vs = rcu_dereference_sk_user_data(sk); + if (!vs) + return NULL; + off_vx = skb_gro_offset(skb); hlen = off_vx + sizeof(*vh); vh = skb_gro_header(skb, hlen, off_vx); @@ -1493,7 +1497,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs) return false; hlist_del_rcu(&vs->hlist); - udp_tunnel_notify_del_rx_port(vs->sock, + udp_tunnel_notify_del_rx_port(vs->sk, (vs->flags & VXLAN_F_GPE) ? UDP_TUNNEL_TYPE_VXLAN_GPE : UDP_TUNNEL_TYPE_VXLAN); @@ -1511,7 +1515,6 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan) #endif RCU_INIT_POINTER(vxlan->vn4_sock, NULL); - synchronize_net(); if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) vxlan_vs_del_vnigrp(vxlan); @@ -1519,14 +1522,14 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan) vxlan_vs_del_dev(vxlan); if (__vxlan_sock_release_prep(sock4)) { - udp_tunnel_sock_release(sock4->sock); - kfree(sock4); + udp_tunnel_sock_release(sock4->sk); + kfree_rcu(sock4, rcu); } #if IS_ENABLED(CONFIG_IPV6) if (__vxlan_sock_release_prep(sock6)) { - udp_tunnel_sock_release(sock6->sock); - kfree(sock6); + udp_tunnel_sock_release(sock6->sk); + kfree_rcu(sock6, rcu); } #endif } @@ -2473,7 +2476,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } if (!ifindex) - ifindex = sock4->sock->sk->sk_bound_dev_if; + ifindex = sock4->sk->sk_bound_dev_if; rt = udp_tunnel_dst_lookup(skb, dev, vxlan->net, ifindex, &saddr, pkey, src_port, dst_port, @@ -2540,7 +2543,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto tx_error; } - udp_tunnel_xmit_skb(rt, sock4->sock->sk, skb, saddr, + udp_tunnel_xmit_skb(rt, sock4->sk, skb, saddr, pkey->u.ipv4.dst, tos, ttl, df, src_port, dst_port, xnet, !udp_sum, ipcb_flags); @@ -2557,9 +2560,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } if (!ifindex) - ifindex = sock6->sock->sk->sk_bound_dev_if; + ifindex = sock6->sk->sk_bound_dev_if; - ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock, + ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sk, ifindex, &saddr, pkey, src_port, dst_port, tos, use_cache ? dst_cache : NULL); @@ -2615,7 +2618,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto tx_error; } - udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev, + udp_tunnel6_xmit_skb(ndst, sock6->sk, skb, dev, &saddr, &pkey->u.ipv6.dst, tos, ttl, pkey->label, src_port, dst_port, !udp_sum, ip6cb_flags); @@ -3254,7 +3257,7 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) if (!sock6) return -EIO; - ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock, + ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sk, 0, &info->key.u.ipv6.src, &info->key, sport, dport, info->key.tos, @@ -3332,9 +3335,9 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push) type = UDP_TUNNEL_TYPE_VXLAN; if (push) - udp_tunnel_push_rx_port(dev, vs->sock, type); + udp_tunnel_push_rx_port(dev, vs->sk, type); else - udp_tunnel_drop_rx_port(dev, vs->sock, type); + udp_tunnel_drop_rx_port(dev, vs->sk, type); } } } @@ -3540,8 +3543,8 @@ static const struct ethtool_ops vxlan_ethtool_ops = { .get_link_ksettings = vxlan_get_link_ksettings, }; -static struct socket *vxlan_create_sock(struct net *net, bool ipv6, - __be16 port, u32 flags, int ifindex) +static struct sock *vxlan_create_sock(struct net *net, bool ipv6, + __be16 port, u32 flags, int ifindex) { struct socket *sock; struct udp_port_cfg udp_conf; @@ -3567,7 +3570,7 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6, return ERR_PTR(err); udp_allow_gso(sock->sk); - return sock; + return sock->sk; } /* Create new listen socket if needed */ @@ -3575,10 +3578,10 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, __be16 port, u32 flags, int ifindex) { - struct vxlan_sock *vs; - struct socket *sock; - unsigned int h; struct udp_tunnel_sock_cfg tunnel_cfg; + struct vxlan_sock *vs; + struct sock *sk; + unsigned int h; ASSERT_RTNL(); @@ -3589,18 +3592,18 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, for (h = 0; h < VNI_HASH_SIZE; ++h) INIT_HLIST_HEAD(&vs->vni_list[h]); - sock = vxlan_create_sock(net, ipv6, port, flags, ifindex); - if (IS_ERR(sock)) { + sk = vxlan_create_sock(net, ipv6, port, flags, ifindex); + if (IS_ERR(sk)) { kfree(vs); - return ERR_CAST(sock); + return ERR_CAST(sk); } - vs->sock = sock; + vs->sk = sk; refcount_set(&vs->refcnt, 1); vs->flags = (flags & VXLAN_F_RCV_FLAGS); hlist_add_head_rcu(&vs->hlist, vs_head(net, port)); - udp_tunnel_notify_add_rx_port(sock, + udp_tunnel_notify_add_rx_port(sk, (vs->flags & VXLAN_F_GPE) ? UDP_TUNNEL_TYPE_VXLAN_GPE : UDP_TUNNEL_TYPE_VXLAN); @@ -3620,7 +3623,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, tunnel_cfg.gro_complete = vxlan_gro_complete; } - setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(net, sk, &tunnel_cfg); return vs; } diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c index b0e80bca855c..3b75b48dc726 100644 --- a/drivers/net/vxlan/vxlan_multicast.c +++ b/drivers/net/vxlan/vxlan_multicast.c @@ -29,7 +29,7 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, .imr_ifindex = ifindex, }; - sk = sock4->sock->sk; + sk = sock4->sk; lock_sock(sk); ret = ip_mc_join_group(sk, &mreq); release_sock(sk); @@ -37,7 +37,7 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip, } else { struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); - sk = sock6->sock->sk; + sk = sock6->sk; lock_sock(sk); ret = ipv6_sock_mc_join(sk, ifindex, &ip->sin6.sin6_addr); release_sock(sk); @@ -62,7 +62,7 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, .imr_ifindex = ifindex, }; - sk = sock4->sock->sk; + sk = sock4->sk; lock_sock(sk); ret = ip_mc_leave_group(sk, &mreq); release_sock(sk); @@ -70,7 +70,7 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip, } else { struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); - sk = sock6->sock->sk; + sk = sock6->sk; lock_sock(sk); ret = ipv6_sock_mc_drop(sk, ifindex, &ip->sin6.sin6_addr); release_sock(sk); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 73f393900710..6508c53bdc24 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -524,27 +524,21 @@ do { \ /* PCI ID lookup table */ static const struct pci_device_id fst_pci_dev_id[] = { - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2P, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T2P}, + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_T2P), .driver_data = FST_TYPE_T2P }, - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4P, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T4P}, + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_T4P), .driver_data = FST_TYPE_T4P }, - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T1U, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T1U}, + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_T1U), .driver_data = FST_TYPE_T1U }, - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2U, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T2U}, + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_T2U), .driver_data = FST_TYPE_T2U }, - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4U, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T4U}, + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_T4U), .driver_data = FST_TYPE_T4U }, - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_TE1), .driver_data = FST_TYPE_TE1 }, - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1C, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, - {0,} /* End */ + { PCI_VDEVICE(FARSITE, PCI_DEVICE_ID_FARSITE_TE1C), .driver_data = FST_TYPE_TE1 }, + + { } /* End */ }; MODULE_DEVICE_TABLE(pci, fst_pci_dev_id); diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c index 7e57d289b62c..a91564e53caa 100644 --- a/drivers/net/wan/pc300too.c +++ b/drivers/net/wan/pc300too.c @@ -461,15 +461,11 @@ static int pc300_pci_init_one(struct pci_dev *pdev, } static const struct pci_device_id pc300_pci_tbl[] = { - { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_RX_1, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_RX_2, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_TE_1, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_TE_2, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { 0, } + { PCI_VDEVICE(CYCLADES, PCI_DEVICE_ID_PC300_RX_1) }, + { PCI_VDEVICE(CYCLADES, PCI_DEVICE_ID_PC300_RX_2) }, + { PCI_VDEVICE(CYCLADES, PCI_DEVICE_ID_PC300_TE_1) }, + { PCI_VDEVICE(CYCLADES, PCI_DEVICE_ID_PC300_TE_2) }, + { } }; static struct pci_driver pc300_pci_driver = { diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c index 89f4b4584a33..9503130fd9a8 100644 --- a/drivers/net/wan/pci200syn.c +++ b/drivers/net/wan/pci200syn.c @@ -397,9 +397,9 @@ static int pci200_pci_init_one(struct pci_dev *pdev, } static const struct pci_device_id pci200_pci_tbl[] = { - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, - PCI_DEVICE_ID_PLX_PCI200SYN, 0, 0, 0 }, - { 0, } + { PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_PCI200SYN) }, + { } }; static struct pci_driver pci200_pci_driver = { diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index 3f7707118450..d4da88c77112 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -806,13 +806,10 @@ static int wanxl_pci_init_one(struct pci_dev *pdev, } static const struct pci_device_id wanxl_pci_tbl[] = { - { PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL100, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL200, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { PCI_VENDOR_ID_SBE, PCI_DEVICE_ID_SBE_WANXL400, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, 0 }, - { 0, } + { PCI_VDEVICE(SBE, PCI_DEVICE_ID_SBE_WANXL100) }, + { PCI_VDEVICE(SBE, PCI_DEVICE_ID_SBE_WANXL200) }, + { PCI_VDEVICE(SBE, PCI_DEVICE_ID_SBE_WANXL400) }, + { } }; static struct pci_driver wanxl_pci_driver = { diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index c362c78d908e..0028ef17dc71 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -335,7 +335,7 @@ static void sock_free(struct sock *sock) if (unlikely(!sock)) return; sk_clear_memalloc(sock); - udp_tunnel_sock_release(sock->sk_socket); + udp_tunnel_sock_release(sock); } static void set_sock_opts(struct socket *sock) @@ -389,14 +389,14 @@ int wg_socket_init(struct wg_device *wg, u16 port) goto out; } set_sock_opts(new4); - setup_udp_tunnel_sock(net, new4, &cfg); + setup_udp_tunnel_sock(net, new4->sk, &cfg); #if IS_ENABLED(CONFIG_IPV6) if (ipv6_mod_enabled()) { port6.local_udp_port = inet_sk(new4->sk)->inet_sport; ret = udp_sock_create(net, &port6, &new6); if (ret < 0) { - udp_tunnel_sock_release(new4); + udp_tunnel_sock_release(new4->sk); if (ret == -EADDRINUSE && !port && retries++ < 100) goto retry; pr_err("%s: Could not create IPv6 socket\n", @@ -404,7 +404,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) goto out; } set_sock_opts(new6); - setup_udp_tunnel_sock(net, new6, &cfg); + setup_udp_tunnel_sock(net, new6->sk, &cfg); } #endif diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 52118867ecde..e4c84be44ba6 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index a9cbe955e084..5a796e704ae9 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 73a9db302245..dfee432615eb 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1269,7 +1269,6 @@ struct ath10k { } testmode; struct { - struct gpio_led wifi_led; struct led_classdev cdev; char label[48]; u32 gpio_state_pin; diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c index 50d0c4213ecf..7fdc06fff79f 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.c +++ b/drivers/net/wireless/ath/ath10k/coredump.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h index 8d274e0f374b..95ee70d23370 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.h +++ b/drivers/net/wireless/ath/ath10k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index b7520220465a..fb61e53ff4c3 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index b9fb192e0b48..7f7be7c69232 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index ce9b248c12dc..821a12090ba9 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index dbaf262cd7c1..1c2e137b6002 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 603f6de62b0a..25c6b2e2f81c 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _HTT_H_ diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 25ab945fecef..faac359aa9ac 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index d6f1d85ba871..e46f579d745d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ @@ -1353,7 +1352,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm msdu_id = res; } - /* As msdu is freed by mac80211 (in ieee80211_tx_status()) and by + /* As msdu is freed by mac80211 (in ieee80211_tx_status_skb()) and by * ath10k (in ath10k_htt_htc_tx_complete()) we have to increase * reference by one to avoid a use-after-free case and a double * free. diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 59b6cebfdd8f..6f5019bf0b7d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index da71dce9babf..cd468b24bd33 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _HW_H_ diff --git a/drivers/net/wireless/ath/ath10k/leds.c b/drivers/net/wireless/ath/ath10k/leds.c index 3a6c8111e7c6..a3961e7760a5 100644 --- a/drivers/net/wireless/ath/ath10k/leds.c +++ b/drivers/net/wireless/ath/ath10k/leds.c @@ -19,15 +19,13 @@ static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev, { struct ath10k *ar = container_of(led_cdev, struct ath10k, leds.cdev); - struct gpio_led *led = &ar->leds.wifi_led; mutex_lock(&ar->conf_mutex); if (ar->state != ATH10K_STATE_ON) goto out; - ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low; - ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, ar->leds.gpio_state_pin); + ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, brightness == LED_OFF); out: mutex_unlock(&ar->conf_mutex); @@ -63,13 +61,9 @@ int ath10k_leds_register(struct ath10k *ar) snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s", wiphy_name(ar->hw->wiphy)); - ar->leds.wifi_led.active_low = 1; - ar->leds.wifi_led.name = ar->leds.label; - ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP; ar->leds.cdev.name = ar->leds.label; ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking; - ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger; ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev); if (ret) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 97b49bf4ad80..335bc7c488e4 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 4c3f536f2ea1..d114778edb41 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _PCI_H_ diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index e7f90fd9e9b8..0d74548a5f34 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -1112,7 +1112,7 @@ int ath10k_qmi_init(struct ath10k *ar, u32 msa_size) spin_lock_init(&qmi->event_lock); INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work); - ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01, + ret = qmi_add_lookup(&qmi->qmi_hdl, QMI_SERVICE_ID_WLFW, WLFW_SERVICE_VERS_V01, 0); if (ret) goto err_qmi_lookup; diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c index 0e85c75d2278..279aeb2a7dbc 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h index 9f311f3bc9e7..8d4b929e8437 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h @@ -1,13 +1,12 @@ /* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef WCN3990_QMI_SVC_V01_H #define WCN3990_QMI_SVC_V01_H -#define WLFW_SERVICE_ID_V01 0x45 #define WLFW_SERVICE_VERS_V01 0x01 #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index 564293df1e9a..e6d92a29d2a0 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _RX_DESC_H_ diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 43a6b1ba8fb8..65e941b52751 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -3,7 +3,7 @@ * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. * Copyright (c) 2016-2017 Erik Stromdahl - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index 8b15ec07b107..9bb486f8e920 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/usb.h b/drivers/net/wireless/ath/ath10k/usb.h index 7e4cfbb673c9..517afbd73764 100644 --- a/drivers/net/wireless/ath/ath10k/usb.h +++ b/drivers/net/wireless/ath/ath10k/usb.h @@ -3,7 +3,7 @@ * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * Copyright (c) 2016-2017 Erik Stromdahl - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _USB_H_ diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 8a2f87d0a3a3..ea18ed8939db 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _WMI_TLV_H #define _WMI_TLV_H diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 78653f257925..92af8bc63362 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "mac.h" diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig index 385513cfdc30..122726f84492 100644 --- a/drivers/net/wireless/ath/ath11k/Kconfig +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: BSD-3-Clause-Clear config ATH11K - tristate "Qualcomm Technologies 802.11ax chipset support" + tristate "Qualcomm 802.11ax chipset support" depends on MAC80211 && HAS_DMA select ATH_COMMON select QCOM_QMI_HELPERS diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 08d3a0c8f105..f566d699d074 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h index 415ddfd26654..8c1eb1e8e6b1 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.h +++ b/drivers/net/wireless/ath/ath11k/ahb.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_AHB_H #define ATH11K_AHB_H diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index f2d5106adb01..c56dd8a86760 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index bcde2fcf02cf..308f425fcec0 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_CE_H diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 3f6f4db5b7ee..8dacc878c006 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -267,7 +267,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), @@ -445,7 +445,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -535,7 +535,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -797,7 +797,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -886,7 +886,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c index 1949d57b007a..3de34295ddc8 100644 --- a/drivers/net/wireless/ath/ath11k/coredump.c +++ b/drivers/net/wireless/ath/ath11k/coredump.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h index 3960d9385261..1be82207690f 100644 --- a/drivers/net/wireless/ath/ath11k/coredump.h +++ b/drivers/net/wireless/ath/ath11k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_COREDUMP_H_ #define _ATH11K_COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c index 37d23a559ba3..f65e6cf846f7 100644 --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 0c1138407838..70922612d3fb 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index ed7fec177588..859071938a65 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_DEBUGFS_H_ diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c index 11d28c42227e..731f3ca86711 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h index 476689bbd4da..856aa74ba365 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef DEBUG_HTT_STATS_H diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.h b/drivers/net/wireless/ath/ath11k/debugfs_sta.h index ace877e19275..6d4cf365d937 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.h +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_DEBUGFS_STA_H_ diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index bbb86f165141..f389b97acbdd 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ @@ -1040,6 +1039,7 @@ void ath11k_dp_free(struct ath11k_base *ab) idr_destroy(&dp->tx_ring[i].txbuf_idr); spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); kfree(dp->tx_ring[i].tx_status); + dp->tx_ring[i].tx_status = NULL; } /* Deinit any SOC level resource */ diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 1bd513f68a3c..84f66839f0c6 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_DP_H diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 2a413e3a07a7..9e90d8e3f155 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2503,6 +2503,29 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); } +static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab, + struct hal_rx_desc *rx_desc, + struct sk_buff *msdu) +{ + struct ieee80211_hdr *hdr; + u8 decap_type; + u32 hdr_len; + + decap_type = ath11k_dp_rx_h_msdu_start_decap_type(ab, rx_desc); + if (decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI) + return true; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN)) + return true; + + ab->soc_stats.invalid_rbm++; + WARN_ON_ONCE(1); + return false; +} + static int ath11k_dp_rx_process_msdu(struct ath11k *ar, struct sk_buff *msdu, struct sk_buff_head *msdu_list, @@ -2573,6 +2596,11 @@ static int ath11k_dp_rx_process_msdu(struct ath11k *ar, } } + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) { + ret = -EINVAL; + goto free_out; + } + ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); @@ -3262,6 +3290,12 @@ static int ath11k_dp_rx_h_verify_tkip_mic(struct ath11k *ar, struct ath11k_peer RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; skb_pull(msdu, hal_rx_desc_sz); + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ar->ab, rx_desc, + msdu))) { + dev_kfree_skb_any(msdu); + return -EINVAL; + } + ath11k_dp_rx_h_ppdu(ar, rx_desc, rxs); ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); @@ -3954,6 +3988,10 @@ static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } + + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ar->ab, desc, msdu))) + return -EINVAL; + ath11k_dp_rx_h_ppdu(ar, desc, status); ath11k_dp_rx_h_mpdu(ar, msdu, desc, status); @@ -3998,7 +4036,7 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, return drop; } -static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, +static bool ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status) { u16 msdu_len; @@ -4006,15 +4044,28 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, u8 l3pad_bytes; struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; + struct ath11k_base *ab = ar->ab; rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ar->ab, desc); rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ar->ab, desc); l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, desc); msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(ar->ab, desc); + + if (unlikely(hal_rx_desc_sz + l3pad_bytes + msdu_len > DP_RX_BUFFER_SIZE)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "invalid msdu len in tkip mic err %u\n", msdu_len); + ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", desc, + sizeof(*desc)); + return true; + } + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return true; + ath11k_dp_rx_h_ppdu(ar, desc, status); status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | @@ -4022,19 +4073,21 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, ath11k_dp_rx_h_undecap(ar, msdu, desc, HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); + + return false; } static bool ath11k_dp_rx_h_rxdma_err(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status) { struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); - bool drop = false; + bool drop; ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: - ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); + drop = ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); break; default: /* TODO: Review other rxdma error code to check if anything is diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h index c322e30caa96..f69d9301e0a1 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.h +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_DP_RX_H #define ATH11K_DP_RX_H diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 9c2310665713..cac970c92806 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h index 795fe3b8fa0d..9303b5ba6e01 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.h +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_DP_TX_H diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c index 07d775a7b528..ef1b4c81843a 100644 --- a/drivers/net/wireless/ath/ath11k/fw.c +++ b/drivers/net/wireless/ath/ath11k/fw.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/fw.h b/drivers/net/wireless/ath/ath11k/fw.h index d9893ceb2c3d..df2fab2c1143 100644 --- a/drivers/net/wireless/ath/ath11k/fw.h +++ b/drivers/net/wireless/ath/ath11k/fw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_FW_H diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h index b2fd180bd28e..c8b01f6921c0 100644 --- a/drivers/net/wireless/ath/ath11k/hal_desc.h +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 51e0840bc0d1..75f7b8d8a0d7 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "debug.h" diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 0fa9aef9d533..475807b89a1a 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_HAL_RX_H diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index b919df6ce743..b7e9b1cdd76a 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "hal_desc.h" diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index 46d17abd808b..f2dc6e270534 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_HAL_TX_H diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index cd9c4b838246..017fed1b4bd1 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _HIF_H_ diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c index 4571d01cc33d..5f9ee5e633da 100644 --- a/drivers/net/wireless/ath/ath11k/htc.c +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include diff --git a/drivers/net/wireless/ath/ath11k/htc.h b/drivers/net/wireless/ath/ath11k/htc.h index 86f77eacaea7..945c409bc08f 100644 --- a/drivers/net/wireless/ath/ath11k/htc.h +++ b/drivers/net/wireless/ath/ath11k/htc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_HTC_H diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c index d19c4b372a2a..93f9a03c48dc 100644 --- a/drivers/net/wireless/ath/ath11k/hw.c +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index a48b6bf1f29a..2d55cdc4d165 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4227,13 +4227,14 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, req->n_channels); if (!arg) { ret = -ENOMEM; goto exit; } + arg->num_chan = req->n_channels; ath11k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; @@ -4261,38 +4262,27 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, arg->scan_f_passive = 1; } - if (req->n_channels) { - arg->num_chan = req->n_channels; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); + for (i = 0; i < arg->num_chan; i++) { + if (test_bit(WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL, + ar->ab->wmi_ab.svc_map)) { + arg->chan_list[i] = + u32_encode_bits(req->channels[i]->center_freq, + WMI_SCAN_CONFIG_PER_CHANNEL_MASK); - if (!arg->chan_list) { - ret = -ENOMEM; - goto exit; - } - - for (i = 0; i < arg->num_chan; i++) { - if (test_bit(WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL, - ar->ab->wmi_ab.svc_map)) { - arg->chan_list[i] = - u32_encode_bits(req->channels[i]->center_freq, - WMI_SCAN_CONFIG_PER_CHANNEL_MASK); - - /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan - * flags, then scan all PSC channels in 6 GHz band and - * those non-PSC channels where RNR IE is found during - * the legacy 2.4/5 GHz scan. - * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, - * then all channels in 6 GHz will be scanned. - */ - if (req->channels[i]->band == NL80211_BAND_6GHZ && - req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && - !cfg80211_channel_is_psc(req->channels[i])) - arg->chan_list[i] |= - WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; - } else { - arg->chan_list[i] = req->channels[i]->center_freq; - } + /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan + * flags, then scan all PSC channels in 6 GHz band and + * those non-PSC channels where RNR IE is found during + * the legacy 2.4/5 GHz scan. + * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, + * then all channels in 6 GHz will be scanned. + */ + if (req->channels[i]->band == NL80211_BAND_6GHZ && + req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && + !cfg80211_channel_is_psc(req->channels[i])) + arg->chan_list[i] |= + WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; + } else { + arg->chan_list[i] = req->channels[i]->center_freq; } } @@ -4335,7 +4325,6 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, exit: if (arg) { - kfree(arg->chan_list); kfree(arg->extraie.ptr); kfree(arg); } @@ -9735,19 +9724,14 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, 1); if (!arg) { ret = -ENOMEM; goto exit; } - ath11k_wmi_start_scan_init(ar, arg); + arg->num_chan = 1; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); - if (!arg->chan_list) { - ret = -ENOMEM; - goto free_arg; - } + ath11k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; @@ -9768,7 +9752,7 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); ar->scan.state = ATH11K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); - goto free_chan_list; + goto free_arg; } ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ); @@ -9778,7 +9762,7 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret); ret = -ETIMEDOUT; - goto free_chan_list; + goto free_arg; } ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, @@ -9786,8 +9770,6 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, ret = 0; -free_chan_list: - kfree(arg->chan_list); free_arg: kfree(arg); exit: diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 5e61eea1bb03..59f83c7175fd 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_MAC_H diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index f994233df2bb..a6c9ff112c68 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -282,8 +282,10 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, break; } + spin_lock_bh(&ab->base_lock); if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))) queue_work(ab->workqueue_aux, &ab->reset_work); + spin_unlock_bh(&ab->base_lock); break; default: diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h index 5c5c2b03c81f..7e7561ce883c 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.h +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_MHI_H #define _ATH11K_MHI_H diff --git a/drivers/net/wireless/ath/ath11k/p2p.c b/drivers/net/wireless/ath/ath11k/p2p.c index 01e14523f1fe..77672099c602 100644 --- a/drivers/net/wireless/ath/ath11k/p2p.c +++ b/drivers/net/wireless/ath/ath11k/p2p.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/p2p.h b/drivers/net/wireless/ath/ath11k/p2p.h index d907940a9b09..449f0c61b600 100644 --- a/drivers/net/wireless/ath/ath11k/p2p.h +++ b/drivers/net/wireless/ath/ath11k/p2p.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_P2P_H diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 7114eca8810d..35bb9e7a63a2 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1210,6 +1210,14 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev) struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); + + spin_lock_bh(&ab->base_lock); + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + spin_unlock_bh(&ab->base_lock); + + cancel_work_sync(&ab->reset_work); + cancel_work_sync(&ab->dump_work); + ath11k_pci_power_down(ab, false); } diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index fc6e7da05c60..2259adc3bbdc 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/pcic.h b/drivers/net/wireless/ath/ath11k/pcic.h index ac012e88bf6d..7a654a6ef9d1 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.h +++ b/drivers/net/wireless/ath/ath11k/pcic.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_PCI_CMN_H diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 027cc7aa58c8..b30a90698544 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h index f5ef1a27f8f2..20c68ef52301 100644 --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_PEER_H diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index feebbc30f3df..410a7ee076a0 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ @@ -3337,7 +3336,7 @@ int ath11k_qmi_init_service(struct ath11k_base *ab) spin_lock_init(&ab->qmi.event_lock); INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work); - ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01, + ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW, ATH11K_QMI_WLFW_SERVICE_VERS_V01, ab->qmi.service_ins_id); if (ret < 0) { diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 7968ab122b65..350816c81ee5 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_QMI_H @@ -15,7 +15,6 @@ #define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 #define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000 #define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 -#define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45 #define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01 #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01 diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 72b483594015..fed9bedc52ef 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_REG_H diff --git a/drivers/net/wireless/ath/ath11k/rx_desc.h b/drivers/net/wireless/ath/ath11k/rx_desc.h index 2da6da727278..3780bd909bbf 100644 --- a/drivers/net/wireless/ath/ath11k/rx_desc.h +++ b/drivers/net/wireless/ath/ath11k/rx_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_RX_DESC_H #define ATH11K_RX_DESC_H diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c index b6b0516819a6..03af39071839 100644 --- a/drivers/net/wireless/ath/ath11k/spectral.c +++ b/drivers/net/wireless/ath/ath11k/spectral.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/spectral.h b/drivers/net/wireless/ath/ath11k/spectral.h index 789cff7c64a7..b582f2c36c08 100644 --- a/drivers/net/wireless/ath/ath11k/spectral.h +++ b/drivers/net/wireless/ath/ath11k/spectral.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_SPECTRAL_H diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index c72eed358f6d..21a85301bf9f 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "testmode.h" diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h index 2f62f2c4422f..45019b79fcf1 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.h +++ b/drivers/net/wireless/ath/ath11k/testmode.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/thermal.c b/drivers/net/wireless/ath/ath11k/thermal.c index 18d6eab5cce3..67246b5ca5bf 100644 --- a/drivers/net/wireless/ath/ath11k/thermal.c +++ b/drivers/net/wireless/ath/ath11k/thermal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/thermal.h b/drivers/net/wireless/ath/ath11k/thermal.h index cdaf4e01d92e..15e4e97c944f 100644 --- a/drivers/net/wireless/ath/ath11k/thermal.h +++ b/drivers/net/wireless/ath/ath11k/thermal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_THERMAL_ diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h index 75246b0a82e3..d43776229bc9 100644 --- a/drivers/net/wireless/ath/ath11k/trace.h +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index baed501b640b..b2dade0516ac 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -3423,7 +3423,6 @@ struct scan_req_params { u32 num_bssid; u32 num_ssids; u32 n_probes; - u32 *chan_list; u32 notify_scan_events; struct wlan_ssid ssid[WLAN_SCAN_PARAMS_MAX_SSID]; struct wmi_mac_addr bssid_list[WLAN_SCAN_PARAMS_MAX_BSSID]; @@ -3436,6 +3435,7 @@ struct scan_req_params { struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; struct wmi_mac_addr mac_addr; struct wmi_mac_addr mac_mask; + u32 chan_list[] __counted_by(num_chan); }; struct wmi_ssid_arg { diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c index f02edc9b8a9e..d6c2725903cc 100644 --- a/drivers/net/wireless/ath/ath11k/wow.c +++ b/drivers/net/wireless/ath/ath11k/wow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/wow.h b/drivers/net/wireless/ath/ath11k/wow.h index c85811e3f42b..13dbcfec9227 100644 --- a/drivers/net/wireless/ath/ath11k/wow.h +++ b/drivers/net/wireless/ath/ath11k/wow.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _WOW_H_ diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig index d39c075758bd..4a2b240f967a 100644 --- a/drivers/net/wireless/ath/ath12k/Kconfig +++ b/drivers/net/wireless/ath/ath12k/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: BSD-3-Clause-Clear config ATH12K - tristate "Qualcomm Technologies Wi-Fi 7 support (ath12k)" + tristate "Qualcomm Wi-Fi 7 support (ath12k)" depends on MAC80211 && HAS_DMA && PCI select QCOM_QMI_HELPERS select MHI_BUS @@ -15,7 +15,7 @@ config ATH12K If you choose to build a module, it'll be called ath12k. config ATH12K_AHB - bool "QTI ath12k AHB support" + bool "Qualcomm ath12k AHB support" depends on ATH12K && REMOTEPROC select QCOM_MDT_LOADER select QCOM_SCM @@ -33,7 +33,7 @@ config ATH12K_DEBUG you want optimal performance choose N. config ATH12K_DEBUGFS - bool "QTI ath12k debugfs support" + bool "Qualcomm ath12k debugfs support" depends on ATH12K && MAC80211_DEBUGFS help Enable ath12k debugfs support diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index d81367ce6929..c1b9be3e03ac 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 3a26fea6af1a..dd80eebff96a 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_ACPI_H #define ATH12K_ACPI_H diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 2dcf0a52e4c1..30733a244454 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -583,31 +583,36 @@ static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath12k_ahb_ext_grp_napi_poll); - for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { - /* For TX ring, ensure that the ring mask and the - * tcl_to_wbm_rbm_map point to the same ring number. - */ + for (j = 0; j < DP_TCL_NUM_RING_MAX; j++) { if (ring_mask->tx[i] & - BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num)) { + BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) { irq_grp->irqs[num_irq++] = wbm2host_tx_completions_ring1 - j; } + } - if (ring_mask->rx[i] & BIT(j)) { + for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { + if (ring_mask->rx[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) { irq_grp->irqs[num_irq++] = reo2host_destination_ring1 - j; } - if (ring_mask->rx_err[i] & BIT(j)) + if (ring_mask->rx_err[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = reo2host_exception; - if (ring_mask->rx_wbm_rel[i] & BIT(j)) + if (ring_mask->rx_wbm_rel[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = wbm2host_rx_release; - if (ring_mask->reo_status[i] & BIT(j)) + if (ring_mask->reo_status[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = reo2host_status; - if (ring_mask->rx_mon_dest[i] & BIT(j)) + if (ring_mask->rx_mon_dest[i] & BIT(j) && + num_irq < ATH12K_EXT_IRQ_NUM_MAX) irq_grp->irqs[num_irq++] = rxdma2host_monitor_destination_mac1; } diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 980a12fb2c6e..742d4fd1b598 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1006,6 +1006,27 @@ static void ath12k_core_device_cleanup(struct ath12k_base *ab) mutex_unlock(&ab->core_lock); } +static int ath12k_core_device_setup(struct ath12k_base *ab) +{ + int ret; + + guard(mutex)(&ab->core_lock); + + ret = ath12k_core_pdev_create(ab); + if (ret) { + ath12k_err(ab, "failed to create pdev core %d\n", ret); + return ret; + } + + ath12k_hif_irq_enable(ab); + + ret = ath12k_core_rfkill_config(ab); + if (ret && ret != -EOPNOTSUPP) + return ret; + + return 0; +} + static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) { struct ath12k_base *ab; @@ -1015,10 +1036,6 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) clear_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags); - ath12k_mac_unregister(ag); - - ath12k_mac_mlo_teardown(ag); - for (i = ag->num_devices - 1; i >= 0; i--) { ab = ag->ab[i]; if (!ab) @@ -1029,6 +1046,12 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) ath12k_core_device_cleanup(ab); } + /* Unregister MAC (drops wiphys) only after per-device cleanup */ + ath12k_mac_unregister(ag); + + /* Teardown MLO state after MAC unregister for symmetry */ + ath12k_mac_mlo_teardown(ag); + ath12k_mac_destroy(ag); } @@ -1165,26 +1188,11 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag) if (!ab) continue; - mutex_lock(&ab->core_lock); - set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); - ret = ath12k_core_pdev_create(ab); - if (ret) { - ath12k_err(ab, "failed to create pdev core %d\n", ret); - mutex_unlock(&ab->core_lock); + ret = ath12k_core_device_setup(ab); + if (ret) goto err; - } - - ath12k_hif_irq_enable(ab); - - ret = ath12k_core_rfkill_config(ab); - if (ret && ret != -EOPNOTSUPP) { - mutex_unlock(&ab->core_lock); - goto err; - } - - mutex_unlock(&ab->core_lock); } return 0; @@ -2275,6 +2283,7 @@ void ath12k_core_deinit(struct ath12k_base *ab) void ath12k_core_free(struct ath12k_base *ab) { timer_delete_sync(&ab->rx_replenish_retry); + ath12k_wmi_free(); destroy_workqueue(ab->workqueue_aux); destroy_workqueue(ab->workqueue); kfree(ab); @@ -2299,6 +2308,9 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, if (!ab->workqueue_aux) goto err_free_wq; + if (ath12k_wmi_alloc() < 0) + goto err_free_wq_aux; + mutex_init(&ab->core_lock); spin_lock_init(&ab->base_lock); init_completion(&ab->reset_complete); @@ -2333,6 +2345,8 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, return ab; +err_free_wq_aux: + destroy_workqueue(ab->workqueue_aux); err_free_wq: destroy_workqueue(ab->workqueue); err_sc_free: diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 8be435535a4e..fc5127b5c1a3 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -353,6 +353,9 @@ struct ath12k_link_vif { u16 num_stations; bool is_csa_in_progress; struct wiphy_work bcn_tx_work; + + bool set_wds_vdev_param; + bool nawds_enabled; }; struct ath12k_vif { @@ -492,6 +495,10 @@ struct ath12k_link_sta { /* link address similar to ieee80211_link_sta */ u8 addr[ETH_ALEN]; + u16 tcl_metadata; + u16 ast_hash; + u16 ast_idx; + /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ u32 bw; @@ -527,6 +534,8 @@ struct ath12k_sta { u16 free_logical_link_idx_map; enum ieee80211_sta_state state; + + bool enable_4addr; }; #define ATH12K_HALF_20MHZ_BW 10 @@ -542,8 +551,8 @@ struct ath12k_sta { #define ATH12K_MAX_5GHZ_FREQ (ATH12K_5GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW) #define ATH12K_MIN_6GHZ_FREQ (ATH12K_6GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW) #define ATH12K_MAX_6GHZ_FREQ (ATH12K_6GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW) -#define ATH12K_NUM_CHANS 101 -#define ATH12K_MAX_5GHZ_CHAN 173 +#define ATH12K_NUM_CHANS 102 +#define ATH12K_MAX_5GHZ_CHAN 177 static inline bool ath12k_is_2ghz_channel_freq(u32 freq) { @@ -763,6 +772,14 @@ struct ath12k { struct ath12k_pdev_rssi_offsets rssi_info; struct ath12k_thermal thermal; + + /* Protected by ar->data_lock */ + struct ath12k_incumbent_signal_interference { + u32 center_freq; + enum nl80211_chan_width width; + u32 chan_bw_interference_bitmap; + bool handling_in_progress; + } incumbent_signal_interference; }; struct ath12k_hw { diff --git a/drivers/net/wireless/ath/ath12k/coredump.c b/drivers/net/wireless/ath/ath12k/coredump.c index ce1beeb54836..070512338f13 100644 --- a/drivers/net/wireless/ath/ath12k/coredump.c +++ b/drivers/net/wireless/ath/ath12k/coredump.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include "hif.h" diff --git a/drivers/net/wireless/ath/ath12k/coredump.h b/drivers/net/wireless/ath/ath12k/coredump.h index 13f46a605113..584ce0a085a7 100644 --- a/drivers/net/wireless/ath/ath12k/coredump.h +++ b/drivers/net/wireless/ath/ath12k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_COREDUMP_H_ #define _ATH12K_COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath12k/dbring.h b/drivers/net/wireless/ath/ath12k/dbring.h index e1c0eba774ec..780c27c570e5 100644 --- a/drivers/net/wireless/ath/ath12k/dbring.h +++ b/drivers/net/wireless/ath/ath12k/dbring.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_DBRING_H diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index bf254e43a68d..658b8083c9c3 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUG_H_ diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 8c81a1c22449..d17d4a8f1cb7 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1450,6 +1450,44 @@ static const struct file_operations fops_pdev_stats = { .llseek = default_llseek, }; +static ssize_t +ath12k_write_simulate_incumbent_signal_interference(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + struct wiphy *wiphy = ath12k_ar_to_hw(ar)->wiphy; + u32 chan_bw_interference_bitmap; + int ret; + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + /* + * Bitmap uses the firmware primary-based ordering documented in + * ath12k_wmi_transform_interference_bitmap() & intf_map_80. + */ + if (kstrtou32_from_user(user_buf, count, 0, &chan_bw_interference_bitmap)) + return -EINVAL; + + wiphy_lock(wiphy); + ret = ath12k_wmi_simulate_incumbent_signal_interference(ar, chan_bw_interference_bitmap); + if (ret) + goto exit; + + ret = count; + +exit: + wiphy_unlock(wiphy); + return ret; +} + +static const struct file_operations fops_simulate_incumbent_signal_interference = { + .write = ath12k_write_simulate_incumbent_signal_interference, + .open = simple_open, +}; + static void ath12k_debugfs_fw_stats_register(struct ath12k *ar) { @@ -1515,6 +1553,14 @@ void ath12k_debugfs_register(struct ath12k *ar) ar, &fops_tpc_stats_type); init_completion(&ar->debug.tpc_complete); + if (ar->mac.sbands[NL80211_BAND_6GHZ].channels && + test_bit(WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT, + ar->ab->wmi_ab.svc_map)) { + debugfs_create_file("simulate_incumbent_signal_interference", 0200, + ar->debug.debugfs_pdev, ar, + &fops_simulate_incumbent_signal_interference); + } + ath12k_debugfs_htt_stats_register(ar); ath12k_debugfs_fw_stats_register(ar); diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 21641a8a0346..f5dbca1b8d75 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUGFS_H_ diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h index 8de924f4d7d5..44c78f3ab904 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_sta.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUGFS_STA_H_ diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 90802ed1aa59..af5f11fc1d84 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -943,11 +943,11 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) dp_link_vif = ath12k_dp_vif_to_dp_link_vif(&ahvif->dp_vif, link_id); - dp_link_vif->tcl_metadata |= u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | - u32_encode_bits(arvif->vdev_id, - HTT_TCL_META_DATA_VDEV_ID) | - u32_encode_bits(ar->pdev->pdev_id, - HTT_TCL_META_DATA_PDEV_ID); + dp_link_vif->tcl_metadata = u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(arvif->vdev_id, + HTT_TCL_META_DATA_VDEV_ID) | + u32_encode_bits(ar->pdev->pdev_id, + HTT_TCL_META_DATA_PDEV_ID); /* set HTT extension valid bit to 0 by default */ dp_link_vif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c index 9c19d9707abf..52e10059c6d5 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.c +++ b/drivers/net/wireless/ath/ath12k/dp_htt.c @@ -146,7 +146,7 @@ static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab, } int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, - int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + int (*iter)(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data), void *data) { diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.h b/drivers/net/wireless/ath/ath12k/dp_htt.h index 6020e632f74e..987689f11cda 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.h +++ b/drivers/net/wireless/ath/ath12k/dp_htt.h @@ -1523,7 +1523,7 @@ int ath12k_dp_tx_htt_srng_setup(struct ath12k_base *ab, u32 ring_id, void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, struct sk_buff *skb); int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, - int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, + int (*iter)(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data), void *data); int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 737287a9aa46..44c5cff75f16 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -115,13 +115,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k_pdev_dp *dp_pdev, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev); @@ -471,13 +472,10 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_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 { struct ath12k *ar; diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index a1100782d45e..47d009a0d61f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -419,7 +419,7 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "ath12k dp peer find by peerid index called without rcu lock"); - if (!peer_id || peer_id >= ATH12K_DP_PEER_ID_INVALID) + if (peer_id >= ATH12K_DP_PEER_ID_INVALID) return NULL; index = ath12k_dp_peer_get_peerid_index(dp, peer_id); diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index 113b8040010f..f5067e66f1e1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -143,6 +143,8 @@ struct ath12k_dp_peer { struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; + + bool use_4addr; }; struct ath12k_dp_link_peer * diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index b108ccd0f637..06e74124e57e 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -17,6 +17,11 @@ #include "dp_mon.h" #include "debugfs_htt_stats.h" +#define ATH12K_2GHZ_MIN_CHAN_NUM 1 +#define ATH12K_2GHZ_MAX_CHAN_NUM 14 +#define ATH12K_5GHZ_MIN_CHAN_NUM 36 +#define ATH12K_5GHZ_MAX_CHAN_NUM 177 + static int ath12k_dp_rx_tid_delete_handler(struct ath12k_base *ab, struct ath12k_dp_rx_tid_rxq *rx_tid); @@ -1095,7 +1100,8 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k_pdev_dp *dp_pdev, static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, - struct hal_rx_desc_data *rx_info) + struct hal_rx_desc_data *rx_info, + enum ath12k_dp_rx_decap_type decap_type) { struct ieee80211_hdr *hdr; struct ethhdr *eth; @@ -1103,12 +1109,24 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, u8 sa[ETH_ALEN]; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ath12k_dp_rx_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}}; + struct ath12k_dp_rx_rfc1042_hdr *llc; eth = (struct ethhdr *)msdu->data; ether_addr_copy(da, eth->h_dest); ether_addr_copy(sa, eth->h_source); - rfc.snap_type = eth->h_proto; - skb_pull(msdu, sizeof(*eth)); + if (decap_type == DP_RX_DECAP_TYPE_8023) { + /* + * For 802.3 frames, eth->h_proto carries a length field, not + * an EtherType. The actual EtherType is in the LLC/SNAP header + * that follows the Ethernet header. + */ + llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + sizeof(*eth)); + rfc.snap_type = llc->snap_type; + skb_pull(msdu, sizeof(*eth) + sizeof(*llc)); + } else { + rfc.snap_type = eth->h_proto; + skb_pull(msdu, sizeof(*eth)); + } memcpy(skb_push(msdu, sizeof(rfc)), &rfc, sizeof(rfc)); ath12k_get_dot11_hdr_from_rx_desc(dp_pdev, msdu, rxcb, enctype, rx_info); @@ -1124,11 +1142,13 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info) + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer) { + enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type; struct ethhdr *ehdr; - switch (rx_info->decap_type) { + switch (decap_type) { case DP_RX_DECAP_TYPE_NATIVE_WIFI: ath12k_dp_rx_h_undecap_nwifi(dp_pdev, msdu, enctype, rx_info); break; @@ -1142,19 +1162,40 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu /* mac80211 allows fast path only for authorized STA */ if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) { ATH12K_SKB_RXCB(msdu)->is_eapol = true; - ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info); + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + + if (peer && !peer->use_4addr && + rx_info->is_from_ds && rx_info->is_to_ds) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); break; } /* PN for mcast packets will be validated in mac80211; * remove eth header and add 802.11 header. */ - if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) - ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info); + if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + + rx_info->rx_status->flag |= RX_FLAG_8023; break; case DP_RX_DECAP_TYPE_8023: - /* TODO: Handle undecap for these formats */ - break; + /* + * Note that ethernet decap format indicates that the decapped + * packet is either Ethernet 2 (DIX) or 802.3 (uses SNAP/LLC). + */ + if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + rx_info->rx_status->flag |= RX_FLAG_8023; } } EXPORT_SYMBOL(ath12k_dp_rx_h_undecap); @@ -1169,8 +1210,7 @@ ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *ms lockdep_assert_held(&dp->dp_lock); - if (rxcb->peer_id) - peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) return peer; @@ -1289,9 +1329,11 @@ void ath12k_dp_rx_h_ppdu(struct ath12k_pdev_dp *dp_pdev, center_freq <= ATH12K_MAX_6GHZ_FREQ) { rx_status->band = NL80211_BAND_6GHZ; rx_status->freq = center_freq; - } else if (channel_num >= 1 && channel_num <= 14) { + } else if (channel_num >= ATH12K_2GHZ_MIN_CHAN_NUM && + channel_num <= ATH12K_2GHZ_MAX_CHAN_NUM) { rx_status->band = NL80211_BAND_2GHZ; - } else if (channel_num >= 36 && channel_num <= 173) { + } else if (channel_num >= ATH12K_5GHZ_MIN_CHAN_NUM && + channel_num <= ATH12K_5GHZ_MAX_CHAN_NUM) { rx_status->band = NL80211_BAND_5GHZ; } @@ -1336,9 +1378,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc struct ath12k_dp_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ieee80211_rx_status *status = rx_info->rx_status; - u8 decap = rx_info->decap_type; bool is_mcbc = rxcb->is_mcbc; - bool is_eapol = rxcb->is_eapol; peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); @@ -1383,15 +1423,6 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc /* TODO: trace rx packet */ - /* PN for multicast packets are not validate in HW, - * so skip 802.3 rx path - * Also, fast_rx expects the STA to be authorized, hence - * eapol packets are sent in slow path. - */ - if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol && - !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) - rx_status->flag |= RX_FLAG_8023; - ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi); } EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 55a31e669b3b..0660db070e92 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -190,7 +190,8 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal, void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info); + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer); void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info); diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index bf4f7dbae866..21c551d8b248 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -738,7 +738,9 @@ struct hal_rx_desc_data { addr2_present:1, is_mcbc:1, seq_ctl_valid:1, - fc_valid:1; + fc_valid:1, + is_to_ds:1, + is_from_ds:1; u16 msdu_len; u16 peer_id; u16 seq_no; diff --git a/drivers/net/wireless/ath/ath12k/hif.h b/drivers/net/wireless/ath/ath12k/hif.h index e8840fab6061..4de8667690e9 100644 --- a/drivers/net/wireless/ath/ath12k/hif.h +++ b/drivers/net/wireless/ath/ath12k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_HIF_H diff --git a/drivers/net/wireless/ath/ath12k/htc.h b/drivers/net/wireless/ath/ath12k/htc.h index 7e3dccc7cc14..05b3e593542d 100644 --- a/drivers/net/wireless/ath/ath12k/htc.h +++ b/drivers/net/wireless/ath/ath12k/htc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_HTC_H @@ -301,7 +301,7 @@ struct ath12k_htc { u8 wmi_ep_count; }; -int ath12k_htc_init(struct ath12k_base *ar); +int ath12k_htc_init(struct ath12k_base *ab); int ath12k_htc_wait_target(struct ath12k_htc *htc); int ath12k_htc_start(struct ath12k_htc *htc); int ath12k_htc_connect_service(struct ath12k_htc *htc, @@ -309,8 +309,8 @@ int ath12k_htc_connect_service(struct ath12k_htc *htc, struct ath12k_htc_svc_conn_resp *conn_resp); int ath12k_htc_send(struct ath12k_htc *htc, enum ath12k_htc_ep_id eid, struct sk_buff *packet); -struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ar, int size); -void ath12k_htc_rx_completion_handler(struct ath12k_base *ar, +struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ab, int size); +void ath12k_htc_rx_completion_handler(struct ath12k_base *ab, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index a9888e0521a1..d135b2936378 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -19,14 +19,28 @@ #define TARGET_NUM_VDEVS(ab) ((ab)->profile_param->num_vdevs) /* Max num of stations for Single Radio mode */ -#define TARGET_NUM_STATIONS_SINGLE(ab) ((ab)->profile_param->max_client_single) +#define TARGET_NUM_STATIONS_SINGLE(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_single, \ + _ab->profile_param->max_client_single); \ +}) /* Max num of stations for DBS */ -#define TARGET_NUM_STATIONS_DBS(ab) ((ab)->profile_param->max_client_dbs) +#define TARGET_NUM_STATIONS_DBS(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs, \ + _ab->profile_param->max_client_dbs); \ +}) /* Max num of stations for DBS_SBS */ #define TARGET_NUM_STATIONS_DBS_SBS(ab) \ - ((ab)->profile_param->max_client_dbs_sbs) +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs_sbs, \ + _ab->profile_param->max_client_dbs_sbs); \ +}) #define TARGET_NUM_STATIONS(ab, x) TARGET_NUM_STATIONS_##x(ab) @@ -213,6 +227,11 @@ struct ath12k_hw_params { /* setup REO queue, frag etc only for primary link peer */ bool dp_primary_link_only:1; + struct { + u32 max_client_single; + u32 max_client_dbs; + u32 max_client_dbs_sbs; + } client; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2cff9485c95a..af354bef5c0d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -51,6 +51,9 @@ .max_power = 30, \ } +#define ATH12K_5_9_GHZ_MIN_FREQ 5845 +#define ATH12K_5_9_GHZ_MAX_FREQ 5885 + static const struct ieee80211_channel ath12k_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), @@ -96,6 +99,7 @@ static const struct ieee80211_channel ath12k_5ghz_channels[] = { CHAN5G(165, 5825, 0), CHAN5G(169, 5845, 0), CHAN5G(173, 5865, 0), + CHAN5G(177, 5885, 0), }; static const struct ieee80211_channel ath12k_6ghz_channels[] = { @@ -5615,12 +5619,14 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, n_channels); if (!arg) { ret = -ENOMEM; goto exit; } + arg->num_chan = n_channels; + ath12k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH12K_SCAN_ID; @@ -5642,18 +5648,8 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw, arg->scan_f_passive = 1; } - if (n_channels) { - arg->num_chan = n_channels; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); - if (!arg->chan_list) { - ret = -ENOMEM; - goto exit; - } - - for (i = 0; i < arg->num_chan; i++) - arg->chan_list[i] = chan_list[i]->center_freq; - } + for (i = 0; i < arg->num_chan; i++) + arg->chan_list[i] = chan_list[i]->center_freq; ret = ath12k_start_scan(ar, arg); if (ret) { @@ -5678,7 +5674,6 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw, exit: if (arg) { - kfree(arg->chan_list); kfree(arg->extraie.ptr); kfree(arg); } @@ -6576,6 +6571,79 @@ static int ath12k_mac_station_disassoc(struct ath12k *ar, return 0; } +static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahsta) +{ + struct ath12k_dp_link_peer *peer; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + struct ath12k_vif *ahvif; + struct ath12k_dp *dp; + unsigned long links; + struct ath12k *ar; + u8 link_id; + int ret; + + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arsta = wiphy_dereference(wiphy, ahsta->link[link_id]); + if (!arsta) + continue; + + arvif = arsta->arvif; + ahvif = arvif->ahvif; + ar = arvif->ar; + + if (arvif->set_wds_vdev_param) + goto skip_nawds; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "setting USE_4ADDR for peer %pM\n", arsta->addr); + + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME); + if (ret) { + ath12k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n", + arsta->addr, ret); + return ret; + } + + if (ahvif->dp_vif.tx_encap_type != ATH12K_HW_TXRX_ETHERNET) + goto skip_nawds; + + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WDS_EXT_ENABLE); + if (ret) { + ath12k_warn(ar->ab, "failed to set vdev %d nawds parameter: %d\n", + arvif->vdev_id, ret); + return ret; + } + + arvif->nawds_enabled = true; + +skip_nawds: + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id, + arsta->addr); + if (peer && peer->dp_peer) { + peer->dp_peer->ucast_ra_only = true; + peer->dp_peer->use_4addr = true; + } else { + spin_unlock_bh(&dp->dp_lock); + ath12k_warn(ar->ab, "failed to find DP peer for %pM\n", + arsta->addr); + return -ENOENT; + } + + spin_unlock_bh(&dp->dp_lock); + } + + return 0; +} + static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk) { struct ieee80211_link_sta *link_sta; @@ -7870,6 +7938,28 @@ int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ath12k_mac_op_sta_set_txpwr); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled) +{ + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + + lockdep_assert_wiphy(hw->wiphy); + + /* + * 4-address mode disabled option is available only for station + * interface from mac80211, and we have wds_vdev_param for station + * interface and target will not allow to disable the wds_vdev_param + * during run time. So, add support only for enable case, for + * disable case station interface needs to be reconnect. + */ + if (enabled && !ahsta->enable_4addr) { + if (!ath12k_mac_sta_set_4addr(hw->wiphy, ahsta)) + ahsta->enable_4addr = true; + } +} +EXPORT_SYMBOL(ath12k_mac_op_sta_set_4addr); + void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, @@ -8054,16 +8144,16 @@ int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, continue; arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arvif || !arvif->is_created) + continue; - if (!arvif || !arsta) { + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arsta) { ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); continue; } ar = arvif->ar; - if (!ar) - continue; ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) { @@ -8399,6 +8489,10 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask, vht_cap.vht_supported = 1; vht_cap.cap = ar->pdev->cap.vht_cap; + if (ar->pdev->cap.nss_ratio_enabled) + vht_cap.vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); + ath12k_set_vht_txbf_cap(ar, &vht_cap.cap); /* 80P80 is not supported */ @@ -9650,6 +9744,10 @@ static int ath12k_mac_start(struct ath12k *ar) ar->allocated_vdev_map = 0; ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; + spin_lock_bh(&ar->data_lock); + ar->incumbent_signal_interference.handling_in_progress = false; + spin_unlock_bh(&ar->data_lock); + /* Configure monitor status ring with default rx_filter to get rx status * such as rssi, rx_duration. */ @@ -9677,6 +9775,12 @@ static int ath12k_mac_start(struct ath12k *ar) } } + ret = ath12k_thermal_throttling_config_default(ar); + if (ret) { + ath12k_err(ab, "failed to set thermal throttle: %d\n", ret); + goto err; + } + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], &ab->pdevs[ar->pdev_idx]); @@ -9857,6 +9961,10 @@ static void ath12k_mac_stop(struct ath12k *ar) synchronize_rcu(); atomic_set(&ar->num_pending_mgmt_tx, 0); + + spin_lock_bh(&ar->data_lock); + ar->incumbent_signal_interference.handling_in_progress = false; + spin_unlock_bh(&ar->data_lock); } void ath12k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) @@ -10011,12 +10119,14 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif) vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED | IEEE80211_OFFLOAD_DECAP_ENABLED); - if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) + if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_ETHERNET; - else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + } else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_RAW; - else + } else { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI; + } ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param_id, ahvif->dp_vif.tx_encap_type); @@ -10226,6 +10336,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) struct ieee80211_hw *hw = ah->hw; struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); struct ath12k_wmi_vdev_create_arg vdev_arg = {}; struct ath12k_wmi_peer_create_arg peer_param = {}; struct ieee80211_bss_conf *link_conf = NULL; @@ -10293,7 +10404,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ab, "failed to create WMI vdev %d: %d\n", arvif->vdev_id, ret); - return ret; + goto err; } ar->num_created_vdevs++; @@ -10394,6 +10505,26 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) goto err_peer_del; } + /* + * There could be race condition in firmware for the station + * interface between enabling 4-address peer WMI param and + * sending 4-address frame (NULL or EAPOL via TCL). + * Make the station as WDS while bringup itself + * to avoid the race condition + */ + if (vif->type == NL80211_IFTYPE_STATION && + (wdev && wdev->use_4addr)) { + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_WDS, + 1); + if (ret) { + ath12k_warn(ar->ab, "failed to set WDS vdev param: %d\n", + ret); + goto err_peer_del; + } + arvif->set_wds_vdev_param = true; + } + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && ahvif->vdev_type == WMI_VDEV_TYPE_STA && ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { @@ -10440,13 +10571,13 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", arvif->vdev_id, arvif->bssid); - goto err; + goto err_dp_peer_del; } ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arvif->bssid); if (ret) - goto err_vdev_del; + goto err_dp_peer_del; ar->num_peers--; } @@ -10463,8 +10594,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; - arvif->is_created = false; - arvif->ar = NULL; ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); @@ -10473,6 +10602,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) spin_unlock_bh(&ar->data_lock); err: + arvif->is_created = false; arvif->ar = NULL; return ret; } @@ -11443,8 +11573,10 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, struct ieee80211_vif_chanctx_switch *vifs, int n_vifs) { + struct ath12k_incumbent_signal_interference *incumbent; struct ath12k_wmi_vdev_up_params params = {}; struct ieee80211_bss_conf *link_conf; + struct cfg80211_chan_def *chandef; struct ath12k_base *ab = ar->ab; struct ath12k_link_vif *arvif; struct ieee80211_vif *vif; @@ -11469,6 +11601,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, continue; } + if (WARN_ON(!arvif)) + continue; + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", arvif->vdev_id, @@ -11556,6 +11691,42 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, if (!ath12k_mac_monitor_stop(ar)) ath12k_mac_monitor_start(ar); } + + incumbent = &ar->incumbent_signal_interference; + spin_lock_bh(&ar->data_lock); + if (incumbent->handling_in_progress) { + chandef = &vifs[0].new_ctx->def; + if (incumbent->chan_bw_interference_bitmap & + ATH12K_WMI_DCS_SEG_PRI20) { + if (incumbent->center_freq != + chandef->chan->center_freq) { + incumbent->chan_bw_interference_bitmap = 0; + incumbent->handling_in_progress = false; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "incumbent signal interference chan switch completed\n"); + } else { + ath12k_warn(ab, + "incumbent signal interference chan switch not done, freq %u\n", + incumbent->center_freq); + } + } else { + if (incumbent->center_freq != + chandef->chan->center_freq || + incumbent->width != chandef->width) { + incumbent->chan_bw_interference_bitmap = 0; + incumbent->handling_in_progress = false; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "Bandwidth/channel change due to incumbent signal interference completed\n"); + } else { + ath12k_warn(ab, "Bandwidth/channel change due to incumbent sig intf not done intf_freq %u chan_freq %u intf_width %u chan_width %u\n", + incumbent->center_freq, + chandef->chan->center_freq, + incumbent->width, + chandef->width); + } + } + } + spin_unlock_bh(&ar->data_lock); } static void @@ -12224,23 +12395,85 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct ath12k *ar; + struct ath12k *curr_ar, *new_ar, *group_ar; + struct ieee80211_vif_chanctx_switch *v; + int i, j, count = 0; lockdep_assert_wiphy(hw->wiphy); - ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx); - if (!ar) - return -EINVAL; + if (n_vifs == 0) + return 0; - /* Switching channels across radio is not allowed */ - if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx)) - return -EINVAL; + struct ath12k **ar_map __free(kfree) = kzalloc_objs(*ar_map, n_vifs); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "mac chanctx switch n_vifs %d mode %d\n", - n_vifs, mode); - ath12k_mac_update_vif_chan(ar, vifs, n_vifs); + if (!ar_map) + return -ENOMEM; + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + + if (v->old_ctx->def.chan->band != v->new_ctx->def.chan->band) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac chanctx switch band change not supported\n"); + return -EOPNOTSUPP; + } + + curr_ar = ath12k_get_ar_by_ctx(hw, v->old_ctx); + new_ar = ath12k_get_ar_by_ctx(hw, v->new_ctx); + + if (!curr_ar || !new_ar) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "unable to determine device for the passed channel ctx\n"); + ath12k_generic_dbg(ATH12K_DBG_MAC, + "Old freq %d MHz (device %s) to new freq %d MHz (device %s)\n", + v->old_ctx->def.chan->center_freq, + curr_ar ? "valid" : "invalid", + v->new_ctx->def.chan->center_freq, + new_ar ? "valid" : "invalid"); + return -EINVAL; + } + + /* Switching a vif between two radios is not allowed */ + if (curr_ar != new_ar) { + ath12k_dbg(curr_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch to another radio not supported\n"); + return -EOPNOTSUPP; + } + + ar_map[i] = curr_ar; + } + + /* Group vifs by radio (ar) and process each group independently. */ + bool *processed __free(kfree) = kzalloc_objs(*processed, n_vifs); + + if (!processed) + return -ENOMEM; + + struct ieee80211_vif_chanctx_switch *group_vifs __free(kfree) = + kzalloc_objs(*group_vifs, n_vifs); + + if (!group_vifs) + return -ENOMEM; + + for (i = 0; i < n_vifs; i++) { + if (processed[i]) + continue; + + group_ar = ar_map[i]; + + count = 0; + for (j = 0; j < n_vifs; j++) { + if (!processed[j] && ar_map[j] == group_ar) { + group_vifs[count++] = vifs[j]; + processed[j] = true; + } + } + + ath12k_dbg(group_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch n_vifs %d mode %d\n", + count, mode); + ath12k_mac_update_vif_chan(group_ar, group_vifs, count); + } return 0; } EXPORT_SYMBOL(ath12k_mac_op_switch_vif_chanctx); @@ -13739,19 +13972,13 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, scan_time_msec = hw->wiphy->max_remain_on_channel_duration * 2; struct ath12k_wmi_scan_req_arg *arg __free(kfree) = - kzalloc_obj(*arg); + kzalloc_flex(*arg, chan_list, 1); if (!arg) return -ENOMEM; - ath12k_wmi_start_scan_init(ar, arg); arg->num_chan = 1; + ath12k_wmi_start_scan_init(ar, arg); - u32 *chan_list __free(kfree) = kcalloc(arg->num_chan, sizeof(*chan_list), - GFP_KERNEL); - if (!chan_list) - return -ENOMEM; - - arg->chan_list = chan_list; arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH12K_SCAN_ID; arg->chan_list[0] = chan->center_freq; @@ -13904,6 +14131,26 @@ static int ath12k_mac_update_band(struct ath12k *ar, return 0; } +static void ath12k_mac_update_5_9_ghz_ch_list(struct ath12k *ar, + struct ieee80211_supported_band *band) +{ + int i; + + if (test_bit(WMI_TLV_SERVICE_5_9GHZ_SUPPORT, + ar->ab->wmi_ab.svc_map)) + return; + + guard(spinlock_bh)(&ar->ab->base_lock); + if (ar->ab->dfs_region != ATH12K_DFS_REG_FCC) + return; + + for (i = 0; i < band->n_channels; i++) { + if (band->channels[i].center_freq >= ATH12K_5_9_GHZ_MIN_FREQ && + band->channels[i].center_freq <= ATH12K_5_9_GHZ_MAX_FREQ) + band->channels[i].flags |= IEEE80211_CHAN_DISABLED; + } +} + static int ath12k_mac_setup_channels_rates(struct ath12k *ar, u32 supported_bands, struct ieee80211_supported_band *bands[]) @@ -14037,6 +14284,8 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->n_bitrates = ath12k_a_rates_size; band->bitrates = ath12k_a_rates; + ath12k_mac_update_5_9_ghz_ch_list(ar, band); + if (ab->hw_params->single_pdev_only) { phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5GHZ_CAP); reg_cap = &ab->hal_reg_cap[phy_id]; @@ -14465,6 +14714,8 @@ static int ath12k_mac_setup_register(struct ath12k *ar, ar->rssi_info.temp_offset = 0; ar->rssi_info.noise_floor = ar->rssi_info.min_nf_dbm + ar->rssi_info.temp_offset; + ath12k_thermal_init_configs(ar); + return 0; } @@ -14817,6 +15068,7 @@ static void ath12k_mac_setup(struct ath12k *ar) init_completion(&ar->completed_11d_scan); init_completion(&ar->regd_update_completed); init_completion(&ar->thermal.wmi_sync); + mutex_init(&ar->thermal.lock); ar->thermal.temperature = 0; ar->thermal.hwmon_dev = NULL; diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 7b50c5976384..aba98afd4365 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -255,6 +255,9 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c index 19ebcd1d8eb2..ae9a8be17be4 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.c +++ b/drivers/net/wireless/ath/ath12k/p2p.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath12k/p2p.h b/drivers/net/wireless/ath/ath12k/p2p.h index 03ee877e6d6b..3041f88508db 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.h +++ b/drivers/net/wireless/ath/ath12k/p2p.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_P2P_H diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 375277ca2b89..d9a22d6afbb0 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1639,7 +1639,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ret = ab_pci->device_family_ops->arch_init(ab); if (ret) { ath12k_err(ab, "PCI arch_init failed %d\n", ret); - goto err_pci_msi_free; + goto err_free_irq; } ret = ath12k_core_init(ab); diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 2e875176baaa..c222bdaa333c 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -218,7 +218,13 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ahsta = ath12k_sta_to_ahsta(sta); arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, ahsta->link[link_id]); - + /* TODO: Split DP related field usage to DP peer structure */ + arsta->tcl_metadata = u16_encode_bits(0, HTT_TCL_META_DATA_TYPE) | + u16_encode_bits(peer->peer_id, + HTT_TCL_META_DATA_PEER_ID) | + u16_encode_bits(0, HTT_TCL_META_DATA_VALID_HTT); + arsta->ast_hash = peer->ast_hash; + arsta->ast_idx = peer->hw_peer_id; peer->link_id = arsta->link_id; /* Fill ML info into created peer */ @@ -249,6 +255,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ar->hw_link_id); } + if (vif->type == NL80211_IFTYPE_AP && peer->dp_peer) + peer->dp_peer->ucast_ra_only = true; + return ret; } diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 8c5dacf227da..fd762b5d7bb5 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -4061,7 +4061,7 @@ int ath12k_qmi_init_service(struct ath12k_base *ab) spin_lock_init(&ab->qmi.event_lock); INIT_WORK(&ab->qmi.event_work, ath12k_qmi_driver_event_work); - ret = qmi_add_lookup(&ab->qmi.handle, ATH12K_QMI_WLFW_SERVICE_ID_V01, + ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW, ATH12K_QMI_WLFW_SERVICE_VERS_V01, ab->qmi.service_ins_id); if (ret < 0) { diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index b5a4a01391cb..2a63e214eb42 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -15,7 +15,6 @@ #define ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE 64 #define ATH12K_QMI_CALDB_ADDRESS 0x4BA00000 #define ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 -#define ATH12K_QMI_WLFW_SERVICE_ID_V01 0x45 #define ATH12K_QMI_WLFW_SERVICE_VERS_V01 0x01 #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1 diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index d784d9ec5061..89abf2e87ad1 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index da5128b8c97f..7c37c0d2a6f9 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_REG_H diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h index ef6ab21d19b8..342ed0a7fa98 100644 --- a/drivers/net/wireless/ath/ath12k/testmode.h +++ b/drivers/net/wireless/ath/ath12k/testmode.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c index a764d2112a3c..97fc49c40ac1 100644 --- a/drivers/net/wireless/ath/ath12k/thermal.c +++ b/drivers/net/wireless/ath/ath12k/thermal.c @@ -12,6 +12,137 @@ #include "core.h" #include "debug.h" +static const struct ath12k_wmi_tt_level_config_param +tt_level_configs[ATH12K_TT_CFG_IDX_MAX][ENHANCED_THERMAL_LEVELS] = { + [ATH12K_TT_CFG_IDX_IPA] = { + [0] = { .tmplwm = -100, .tmphwm = 115, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [1] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 0, + .pout_reduction_db = 12 }, + [2] = { .tmplwm = 115, .tmphwm = 125, .dcoffpercent = 50, + .pout_reduction_db = 12 }, + [3] = { .tmplwm = 120, .tmphwm = 130, .dcoffpercent = 90, + .pout_reduction_db = 12 }, + [4] = { .tmplwm = 125, .tmphwm = 130, .dcoffpercent = 100, + .pout_reduction_db = 12 }, + }, + [ATH12K_TT_CFG_IDX_XFEM] = { + [0] = { .tmplwm = -100, .tmphwm = 105, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [1] = { .tmplwm = 100, .tmphwm = 110, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [2] = { .tmplwm = 105, .tmphwm = 115, .dcoffpercent = 50, + .pout_reduction_db = 0 }, + [3] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 90, + .pout_reduction_db = 0 }, + [4] = { .tmplwm = 115, .tmphwm = 120, .dcoffpercent = 100, + .pout_reduction_db = 0 }, + }, +}; + +static enum ath12k_thermal_cfg_idx ath12k_thermal_cfg_index(struct ath12k *ar) +{ + if (test_bit(WMI_TLV_SERVICE_IS_TARGET_IPA, ar->ab->wmi_ab.svc_map)) + return ATH12K_TT_CFG_IDX_IPA; + + return ATH12K_TT_CFG_IDX_XFEM; +} + +int ath12k_thermal_throttling_config_default(struct ath12k *ar) +{ + struct ath12k_wmi_thermal_mitigation_arg param = {}; + int ret; + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_5_LEVELS, ar->ab->wmi_ab.svc_map)) + param.num_levels = ENHANCED_THERMAL_LEVELS; + else + param.num_levels = THERMAL_LEVELS; + + param.levelconf = ar->thermal.tt_level_configs; + + ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m); + if (ret) + ath12k_warn(ar->ab, + "failed to send thermal mitigation cmd for default config: %d\n", + ret); + return ret; +} + +void ath12k_thermal_init_configs(struct ath12k *ar) +{ + enum ath12k_thermal_cfg_idx cfg_idx; + + cfg_idx = ath12k_thermal_cfg_index(ar); + ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0]; +} + +static int +ath12k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ATH12K_THERMAL_THROTTLE_MAX; + + return 0; +} + +static int +ath12k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct ath12k *ar = cdev->devdata; + + mutex_lock(&ar->thermal.lock); + *state = ar->thermal.throttle_state; + mutex_unlock(&ar->thermal.lock); + + return 0; +} + +int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state) +{ + struct ath12k_wmi_thermal_mitigation_arg param = {}; + struct ath12k_wmi_tt_level_config_param cfg = {}; + int ret; + + param.num_levels = 1; + cfg.dcoffpercent = throttle_state; + param.levelconf = &cfg; + + ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m); + if (ret) + ath12k_warn(ar->ab, "failed to send thermal mitigation cmd: %d\n", + ret); + + return ret; +} + +static int +ath12k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long throttle_state) +{ + struct ath12k *ar = cdev->devdata; + + if (throttle_state > ATH12K_THERMAL_THROTTLE_MAX) + return -EINVAL; + + scoped_guard(mutex, &ar->thermal.lock) { + if (ar->thermal.throttle_state == throttle_state) + return 0; + ar->thermal.throttle_state = throttle_state; + } + + if (throttle_state == 0) + return ath12k_thermal_throttling_config_default(ar); + + return ath12k_thermal_set_throttling(ar, throttle_state); +} + +static const struct thermal_cooling_device_ops ath12k_thermal_ops = { + .get_max_state = ath12k_thermal_get_max_throttle_state, + .get_cur_state = ath12k_thermal_get_cur_throttle_state, + .set_cur_state = ath12k_thermal_set_cur_throttle_state, +}; + static ssize_t ath12k_thermal_temp_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -66,59 +197,108 @@ static struct attribute *ath12k_hwmon_attrs[] = { }; ATTRIBUTE_GROUPS(ath12k_hwmon); +static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i) +{ + char pdev_name[20]; + struct ath12k *ar; + int ret; + + ar = ab->pdevs[i].ar; + if (!ar) + return 0; + + ar->thermal.cdev = + thermal_cooling_device_register("ath12k_thermal", ar, + &ath12k_thermal_ops); + if (IS_ERR(ar->thermal.cdev)) { + ret = PTR_ERR(ar->thermal.cdev); + ar->thermal.cdev = NULL; + ath12k_err(ar->ab, "failed to register cooling device: %d\n", + ret); + return ret; + } + + scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u", + ar->hw_link_id); + + ret = sysfs_create_link(&ar->ah->hw->wiphy->dev.kobj, + &ar->thermal.cdev->device.kobj, pdev_name); + if (ret) { + ath12k_err(ab, "failed to create cooling device symlink: %d\n", + ret); + goto unregister_cdev; + } + + ar->thermal.hwmon_dev = + hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev, + "ath12k_hwmon", ar, + ath12k_hwmon_groups); + if (IS_ERR(ar->thermal.hwmon_dev)) { + ret = PTR_ERR(ar->thermal.hwmon_dev); + ar->thermal.hwmon_dev = NULL; + ath12k_err(ar->ab, "failed to register hwmon device: %d\n", + ret); + goto remove_sysfs; + } + + return 0; + +remove_sysfs: + sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name); +unregister_cdev: + thermal_cooling_device_unregister(ar->thermal.cdev); + ar->thermal.cdev = NULL; + return ret; +} + +static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i) +{ + char pdev_name[20]; + struct ath12k *ar; + + ar = ab->pdevs[i].ar; + if (!ar) + return; + + hwmon_device_unregister(ar->thermal.hwmon_dev); + ar->thermal.hwmon_dev = NULL; + + scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u", + ar->hw_link_id); + sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name); + + thermal_cooling_device_unregister(ar->thermal.cdev); + ar->thermal.cdev = NULL; +} + int ath12k_thermal_register(struct ath12k_base *ab) { - struct ath12k *ar; - int i, j, ret; + int i, ret; if (!IS_REACHABLE(CONFIG_HWMON)) return 0; for (i = 0; i < ab->num_radios; i++) { - ar = ab->pdevs[i].ar; - if (!ar) - continue; - - ar->thermal.hwmon_dev = - hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev, - "ath12k_hwmon", ar, - ath12k_hwmon_groups); - if (IS_ERR(ar->thermal.hwmon_dev)) { - ret = PTR_ERR(ar->thermal.hwmon_dev); - ar->thermal.hwmon_dev = NULL; - ath12k_err(ar->ab, "failed to register hwmon device: %d\n", - ret); - for (j = i - 1; j >= 0; j--) { - ar = ab->pdevs[j].ar; - if (!ar) - continue; - - hwmon_device_unregister(ar->thermal.hwmon_dev); - ar->thermal.hwmon_dev = NULL; - } - return ret; - } + ret = ath12k_thermal_setup_radio(ab, i); + if (ret) + goto out; } return 0; +out: + for (i--; i >= 0; i--) + ath12k_thermal_cleanup_radio(ab, i); + + return ret; } void ath12k_thermal_unregister(struct ath12k_base *ab) { - struct ath12k *ar; int i; if (!IS_REACHABLE(CONFIG_HWMON)) return; - for (i = 0; i < ab->num_radios; i++) { - ar = ab->pdevs[i].ar; - if (!ar) - continue; - - if (ar->thermal.hwmon_dev) { - hwmon_device_unregister(ar->thermal.hwmon_dev); - ar->thermal.hwmon_dev = NULL; - } - } + for (i = 0; i < ab->num_radios; i++) + ath12k_thermal_cleanup_radio(ab, i); } diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h index 9d84056188e1..30e7b0880e05 100644 --- a/drivers/net/wireless/ath/ath12k/thermal.h +++ b/drivers/net/wireless/ath/ath12k/thermal.h @@ -7,20 +7,41 @@ #ifndef _ATH12K_THERMAL_ #define _ATH12K_THERMAL_ +#include + #define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ) +#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100 +#define ATH12K_THERMAL_THROTTLE_MAX 100 + +enum ath12k_thermal_cfg_idx { + /* Internal Power Amplifier Device */ + ATH12K_TT_CFG_IDX_IPA, + /* External Power Amplifier Device or External Front End Module */ + ATH12K_TT_CFG_IDX_XFEM, + ATH12K_TT_CFG_IDX_MAX, +}; + struct ath12k_thermal { struct completion wmi_sync; /* temperature value in Celsius degree protected by data_lock. */ int temperature; struct device *hwmon_dev; + const struct ath12k_wmi_tt_level_config_param *tt_level_configs; + struct thermal_cooling_device *cdev; + /* Serialize thermal operations and hwmon reads */ + struct mutex lock; + u32 throttle_state; }; #if IS_REACHABLE(CONFIG_THERMAL) int ath12k_thermal_register(struct ath12k_base *ab); void ath12k_thermal_unregister(struct ath12k_base *ab); void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature); +int ath12k_thermal_throttling_config_default(struct ath12k *ar); +void ath12k_thermal_init_configs(struct ath12k *ar); +int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state); #else static inline int ath12k_thermal_register(struct ath12k_base *ab) { @@ -36,5 +57,19 @@ static inline void ath12k_thermal_event_temperature(struct ath12k *ar, { } +static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar) +{ + return 0; +} + +static inline void ath12k_thermal_init_configs(struct ath12k *ar) +{ +} + +static inline int ath12k_thermal_set_throttling(struct ath12k *ar, + u32 throttle_state) +{ + return 0; +} #endif #endif /* _ATH12K_THERMAL_ */ diff --git a/drivers/net/wireless/ath/ath12k/trace.c b/drivers/net/wireless/ath/ath12k/trace.c index 0d0edf4204b7..772a5a7db98b 100644 --- a/drivers/net/wireless/ath/ath12k/trace.c +++ b/drivers/net/wireless/ath/ath12k/trace.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath12k/trace.h b/drivers/net/wireless/ath/ath12k/trace.h index 253c67accb0e..053d681302f0 100644 --- a/drivers/net/wireless/ath/ath12k/trace.h +++ b/drivers/net/wireless/ath/ath12k/trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 77f5d23be78d..7dd4a49d64d5 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -1779,8 +1779,7 @@ ath12k_wifi7_dp_mon_rx_parse_status_tlv(struct ath12k_pdev_dp *dp_pdev, info[1] = __le32_to_cpu(mpdu_start->info1); peer_id = u32_get_bits(info[1], HAL_RX_MPDU_START_INFO1_PEERID); - if (peer_id) - ppdu_info->peer_id = peer_id; + ppdu_info->peer_id = peer_id; ppdu_info->mpdu_len += u32_get_bits(info[1], HAL_RX_MPDU_START_INFO2_MPDU_LEN); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 945680b3ebdf..cb9dd8cb28b6 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -323,9 +323,9 @@ static void ath12k_wifi7_dp_rx_h_csum_offload(struct sk_buff *msdu, CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, - struct sk_buff *msdu, - struct hal_rx_desc_data *rx_info) +static int ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) { struct ath12k_skb_rxcb *rxcb; enum hal_encrypt_type enctype; @@ -347,6 +347,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) { + /* + * Drop the 3-address multicast packet from 4-address + * peer To avoid receiving the duplicate multicast packet + * Specifically from AP interface in 3-address format + */ + if (rxcb->is_mcbc && + rx_info->decap_type == DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + if (peer->use_4addr && + !(rx_info->is_from_ds && rx_info->is_to_ds)) + return -EINVAL; + } + /* resetting mcbc bit because mcbc packets are unicast * packets only for AP as STA sends unicast packets. */ @@ -387,15 +399,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_wifi7_dp_rx_h_csum_offload(msdu, rx_info); - ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info); + ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info, + peer); if (!is_decrypted || rx_info->is_mcbc) - return; + return 0; if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) { hdr = (void *)msdu->data; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } + + return 0; } static int ath12k_wifi7_dp_rx_msdu_coalesce(struct ath12k_hal *hal, @@ -554,7 +569,9 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + goto free_out; rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; @@ -1028,12 +1045,14 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, skb_pull(msdu, hal_rx_desc_sz); if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, - rx_info))) + rx_info))) { + dev_kfree_skb_any(msdu); return -EINVAL; + } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, - rx_info); + rx_info, NULL); ieee80211_rx(ath12k_pdev_dp_to_hw(dp_pdev), msdu); return -EINVAL; } @@ -1588,6 +1607,7 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, u8 l3pad_bytes = rx_info->l3_pad_bytes; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + int ret; if (!rxcb->is_frag && ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE)) { /* First buffer will be freed by the caller, so deduct it's length */ @@ -1632,7 +1652,9 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, return -EINVAL; ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return ret; rxcb->tid = rx_info->tid; @@ -1678,7 +1700,57 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, RX_FLAG_DECRYPTED); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, false, - rx_info); + rx_info, NULL); + return false; +} + +static bool ath12k_wifi7_dp_rx_h_unauth_wds_err(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) +{ + struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); + struct ath12k_dp *dp = dp_pdev->dp; + u32 hdr_len, hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + u8 l3pad_bytes = rx_info->l3_pad_bytes; + struct ath12k_dp_rx_rfc1042_hdr *llc; + u16 msdu_len = rx_info->msdu_len; + struct ath12k_dp_peer *peer; + struct ieee80211_hdr *hdr; + int ret; + + guard(rcu)(); + peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + if (!peer) { + ath12k_dbg(dp->ab, ATH12K_DBG_DATA, + "failed to find the peer to process unauth wds err handling peer_id %d\n", + rxcb->peer_id); + return true; + } + + if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) + return true; + + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); + skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, + rx_info))) + return true; + + ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); + + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return true; + + rxcb->tid = rx_info->tid; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + hdr_len); + if (llc->snap_type != cpu_to_be16(ETH_P_PAE)) + return true; + return false; } @@ -1693,6 +1765,9 @@ static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, dp->device_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR: + drop = ath12k_wifi7_dp_rx_h_unauth_wds_err(dp_pdev, msdu, rx_info); + break; case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR: case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: if (rx_info->err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 629084aa36d8..d2749de44553 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -57,10 +57,12 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb) return 0; } +#define ATH12K_AST_HASH_MASK 0xF + /* TODO: Remove the export once this file is built with wifi7 ko */ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast) + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast) { struct ath12k_dp *dp = dp_pdev->dp; struct ath12k_hal *hal = dp->hal; @@ -78,6 +80,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a struct ath12k_dp_vif *dp_vif = &ahvif->dp_vif; struct ath12k_dp_link_vif *dp_link_vif; struct dp_tx_ring *tx_ring; + struct ethhdr *eth = NULL; u8 pool_id; u8 hal_ring_id; int ret; @@ -96,6 +99,9 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a !ieee80211_is_data(hdr->frame_control)) return -EOPNOTSUPP; + if (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) + eth = (struct ethhdr *)skb->data; + pool_id = skb_get_queue_mapping(skb) & (ATH12K_HW_MAX_QUEUES - 1); /* Let the default ring selection be based on current processor @@ -124,6 +130,19 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.bank_id = dp_link_vif->bank_id; ti.meta_data_flags = dp_link_vif->tcl_metadata; + ti.bss_ast_hash = dp_link_vif->ast_hash; + ti.bss_ast_idx = dp_link_vif->ast_idx; + + if (eth && is_multicast_ether_addr(eth->h_dest) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.bss_ast_hash = arsta->ast_hash & ATH12K_AST_HASH_MASK; + ti.bss_ast_idx = arsta->ast_idx; + ti.lookup_override = true; + } else if (!eth && ieee80211_has_a4(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr3) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + } if (dp_vif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) { @@ -140,7 +159,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a msdu_ext_desc = true; } - if (gsn_valid) { + if (gsn_valid && !ti.lookup_override) { /* Reset and Initialize meta_data_flags with Global Sequence * Number (GSN) info. */ @@ -148,6 +167,14 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM, HTT_TCL_META_DATA_TYPE) | u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM); + + /* + * Since NAWDS enabled for this vdev firmware expects + * this flag to be set for sending 3-address multicast frame. + */ + ti.meta_data_flags |= + u32_encode_bits(arvif->nawds_enabled, + HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED); } ti.encap_type = ath12k_dp_tx_get_encap_type(ab, skb); @@ -158,11 +185,13 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.lmac_id = dp_link_vif->lmac_id; ti.vdev_id = dp_link_vif->vdev_id; - if (gsn_valid) - ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; - ti.bss_ast_hash = dp_link_vif->ast_hash; - ti.bss_ast_idx = dp_link_vif->ast_idx; + if (gsn_valid && !ti.lookup_override) + ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + else if (arvif->nawds_enabled && is_mcast && !ti.lookup_override) + ti.meta_data_flags |= + u32_encode_bits(1, HTT_TCL_META_DATA_HOST_INSPECTED_MISSION); + ti.dscp_tid_tbl_idx = 0; if (skb->ip_summed == CHECKSUM_PARTIAL && diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h index 24cf7972d41b..86bc813878c0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h @@ -8,8 +8,8 @@ #define ATH12K_DP_TX_WIFI7_H int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast); + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast); void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id); u32 ath12k_wifi7_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_link_vif *arvif); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c index 1eefb931a853..8cebb229ebed 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c @@ -184,6 +184,20 @@ static u8 ath12k_hal_rx_desc_get_l3_pad_bytes_qcc2072(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static u32 ath12k_hal_rx_desc_get_mpdu_start_tag_qcc2072(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.qcc2072.mpdu_start_tag, @@ -397,6 +411,8 @@ static void ath12k_hal_extract_rx_desc_data_qcc2072(struct hal_rx_desc_data *rx_ rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcc2072(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcc2072(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcc2072(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcc2072(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcc2072(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_qcc2072(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_qcc2072(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_qcc2072(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c index ba9ce1e718e8..9d5180ef83b4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c @@ -699,6 +699,20 @@ u8 ath12k_hal_rx_desc_get_mpdu_tid_qcn9274(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_TID); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline u16 ath12k_hal_rx_desc_get_mpdu_peer_id_qcn9274(struct hal_rx_desc *desc) { @@ -914,6 +928,8 @@ void ath12k_hal_extract_rx_desc_data_qcn9274(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_ctl_valid = ath12k_hal_rx_desc_get_mpdu_seq_ctl_vld_qcn9274(rx_desc); rx_desc_data->fc_valid = ath12k_hal_rx_desc_get_mpdu_fc_valid_qcn9274(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcn9274(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcn9274(rx_desc); rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcn9274(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcn9274(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcn9274(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h index ac2a8ac03288..dd60626b3240 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h @@ -559,7 +559,6 @@ struct hal_eht_sig_ofdma_cmn_eb1 { #define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27) #define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36) #define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45) -#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54) struct hal_eht_sig_ofdma_cmn_eb2 { __le64 info0; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c index 02d3cadf03fe..eeabe9db776e 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c @@ -59,7 +59,9 @@ void ath12k_wifi7_hal_tx_cmd_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->lmac_id, HAL_TCL_DATA_CMD_INFO3_PMAC_ID) | le32_encode_bits(ti->vdev_id, HAL_TCL_DATA_CMD_INFO3_VDEV_ID); - tcl_cmd->info4 = le32_encode_bits(ti->bss_ast_idx, + tcl_cmd->info4 = le32_encode_bits(ti->lookup_override, + HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE) | + le32_encode_bits(ti->bss_ast_idx, HAL_TCL_DATA_CMD_INFO4_SEARCH_INDEX) | le32_encode_bits(ti->bss_ast_hash, HAL_TCL_DATA_CMD_INFO4_CACHE_SET_NUM); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h index 9d2b1552c2f5..c548a0fbf1bb 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h @@ -34,6 +34,7 @@ struct hal_tx_info { u8 dscp_tid_tbl_idx; bool enable_mesh; int bank_id; + bool lookup_override; }; /* TODO: Check if the actual desc macros can be used instead */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c index e64e512cac7d..efbbc1cbd3e4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c @@ -280,6 +280,20 @@ u8 ath12k_hal_rx_desc_get_l3_pad_bytes_wcn7850(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline bool ath12k_hal_rx_desc_encrypt_valid_wcn7850(struct hal_rx_desc *desc) { @@ -599,6 +613,8 @@ void ath12k_hal_extract_rx_desc_data_wcn7850(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_wcn7850(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_wcn7850(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_wcn7850(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_wcn7850(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_wcn7850(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_wcn7850(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_wcn7850(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_wcn7850(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index cb3185850439..3d59fa452ec0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -434,6 +434,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "wcn7850 hw2.0", @@ -520,6 +525,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcn9274 hw2.0", @@ -602,6 +612,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5332 hw1.0", @@ -677,6 +692,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .bdf_addr_offset = 0xC00000, .dp_primary_link_only = true, + .client = { + .max_client_single = 256, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcc2072 hw1.0", @@ -764,6 +784,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5424 hw1.0", @@ -843,6 +868,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, }; @@ -859,7 +889,9 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_sta *sta = control->sta; + struct ath12k_link_sta *arsta = NULL; struct ath12k_link_vif *tmp_arvif; + struct ath12k_sta *ahsta = NULL; u32 info_flags = info->flags; struct sk_buff *msdu_copied; struct ath12k *ar, *tmp_ar; @@ -941,6 +973,12 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) is_mcast = is_multicast_ether_addr(hdr->addr1); + if (sta) { + ahsta = ath12k_sta_to_ahsta(control->sta); + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + } + /* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p) ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp); @@ -961,7 +999,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!vif->valid_links || !is_mcast || is_dvlan || (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) || test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { - ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, skb, false, 0, is_mcast); + ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, arsta, skb, false, 0, is_mcast); if (unlikely(ret)) { ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); ieee80211_free_txskb(ar->ah->hw, skb); @@ -999,6 +1037,11 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, skb_cb->vif = vif; skb_cb->ar = tmp_ar; + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + else + arsta = NULL; + /* For open mode, skip peer find logic */ if (unlikely(!ahvif->dp_vif.key_cipher)) goto skip_peer_find; @@ -1030,7 +1073,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, spin_unlock_bh(&tmp_dp->dp_lock); skip_peer_find: - ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, + ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, arsta, msdu_copied, true, mcbc_gsn, is_mcast); if (unlikely(ret)) { if (ret == -ENOMEM) { @@ -1075,6 +1118,7 @@ static const struct ieee80211_ops ath12k_ops_wifi7 = { .sta_state = ath12k_mac_op_sta_state, .sta_set_txpwr = ath12k_mac_op_sta_set_txpwr, .link_sta_rc_update = ath12k_mac_op_link_sta_rc_update, + .sta_set_4addr = ath12k_mac_op_sta_set_4addr, .conf_tx = ath12k_mac_op_conf_tx, .set_antenna = ath12k_mac_op_set_antenna, .get_antenna = ath12k_mac_op_get_antenna, diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index b5e904a55aea..84a31b953db8 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "core.h" #include "debugfs.h" #include "debug.h" @@ -134,6 +136,10 @@ struct wmi_pdev_set_obss_bitmap_arg { const char *label; }; +static DEFINE_MUTEX(ath12k_wmi_mutex); +static refcount_t ath12k_wmi_refcount; +static void __percpu *ath12k_wmi_tb; + static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 }, [WMI_TAG_ARRAY_UINT32] = { .min_len = 0 }, @@ -228,6 +234,68 @@ static const int ath12k_hw_mode_pri_map[] = { PRIMAP(WMI_HOST_HW_MODE_MAX), }; +/* + * Interference bitmap transform maps used by + * ath12k_wmi_transform_interference_bitmap(). + * + * Firmware reports bitmap bits in a primary-based order where: + * - bit 0 is always the primary 20 MHz segment, + * - bit 1 is the adjacent 20 MHz in the same 40 MHz block, + * - bit 2 is the lower 20 MHz segment of the adjacent 40 MHz segment + * - bit 3 is the higher 20 MHz segment of the adjacent 40 MHz segment + * - remaining bits continue outward in 80/160/320 MHz groups. + * + * cfg80211 userspace notification expects absolute frequency order where: + * - bit 0 is the lowest-frequency 20 MHz segment in the current chandef, + * - bit N increases monotonically toward higher frequency. + * + * For each bandwidth-specific map: + * - row index = primary 20 MHz index in absolute (low->high) order, + * - column index = source bit position from firmware bitmap, + * - value = destination bit position in absolute order bitmap. + * + * Example for 80 MHz: if primary index is 2 (third 20 MHz chunk from low + * frequency), row intf_map_80[2] = { 2, 3, 0, 1 } means firmware bits {0,1,2,3} + * are remapped to destination bits {2,3,0,1} before notifying cfg80211. + */ + +static const int intf_map_80[4][4] = { + { 0, 1, 2, 3 }, + { 1, 0, 2, 3 }, + { 2, 3, 0, 1 }, + { 3, 2, 0, 1 } +}; + +static const int intf_map_160[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 1, 0, 2, 3, 4, 5, 6, 7 }, + { 2, 3, 0, 1, 4, 5, 6, 7 }, + { 3, 2, 0, 1, 4, 5, 6, 7 }, + { 4, 5, 6, 7, 0, 1, 2, 3 }, + { 5, 4, 6, 7, 0, 1, 2, 3 }, + { 6, 7, 4, 5, 0, 1, 2, 3 }, + { 7, 6, 4, 5, 0, 1, 2, 3 } +}; + +static const int intf_map_320[16][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 3, 2, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 5, 4, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 6, 7, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 7, 6, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 9, 8, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 10, 11, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 11, 10, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 12, 13, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 13, 12, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 14, 15, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 15, 14, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 } +}; + static int ath12k_wmi_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, int (*iter)(struct ath12k_base *ab, u16 tag, u16 len, @@ -289,29 +357,19 @@ static int ath12k_wmi_tlv_iter_parse(struct ath12k_base *ab, u16 tag, u16 len, return 0; } -static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb, - const void *ptr, size_t len) -{ - return ath12k_wmi_tlv_iter(ar, ptr, len, ath12k_wmi_tlv_iter_parse, - (void *)tb); -} - static const void ** -ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab, - struct sk_buff *skb, gfp_t gfp) +ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb) { const void **tb; int ret; - tb = kzalloc_objs(*tb, WMI_TAG_MAX, gfp); - if (!tb) - return ERR_PTR(-ENOMEM); + tb = this_cpu_ptr(ath12k_wmi_tb); + memset(tb, 0, WMI_TAG_MAX * sizeof(*tb)); - ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len); - if (ret) { - kfree(tb); + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tlv_iter_parse, (void *)tb); + if (ret) return ERR_PTR(ret); - } return tb; } @@ -3384,6 +3442,75 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, return ret; } +int +ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar, + struct ath12k_wmi_thermal_mitigation_arg *arg) +{ + struct ath12k_wmi_therm_throt_level_config_param *lvl_conf; + struct ath12k_wmi_therm_throt_config_request_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int i, ret, len; + + len = sizeof(*cmd) + TLV_HDR_SIZE + (arg->num_levels * sizeof(*lvl_conf)); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct ath12k_wmi_therm_throt_config_request_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_CONFIG_REQUEST, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + cmd->enable = cpu_to_le32(1); + cmd->dc = cpu_to_le32(100); + cmd->dc_per_event = cpu_to_le32(0xffffffff); + cmd->therm_throt_levels = cpu_to_le32(arg->num_levels); + + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + arg->num_levels * sizeof(*lvl_conf)); + + lvl_conf = (struct ath12k_wmi_therm_throt_level_config_param *)tlv->value; + + for (i = 0; i < arg->num_levels; i++) { + lvl_conf->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO, + sizeof(*lvl_conf)); + + lvl_conf->temp_lwm = a_cpu_to_sle32(arg->levelconf[i].tmplwm); + lvl_conf->temp_hwm = a_cpu_to_sle32(arg->levelconf[i].tmphwm); + lvl_conf->dc_off_percent = cpu_to_le32(arg->levelconf[i].dcoffpercent); + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION, + ar->ab->wmi_ab.svc_map)) + lvl_conf->pout_reduction_25db = + cpu_to_le32(arg->levelconf[i].pout_reduction_db); + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK, + ar->ab->wmi_ab.svc_map)) + lvl_conf->tx_chain_mask = cpu_to_le32(ar->cfg_tx_chainmask); + + lvl_conf->duty_cycle = cpu_to_le32(ATH12K_THERMAL_DEFAULT_DUTY_CYCLE); + lvl_conf++; + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "WMI vdev set thermal throt pdev_id %u enable dc 100 dc_per_event 0xffffffff levels %d\n", + ar->pdev->pdev_id, arg->num_levels); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID); + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_THERM_THROT_SET_CONF cmd: %d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, struct wmi_11d_scan_start_arg *arg) { @@ -3911,9 +4038,10 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_obss_color_collision_event *ev; struct ath12k_link_vif *arvif; u32 vdev_id, evt_type; + const void **tb; u64 bitmap; - const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n", PTR_ERR(tb)); @@ -4064,7 +4192,12 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab, cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT); wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt); wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); - wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET); + wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET | + WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE); + + if (tg_cfg->is_wds_null_frame_supported) + wmi_cfg->flags2 |= + cpu_to_le32(WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, @@ -4202,12 +4335,9 @@ int ath12k_wmi_pdev_lro_cfg(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send lro cfg req wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -4277,6 +4407,9 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) ab->wmi_ab.svc_map)) arg.res_cfg.is_reg_cc_ext_event_supported = true; + if (test_bit(WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT, ab->wmi_ab.svc_map)) + arg.res_cfg.is_wds_null_frame_supported = true; + ab->hw_params->wmi_init(ab, &arg.res_cfg); ab->wow.wmi_conf_rx_decap_mode = arg.res_cfg.rx_decap_mode; @@ -4338,12 +4471,9 @@ int ath12k_wmi_vdev_spectral_conf(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send spectral scan config wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -4375,12 +4505,9 @@ int ath12k_wmi_vdev_spectral_enable(struct ath12k *ar, u32 vdev_id, if (ret) { ath12k_warn(ar->ab, "failed to send spectral enable wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -4421,12 +4548,9 @@ int ath12k_wmi_pdev_dma_ring_cfg(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send dma ring cfg req wmi cmd\n"); - goto err; + dev_kfree_skb(skb); } - return 0; -err: - dev_kfree_skb(skb); return ret; } @@ -5714,7 +5838,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf const struct wmi_vdev_start_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5724,13 +5848,11 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev start resp ev"); - kfree(tb); return -EPROTO; } *vdev_rsp = *ev; - kfree(tb); return 0; } @@ -5809,7 +5931,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n"); - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5819,7 +5941,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch reg chan list ext update ev\n"); - kfree(tb); return -EPROTO; } @@ -5849,7 +5970,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, if (num_2g_reg_rules > MAX_REG_RULES || num_5g_reg_rules > MAX_REG_RULES) { ath12k_warn(ab, "Num reg rules for 2G/5G exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n", num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES); - kfree(tb); return -EINVAL; } @@ -5859,7 +5979,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n", i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES); - kfree(tb); return -EINVAL; } @@ -5884,14 +6003,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n", i); - kfree(tb); return -EINVAL; } } if (!total_reg_rules) { ath12k_warn(ab, "No reg rules available\n"); - kfree(tb); return -EINVAL; } @@ -5993,7 +6110,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_2g_ptr) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n"); return -ENOMEM; } @@ -6027,7 +6143,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_5g_ptr) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n"); return -ENOMEM; } @@ -6046,7 +6161,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_6g_ap_ptr[i]) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 6g ap rules\n"); return -ENOMEM; } @@ -6061,7 +6175,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_6g_client_ptr[j][i]) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 6g client rules\n"); return -ENOMEM; } @@ -6096,7 +6209,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n"); - kfree(tb); return 0; } @@ -6107,7 +6219,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * const struct wmi_peer_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6117,7 +6229,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer delete resp ev"); - kfree(tb); return -EPROTO; } @@ -6127,7 +6238,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * ether_addr_copy(peer_del_resp->peer_macaddr.addr, ev->peer_macaddr.addr); - kfree(tb); return 0; } @@ -6139,7 +6249,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, const struct wmi_vdev_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6149,13 +6259,11 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev delete resp ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); - kfree(tb); return 0; } @@ -6167,7 +6275,7 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, const struct wmi_bcn_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6177,14 +6285,12 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch bcn tx status ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); *tx_status = le32_to_cpu(ev->tx_status); - kfree(tb); return 0; } @@ -6195,7 +6301,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ const struct wmi_vdev_stopped_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6205,13 +6311,11 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ ev = tb[WMI_TAG_VDEV_STOPPED_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev stop ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); - kfree(tb); return 0; } @@ -6350,7 +6454,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, const struct wmi_mgmt_tx_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6360,7 +6464,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch mgmt tx compl ev"); - kfree(tb); return -EPROTO; } @@ -6370,7 +6473,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, param->ppdu_id = ev->ppdu_id; param->ack_rssi = ev->ack_rssi; - kfree(tb); return 0; } @@ -6533,7 +6635,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_scan_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6543,7 +6645,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_SCAN_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch scan ev"); - kfree(tb); return -EPROTO; } @@ -6555,7 +6656,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, scan_evt_param->vdev_id = ev->vdev_id; scan_evt_param->tsf_timestamp = ev->tsf_timestamp; - kfree(tb); return 0; } @@ -6566,7 +6666,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf const struct wmi_peer_sta_kickout_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6576,7 +6676,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer sta kickout ev"); - kfree(tb); return -EPROTO; } @@ -6584,7 +6683,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf arg->reason = le32_to_cpu(ev->reason); arg->rssi = le32_to_cpu(ev->rssi); - kfree(tb); return 0; } @@ -6595,7 +6693,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_roam_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6605,7 +6703,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_ROAM_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch roam ev"); - kfree(tb); return -EPROTO; } @@ -6613,7 +6710,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, roam_ev->reason = ev->reason; roam_ev->rssi = ev->rssi; - kfree(tb); return 0; } @@ -6647,7 +6743,7 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6657,7 +6753,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_CHAN_INFO_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch chan info ev"); - kfree(tb); return -EPROTO; } @@ -6674,7 +6769,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz; ch_info_ev->vdev_id = ev->vdev_id; - kfree(tb); return 0; } @@ -6686,7 +6780,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_pdev_bss_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6696,7 +6790,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev bss chan info ev"); - kfree(tb); return -EPROTO; } @@ -6714,7 +6807,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low; bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high; - kfree(tb); return 0; } @@ -6726,7 +6818,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_vdev_install_key_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6736,7 +6828,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev install key compl ev"); - kfree(tb); return -EPROTO; } @@ -6746,7 +6837,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk arg->key_flags = le32_to_cpu(ev->key_flags); arg->status = le32_to_cpu(ev->status); - kfree(tb); return 0; } @@ -6757,7 +6847,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff const struct wmi_peer_assoc_conf_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6767,14 +6857,12 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer assoc conf ev"); - kfree(tb); return -EPROTO; } peer_assoc_conf->vdev_id = le32_to_cpu(ev->vdev_id); peer_assoc_conf->macaddr = ev->peer_macaddr.addr; - kfree(tb); return 0; } @@ -6792,7 +6880,7 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s const void **tb; int ret, i; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6801,7 +6889,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; if (!ev) { - kfree(tb); ath12k_warn(ab, "failed to fetch 11d new cc ev"); return -EPROTO; } @@ -6814,8 +6901,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s ab->new_alpha2[0], ab->new_alpha2[1]); - kfree(tb); - for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; @@ -7146,7 +7231,11 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) struct ath12k_wmi_mgmt_rx_arg rx_ev = {}; struct ath12k *ar; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_sta *pubsta = NULL; + struct ath12k_dp_link_peer *peer; struct ieee80211_hdr *hdr; + bool is_4addr_null_pkt; + struct ath12k_dp *dp; u16 fc; struct ieee80211_supported_band *sband; s32 noise_floor; @@ -7221,6 +7310,38 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); + is_4addr_null_pkt = (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) && + ieee80211_has_a4(hdr->frame_control); + + /* + * Add check to drop frames other than 4-address NULL frame. Since + * firmware sends all NULL frames in this path (3-address and 4-address) + */ + if (ieee80211_is_data(hdr->frame_control) && !is_4addr_null_pkt) { + dev_kfree_skb(skb); + goto exit; + } + + if (is_4addr_null_pkt) { + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_pdev_and_addr(dp, ar->pdev_idx, + hdr->addr2); + if (!peer) { + spin_unlock_bh(&dp->dp_lock); + dev_kfree_skb(skb); + goto exit; + } + pubsta = peer->sta; + if (pubsta && pubsta->valid_links) { + status->link_valid = 1; + status->link_id = peer->link_id; + } + spin_unlock_bh(&dp->dp_lock); + goto send_rx; + } + /* Firmware is guaranteed to report all essential management frames via * WMI while it can deliver some extra via HTT. Since there can be * duplicates split the reporting wrt monitor/sniffing. @@ -7244,6 +7365,7 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) if (ieee80211_is_beacon(hdr->frame_control)) ath12k_mac_handle_beacon(ar, skb); +send_rx: ath12k_dbg(ab, ATH12K_DBG_MGMT, "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, @@ -8557,7 +8679,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, const struct wmi_pdev_ctl_failsafe_chk_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8567,7 +8689,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev ctl failsafe check ev"); - kfree(tb); return; } @@ -8581,8 +8702,330 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, if (ev->ctl_failsafe_status != 0) ath12k_warn(ab, "pdev ctl failsafe failure status %d", ev->ctl_failsafe_status); +} - kfree(tb); +static int +ath12k_wmi_incumbent_signal_interference_subtlv_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, + void *data) +{ + const struct ath12k_wmi_incumbent_signal_interference_params *info; + struct ath12k_wmi_incumbent_signal_interference_arg *arg = data; + + switch (tag) { + case WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE: + if (len < sizeof(*info)) { + ath12k_warn(ab, + "DCS incumbent signal interference subtlv 0x%x invalid len %u\n", + tag, len); + return -EINVAL; + } + + info = ptr; + + arg->chan_width = le32_to_cpu(info->chan_width); + arg->chan_freq = le32_to_cpu(info->chan_freq); + arg->center_freq0 = le32_to_cpu(info->center_freq0); + arg->center_freq1 = le32_to_cpu(info->center_freq1); + arg->chan_bw_interference_bitmap = + le32_to_cpu(info->chan_bw_interference_bitmap); + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "incumbent signal interference chan width %u freq %u center_freq0 %u center_freq1 %u bitmap 0x%x\n", + arg->chan_width, arg->chan_freq, + arg->center_freq0, arg->center_freq1, + arg->chan_bw_interference_bitmap); + break; + default: + ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference in subtlvs\n", + tag); + return -EINVAL; + } + + return 0; +} + +static int ath12k_wmi_dcs_interference_event_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + int ret = 0; + + switch (tag) { + case WMI_TAG_DCS_INTERFERENCE_EVENT: + /* Fixed param should already be processed */ + break; + case WMI_TAG_ARRAY_STRUCT: + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_incumbent_signal_interference_subtlv_parser, + data); + break; + default: + ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference event\n", + tag); + ret = -EINVAL; + break; + } + + return ret; +} + +static bool +ath12k_wmi_validate_interference_info(struct ath12k *ar, + struct ath12k_wmi_incumbent_signal_interference_arg *info) +{ + switch (info->chan_width) { + case WMI_CHAN_WIDTH_20: + if (info->chan_bw_interference_bitmap > ATH12K_WMI_DCS_SEG_PRI20) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 20 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_40: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 40 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_80: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20 | + ATH12K_WMI_DCS_SEG_SEC40)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 80 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_160: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20 | + ATH12K_WMI_DCS_SEG_SEC40 | + ATH12K_WMI_DCS_SEG_SEC80)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 160 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + case WMI_CHAN_WIDTH_320: + if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 | + ATH12K_WMI_DCS_SEG_SEC20 | + ATH12K_WMI_DCS_SEG_SEC40 | + ATH12K_WMI_DCS_SEG_SEC80 | + ATH12K_WMI_DCS_SEG_SEC160)) { + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with wrong chan width bmap 0x%x for 320 MHz", + info->chan_bw_interference_bitmap); + return false; + } + break; + default: + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "DCS interference event received with unknown channel width %u", + info->chan_width); + return false; + } + return true; +} + +static u32 +ath12k_wmi_transform_interference_bitmap(int input_bitmap, + struct cfg80211_chan_def *chandef) +{ + u16 output_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {}; + u16 input_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {}; + u32 start_freq, segment_freq; + int primary_index = -1; + u32 output_bitmap = 0; + u16 num_sub_chans; + int bandwidth; + + bandwidth = nl80211_chan_width_to_mhz(chandef->width); + if (bandwidth < 0) + return 0; + + /* + * Firmware reports bit 0 as primary 20 MHz irrespective of absolute + * frequency position. Convert to standardized lowest-to-highest 20 MHz + * ordering expected by cfg80211/mac80211 userspace consumers. + */ + num_sub_chans = bandwidth / 20; + start_freq = (chandef->center_freq1 - bandwidth / 2) + 10; + + for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; i++) { + segment_freq = start_freq + (i * 20); + if (segment_freq == chandef->chan->center_freq) { + primary_index = i; + break; + } + } + if (primary_index == -1) + return 0; + + for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i) + input_bits[i] = BIT(i) & input_bitmap; + + for (int i = 0; i < num_sub_chans; ++i) { + int src = i, dst = i; + + switch (bandwidth) { + case 40: + if (primary_index == 1) + dst = 1 - i; + break; + case 80: + dst = intf_map_80[primary_index][i]; + break; + case 160: + dst = intf_map_160[primary_index][i]; + break; + case 320: + dst = intf_map_320[primary_index][i]; + break; + } + output_bits[dst] = input_bits[src]; + } + + for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i) + output_bitmap |= output_bits[i] ? BIT(i) : 0; + + return output_bitmap; +} + +static void +ath12k_wmi_process_incumbent_signal_interference_evt(struct ath12k_base *ab, + struct sk_buff *skb, + const struct ath12k_wmi_intf_arg *intf_arg) +{ + struct ath12k_wmi_incumbent_signal_interference_arg info = {}; + struct ath12k_incumbent_signal_interference *incumbent; + struct ath12k_mac_get_any_chanctx_conf_arg arg; + u32 transformed_intf_bitmap; + struct ieee80211_hw *hw; + struct ath12k *ar; + int ret; + + guard(rcu)(); + + ar = ath12k_mac_get_ar_by_pdev_id(ab, intf_arg->pdev_id); + if (!ar) { + ath12k_warn(ab, "incumbent signal interference detected on invalid pdev %d\n", + intf_arg->pdev_id); + return; + } + if (!ar->supports_6ghz) { + ath12k_warn(ab, "pdev does not support 6 GHz, dropping DCS interference event\n"); + return; + } + + incumbent = &ar->incumbent_signal_interference; + spin_lock_bh(&ar->data_lock); + if (incumbent->handling_in_progress) { + spin_unlock_bh(&ar->data_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "incumbent signal interference handling ongoing, dropping DCS interference event"); + return; + } + spin_unlock_bh(&ar->data_lock); + + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_dcs_interference_event_parser, + &info); + if (ret) { + ath12k_warn(ab, + "failed to parse incumbent signal interference TLV. Error %d\n", + ret); + return; + } + + if (!ath12k_wmi_validate_interference_info(ar, &info)) { + ath12k_warn(ab, "invalid DCS incumbent signal interference TLV - Skipping event"); + return; + } + + arg.ar = ar; + arg.chanctx_conf = NULL; + hw = ath12k_ar_to_hw(ar); + ieee80211_iter_chan_contexts_atomic(hw, + ath12k_mac_get_any_chanctx_conf_iter, + &arg); + if (!arg.chanctx_conf) { + ath12k_warn(ab, "failed to find valid chanctx_conf in incumbent signal intf detected event\n"); + return; + } + + if (info.chan_freq != arg.chanctx_conf->def.chan->center_freq) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "dcs interference event received with wrong channel %d (ctx freq %d)", + info.chan_freq, arg.chanctx_conf->def.chan->center_freq); + return; + } + + spin_lock_bh(&ar->data_lock); + incumbent->center_freq = arg.chanctx_conf->def.chan->center_freq; + incumbent->width = arg.chanctx_conf->def.width; + incumbent->chan_bw_interference_bitmap = info.chan_bw_interference_bitmap; + incumbent->handling_in_progress = true; + spin_unlock_bh(&ar->data_lock); + transformed_intf_bitmap = + ath12k_wmi_transform_interference_bitmap(info.chan_bw_interference_bitmap, + &arg.chanctx_conf->def); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "incumbent signal interference bitmap 0x%x (transformed 0x%x)\n", + info.chan_bw_interference_bitmap, transformed_intf_bitmap); + cfg80211_incumbent_signal_notify(hw->wiphy, + &arg.chanctx_conf->def, + transformed_intf_bitmap, + GFP_ATOMIC); +} + +static void +ath12k_wmi_dcs_interference_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct ath12k_wmi_dcs_interference_ev_fixed_params *dcs_intf_ev; + struct ath12k_wmi_intf_arg dcs_intf_arg; + const struct wmi_tlv *tlv; + u16 tlv_tag; + u8 *ptr; + + if (skb->len < (sizeof(*dcs_intf_ev) + TLV_HDR_SIZE)) { + ath12k_warn(ab, "DCS interference event is of incorrect length\n"); + return; + } + + ptr = skb->data; + tlv = (struct wmi_tlv *)ptr; + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + ptr += sizeof(*tlv); + + if (tlv_tag != WMI_TAG_DCS_INTERFERENCE_EVENT) { + ath12k_warn(ab, "DCS interference event received with wrong tag\n"); + return; + } + + dcs_intf_ev = (struct ath12k_wmi_dcs_interference_ev_fixed_params *)ptr; + + dcs_intf_arg.interference_type = + le32_to_cpu(dcs_intf_ev->interference_type); + dcs_intf_arg.pdev_id = le32_to_cpu(dcs_intf_ev->pdev_id); + + if (dcs_intf_arg.interference_type == + ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "incumbent signal interference (Type %u) detected on pdev %u.", + dcs_intf_arg.interference_type, + dcs_intf_arg.pdev_id); + ath12k_wmi_process_incumbent_signal_interference_evt(ab, skb, + &dcs_intf_arg); + } } static void @@ -8654,7 +9097,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, const u32 *vdev_ids; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8666,7 +9109,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, if (!ev || !vdev_ids) { ath12k_warn(ab, "failed to fetch pdev csa switch count ev"); - kfree(tb); return; } @@ -8676,8 +9118,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, ev->num_vdevs); ath12k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids); - - kfree(tb); } static void @@ -8689,7 +9129,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff struct ath12k *ar; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8700,7 +9140,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff if (!ev) { ath12k_warn(ab, "failed to fetch pdev dfs radar detected ev"); - kfree(tb); return; } @@ -8739,8 +9178,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff exit: rcu_read_unlock(); - - kfree(tb); } static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, @@ -8751,7 +9188,7 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, int ret; u16 length; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); @@ -8762,14 +9199,11 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, ev = tb[WMI_TAG_ARRAY_BYTE]; if (!ev) { ath12k_warn(ab, "failed to fetch ftm msg\n"); - kfree(tb); return; } length = skb->len - TLV_HDR_SIZE; ath12k_tm_process_event(ab, cmd_id, ev, length); - kfree(tb); - tb = NULL; } static void @@ -8782,7 +9216,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, int temp; u32 pdev_id; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb)); return; @@ -8791,15 +9225,12 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev temp ev\n"); - kfree(tb); return; } temp = a_sle32_to_cpu(ev->temp); pdev_id = le32_to_cpu(ev->pdev_id); - kfree(tb); - ath12k_dbg(ab, ATH12K_DBG_WMI, "pdev temperature ev temp %d pdev_id %u\n", temp, pdev_id); @@ -8819,6 +9250,42 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, rcu_read_unlock(); } +static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ + const struct wmi_therm_throt_stats_event *ev; + struct ath12k *ar; + const void **tb; + + tb = ath12k_wmi_tlv_parse(ab, skb); + if (IS_ERR(tb)) { + ath12k_err(ab, "failed to parse thermal throttling stats tlv: %ld\n", + PTR_ERR(tb)); + return; + } + + ev = tb[WMI_TAG_THERM_THROT_STATS_EVENT]; + if (!ev) { + ath12k_err(ab, "failed to fetch thermal throt stats ev\n"); + return; + } + + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev->pdev_id)); + if (!ar) { + ath12k_warn(ab, "received thermal_throt_stats in invalid pdev %u\n", + le32_to_cpu(ev->pdev_id)); + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "thermal stats ev level %u pdev_id %u temp %u throt_levels %u\n", + le32_to_cpu(ev->level), le32_to_cpu(ev->pdev_id), + le32_to_cpu(ev->temp), le32_to_cpu(ev->therm_throt_levels)); +} + static void ath12k_fils_discovery_event(struct ath12k_base *ab, struct sk_buff *skb) { @@ -8826,7 +9293,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, const struct wmi_fils_discovery_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -8838,15 +9305,12 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, ev = tb[WMI_TAG_HOST_SWFDA_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch FILS discovery event\n"); - kfree(tb); return; } ath12k_warn(ab, "FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n", ev->vdev_id, ev->fils_tt, ev->tbtt); - - kfree(tb); } static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, @@ -8856,7 +9320,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, const struct wmi_probe_resp_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -8869,7 +9333,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, if (!ev) { ath12k_warn(ab, "failed to fetch probe response transmission status event"); - kfree(tb); return; } @@ -8877,8 +9340,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, ath12k_warn(ab, "Probe response transmission failed for vdev_id %u, status %u\n", ev->vdev_id, ev->tx_status); - - kfree(tb); } static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, @@ -8890,7 +9351,7 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, struct ath12k *ar; int ret, vdev_id; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse P2P NoA TLV: %d\n", ret); @@ -8900,10 +9361,8 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, ev = tb[WMI_TAG_P2P_NOA_EVENT]; noa = tb[WMI_TAG_P2P_NOA_INFO]; - if (!ev || !noa) { - ret = -EPROTO; - goto out; - } + if (!ev || !noa) + return -EPROTO; vdev_id = __le32_to_cpu(ev->vdev_id); @@ -8926,8 +9385,6 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, unlock: rcu_read_unlock(); -out: - kfree(tb); return ret; } @@ -8938,7 +9395,7 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8946,10 +9403,8 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, } ev = tb[WMI_TAG_RFKILL_EVENT]; - if (!ev) { - kfree(tb); + if (!ev) return; - } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi tlv rfkill state change gpio %d type %d radio_state %d\n", @@ -8962,7 +9417,6 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, spin_unlock_bh(&ab->base_lock); queue_work(ab->workqueue, &ab->rfkill_work); - kfree(tb); } static void @@ -8978,7 +9432,7 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab, const struct wmi_twt_enable_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse wmi twt enable status event tlv: %d\n", @@ -8989,15 +9443,12 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab, ev = tb[WMI_TAG_TWT_ENABLE_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch twt enable wmi event\n"); - goto exit; + return; } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt enable event pdev id %u status %u\n", le32_to_cpu(ev->pdev_id), le32_to_cpu(ev->status)); - -exit: - kfree(tb); } static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, @@ -9007,7 +9458,7 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, const struct wmi_twt_disable_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse wmi twt disable status event tlv: %d\n", @@ -9018,15 +9469,12 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, ev = tb[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch twt disable wmi event\n"); - goto exit; + return; } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt disable event pdev id %d status %u\n", le32_to_cpu(ev->pdev_id), le32_to_cpu(ev->status)); - -exit: - kfree(tb); } static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab, @@ -9099,7 +9547,7 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -9109,7 +9557,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch gtk offload status ev"); - kfree(tb); return; } @@ -9119,7 +9566,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, rcu_read_unlock(); ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n", le32_to_cpu(ev->vdev_id)); - kfree(tb); return; } @@ -9135,8 +9581,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, (void *)&replay_ctr_be, GFP_ATOMIC); rcu_read_unlock(); - - kfree(tb); } static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, @@ -9148,7 +9592,7 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, const void **tb; int ret, i; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse mlo setup complete event tlv: %d\n", @@ -9159,7 +9603,6 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch mlo setup complete event\n"); - kfree(tb); return; } @@ -9178,14 +9621,11 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, if (!ar) { ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n", ev->pdev_id, ev->status); - goto out; + return; } ar->mlo_setup_status = le32_to_cpu(ev->status); complete(&ar->mlo_setup_done); - -out: - kfree(tb); } static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, @@ -9195,7 +9635,7 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret); @@ -9203,13 +9643,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, } ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE]; - if (!ev) { + if (!ev) ath12k_warn(ab, "failed to fetch teardown complete event\n"); - kfree(tb); - return; - } - - kfree(tb); } #ifdef CONFIG_ATH12K_DEBUGFS @@ -9900,6 +10335,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_PDEV_TEMPERATURE_EVENTID: ath12k_wmi_pdev_temperature_event(ab, skb); break; + case WMI_THERM_THROT_STATS_EVENTID: + ath12k_wmi_thermal_throt_stats_event(ab, skb); + break; case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID: ath12k_wmi_pdev_dma_ring_buf_release_event(ab, skb); break; @@ -9954,6 +10392,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: ath12k_wmi_obss_color_collision_event(ab, skb); break; + case WMI_DCS_INTERFERENCE_EVENTID: + ath12k_wmi_dcs_interference_event(ab, skb); + break; /* add Unsupported events (rare) here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_PEER_OPER_MODE_CHANGE_EVENTID: @@ -10172,6 +10613,42 @@ int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, return ret; } +int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar, + u32 chan_bw_interference_bitmap) +{ + struct wmi_unit_test_arg wmi_ut = {}; + struct ath12k_link_vif *arvif; + struct ath12k_vif *ahvif; + bool arvif_found = false; + + list_for_each_entry(arvif, &ar->arvifs, list) { + ahvif = arvif->ahvif; + if (arvif->is_started && ahvif->vdev_type == WMI_VDEV_TYPE_AP) { + arvif_found = true; + break; + } + } + + if (!arvif_found) + return -EINVAL; + + wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF] = + ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE; + wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP] = + chan_bw_interference_bitmap; + + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE; + wmi_ut.num_args = ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS; + wmi_ut.diag_token = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN; + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "Triggering incumbent signal interference simulation, interference bitmap: 0x%x\n", + chan_bw_interference_bitmap); + + return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut); +} + int ath12k_wmi_connect(struct ath12k_base *ab) { u32 i; @@ -10963,10 +11440,9 @@ int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params ath12k_warn(ar->ab, "failed to submit WMI_MLO_SETUP_CMDID command: %d\n", ret); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } int ath12k_wmi_mlo_ready(struct ath12k *ar) @@ -10991,10 +11467,9 @@ int ath12k_wmi_mlo_ready(struct ath12k *ar) ath12k_warn(ar->ab, "failed to submit WMI_MLO_READY_CMDID command: %d\n", ret); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } int ath12k_wmi_mlo_teardown(struct ath12k *ar) @@ -11020,10 +11495,9 @@ int ath12k_wmi_mlo_teardown(struct ath12k *ar) ath12k_warn(ar->ab, "failed to submit WMI MLO teardown command: %d\n", ret); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar) @@ -11086,10 +11560,9 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, if (ret) { ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n"); dev_kfree_skb(skb); - return ret; } - return 0; + return ret; } static int @@ -11312,3 +11785,31 @@ int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, dev_kfree_skb(skb); return ret; } + +int ath12k_wmi_alloc(void) +{ + guard(mutex)(&ath12k_wmi_mutex); + + if (!ath12k_wmi_tb) { + ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *), + __alignof__(void *)); + if (!ath12k_wmi_tb) + return -ENOMEM; + + refcount_set(&ath12k_wmi_refcount, 1); + } else { + refcount_inc(&ath12k_wmi_refcount); + } + + return 0; +} + +void ath12k_wmi_free(void) +{ + guard(mutex)(&ath12k_wmi_mutex); + + if (refcount_dec_and_test(&ath12k_wmi_refcount)) { + free_percpu(ath12k_wmi_tb); + ath12k_wmi_tb = NULL; + } +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 5ba9b7d3a888..c452e3d57a29 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -870,6 +870,7 @@ enum wmi_tlv_event_id { WMI_READ_DATA_FROM_FLASH_EVENTID, WMI_REPORT_RX_AGGR_FAILURE_EVENTID, WMI_PKGID_EVENTID, + WMI_THERM_THROT_STATS_EVENTID, WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO), WMI_UPLOADH_EVENTID, WMI_CAPTUREH_EVENTID, @@ -2014,7 +2015,7 @@ enum wmi_tlv_tag { WMI_TAG_VDEV_CH_POWER_INFO, WMI_TAG_MLO_LINK_SET_ACTIVE_CMD = 0x3BE, WMI_TAG_EHT_RATE_SET = 0x3C4, - WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, + WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE = 0x3C5, WMI_TAG_MLO_TX_SEND_PARAMS, WMI_TAG_MLO_PARTNER_LINK_PARAMS, WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC, @@ -2259,6 +2260,7 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219, WMI_TLV_SERVICE_EXT2_MSG = 220, WMI_TLV_SERVICE_BEACON_PROTECTION_SUPPORT = 244, + WMI_TLV_SERVICE_5_9GHZ_SUPPORT = 247, WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249, WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253, @@ -2268,11 +2270,18 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, + WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT = 286, + WMI_TLV_SERVICE_11BE = 289, WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361, WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365, + WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410, + WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT = 421, + WMI_TLV_SERVICE_IS_TARGET_IPA = 425, + WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426, + WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429, WMI_TLV_SERVICE_ETH_OFFLOAD = 461, WMI_MAX_EXT2_SERVICE, @@ -2326,6 +2335,19 @@ enum wmi_preamble { WMI_VDEV_PREAMBLE_SHORT = 2, }; +/* + * This will be used to set for WMI_VDEV_PARAM_AP_ENABLE_NAWDS + * whenever 4addr station connects in wds offload case. + * This is for enabling multicast to unicast conversion support in + * firmware + */ +#define WDS_EXT_ENABLE 1 + +enum wmi_peer_4addr_allow_frame { + WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2, +}; + enum wmi_peer_smps_state { WMI_PEER_SMPS_PS_NONE = 0, WMI_PEER_SMPS_STATIC = 1, @@ -2490,6 +2512,7 @@ struct ath12k_wmi_resource_config_arg { u32 ema_max_vap_cnt; u32 ema_max_profile_period; bool is_reg_cc_ext_event_supported; + bool is_wds_null_frame_supported; }; struct ath12k_wmi_init_cmd_arg { @@ -2546,6 +2569,8 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4) #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) +#define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18) +#define WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT BIT(22) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct ath12k_wmi_resource_config_params { @@ -3586,7 +3611,6 @@ struct ath12k_wmi_scan_req_arg { u32 num_bssid; u32 num_ssids; u32 n_probes; - u32 *chan_list; u32 notify_scan_events; struct cfg80211_ssid ssid[WLAN_SCAN_MAX_NUM_SSID]; struct ath12k_wmi_mac_addr_params bssid_list[WLAN_SCAN_MAX_NUM_BSSID]; @@ -3595,6 +3619,7 @@ struct ath12k_wmi_scan_req_arg { u32 num_hint_bssid; struct ath12k_wmi_hint_short_ssid_arg hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID]; struct ath12k_wmi_hint_bssid_arg hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; + u32 chan_list[] __counted_by(num_chan); }; struct wmi_ssid_arg { @@ -4120,6 +4145,49 @@ enum set_init_cc_flags { ALPHA_IS_SET, }; +struct wmi_therm_throt_stats_event { + __le32 pdev_id; + __le32 temp; + __le32 level; + __le32 therm_throt_levels; +} __packed; + +#define THERMAL_LEVELS 4 +#define ENHANCED_THERMAL_LEVELS 5 + +struct ath12k_wmi_tt_level_config_param { + s32 tmplwm; + s32 tmphwm; + u32 dcoffpercent; + u32 pout_reduction_db; +}; + +struct ath12k_wmi_therm_throt_config_request_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 enable; + __le32 dc; + /* After how many duty cycles the firmware sends stats to host */ + __le32 dc_per_event; + __le32 therm_throt_levels; +} __packed; + +struct ath12k_wmi_therm_throt_level_config_param { + __le32 tlv_header; + a_sle32 temp_lwm; + a_sle32 temp_hwm; + __le32 dc_off_percent; + __le32 prio; + __le32 pout_reduction_25db; + __le32 tx_chain_mask; + __le32 duty_cycle; +} __packed; + +struct ath12k_wmi_thermal_mitigation_arg { + int num_levels; + const struct ath12k_wmi_tt_level_config_param *levelconf; +}; + struct ath12k_wmi_init_country_arg { union { u16 country_code; @@ -4203,6 +4271,16 @@ enum dfs_test_args_idx { DFS_MAX_TEST_ARGS, }; +#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE 0x18 +#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN 0 +#define ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE 1 + +enum ath12k_wmi_incumbent_signal_test_args_idx { + ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF, + ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP, + ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS, +}; + /* update if another test command requires more */ #define WMI_UNIT_TEST_ARGS_MAX DFS_MAX_TEST_ARGS @@ -4487,6 +4565,62 @@ struct ath12k_wmi_pdev_radar_event { a_sle32 sidx; } __packed; +#define ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE 0x04 + +struct ath12k_wmi_dcs_interference_ev_fixed_params { + __le32 interference_type; + __le32 pdev_id; +} __packed; + +struct ath12k_wmi_incumbent_signal_interference_params { + __le32 chan_width; + __le32 chan_freq; + __le32 center_freq0; + __le32 center_freq1; + __le32 chan_bw_interference_bitmap; +} __packed; + +struct ath12k_wmi_incumbent_signal_interference_arg { + u32 chan_width; + u32 chan_freq; + u32 center_freq0; + u32 center_freq1; + u32 chan_bw_interference_bitmap; +}; + +struct ath12k_wmi_intf_arg { + u32 interference_type; + u32 pdev_id; +}; + +enum ath12k_wmi_dcs_interference_chan_segment { + /* + * Firmware reports interference bitmap in primary-based order. + * Bit 0 is the primary 20 MHz, bit 1 is the adjacent 20 MHz within + * the primary 40 MHz. Bits 2-3 cover the secondary 40 MHz, bits 4-7 + * cover the secondary 80 MHz, and bits 8-15 cover the secondary 160 MHz. + */ + ATH12K_WMI_DCS_SEG_PRI20 = 0x1, + ATH12K_WMI_DCS_SEG_SEC20 = 0x2, + ATH12K_WMI_DCS_SEG_SEC40_LOW = 0x4, + ATH12K_WMI_DCS_SEG_SEC40_UP = 0x8, + ATH12K_WMI_DCS_SEG_SEC40 = 0xC, + ATH12K_WMI_DCS_SEG_SEC80_LOW = 0x10, + ATH12K_WMI_DCS_SEG_SEC80_LOW_UP = 0x20, + ATH12K_WMI_DCS_SEG_SEC80_UP_LOW = 0x40, + ATH12K_WMI_DCS_SEG_SEC80_UP = 0x80, + ATH12K_WMI_DCS_SEG_SEC80 = 0xF0, + ATH12K_WMI_DCS_SEG_SEC160_LOW = 0x0100, + ATH12K_WMI_DCS_SEG_SEC160_LOW_UP = 0x0200, + ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP = 0x0400, + ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP_UP = 0x0800, + ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW_LOW = 0x1000, + ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW = 0x2000, + ATH12K_WMI_DCS_SEG_SEC160_UP_LOW = 0x4000, + ATH12K_WMI_DCS_SEG_SEC160_UP = 0x8000, + ATH12K_WMI_DCS_SEG_SEC160 = 0xFF00, +}; + struct wmi_pdev_temperature_event { /* temperature value in Celsius degree */ a_sle32 temp; @@ -6514,6 +6648,8 @@ __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, enum wmi_halphy_ctrl_path_stats_id tpc_stats_type); void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar); +int ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar, + struct ath12k_wmi_thermal_mitigation_arg *arg); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) @@ -6576,4 +6712,9 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, struct ath12k_reg_tpc_power_info *param); int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, struct wmi_mlo_link_set_active_arg *param); +int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar, + u32 chan_bw_interference_bitmap); +int ath12k_wmi_alloc(void); +void ath12k_wmi_free(void); + #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.h b/drivers/net/wireless/ath/ath12k/wow.h index af9be5fadcc3..273705956815 100644 --- a/drivers/net/wireless/ath/ath12k/wow.h +++ b/drivers/net/wireless/ath/ath12k/wow.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_WOW_H diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 6ca31d4ea437..610a3321b497 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2600,8 +2600,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) /* SW support for IBSS_RSN is provided by mac80211 */ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; - /* both antennas can be configured as RX or TX */ hw->wiphy->available_antennas_tx = 0x3; hw->wiphy->available_antennas_rx = 0x3; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 739a24a6ad67..cc0f2c45fc3a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -3033,7 +3034,7 @@ static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, - u64 *cookie) + u64 *cookie, const u8 *rx_addr) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index b26224480041..0f24539b75ec 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -80,14 +80,14 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah) /* iniAddac needs to be modified for these chips */ if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) { struct ar5416IniArray *addac = &ah->iniAddac; - u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns; + u32 n = addac->ia_rows * addac->ia_columns; u32 *data; - data = devm_kzalloc(ah->dev, size, GFP_KERNEL); + data = devm_kmemdup_array(ah->dev, addac->ia_array, n, sizeof(u32), + GFP_KERNEL); if (!data) return -ENOMEM; - memcpy(data, addac->ia_array, size); addac->ia_array = data; if (!AR_SREV_5416_22_OR_LATER(ah)) { diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index b70cd4af1ae0..e355f94a9390 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -403,6 +403,19 @@ static int ar9002_hw_get_duration(struct ath_hw *ah, const void *ds, int index) } } +static void ath9k_hw_clear_rxdesc_status(struct ar5416_desc *ads) +{ + WRITE_ONCE(ads->u.rx.status0, 0); + WRITE_ONCE(ads->u.rx.status1, 0); + WRITE_ONCE(ads->u.rx.status2, 0); + WRITE_ONCE(ads->u.rx.status3, 0); + WRITE_ONCE(ads->u.rx.status4, 0); + WRITE_ONCE(ads->u.rx.status5, 0); + WRITE_ONCE(ads->u.rx.status6, 0); + WRITE_ONCE(ads->u.rx.status7, 0); + WRITE_ONCE(ads->u.rx.status8, 0); +} + void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) { @@ -412,7 +425,7 @@ void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, if (flags & ATH9K_RXDESC_INTREQ) ads->ds_ctl1 |= AR_RxIntrReq; - memset(&ads->u.rx, 0, sizeof(ads->u.rx)); + ath9k_hw_clear_rxdesc_status(ads); } EXPORT_SYMBOL(ath9k_hw_setuprxdesc); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index a8bc003077dc..97027d286317 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -352,6 +352,19 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, return true; } +static void ath9k_hw_clear_txstatus(struct ar9003_txs *ads) +{ + WRITE_ONCE(ads->ds_info, 0); + WRITE_ONCE(ads->status1, 0); + WRITE_ONCE(ads->status2, 0); + WRITE_ONCE(ads->status3, 0); + WRITE_ONCE(ads->status4, 0); + WRITE_ONCE(ads->status5, 0); + WRITE_ONCE(ads->status6, 0); + WRITE_ONCE(ads->status7, 0); + WRITE_ONCE(ads->status8, 0); +} + static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_status *ts) { @@ -370,7 +383,7 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, (MS(ads->ds_info, AR_TxRxDesc) != 1)) { ath_dbg(ath9k_hw_common(ah), XMIT, "Tx Descriptor error %x\n", ads->ds_info); - memset(ads, 0, sizeof(*ads)); + ath9k_hw_clear_txstatus(ads); return -EIO; } @@ -427,7 +440,7 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11); ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12); - memset(ads, 0, sizeof(*ads)); + ath9k_hw_clear_txstatus(ads); return 0; } @@ -591,10 +604,12 @@ EXPORT_SYMBOL(ath9k_hw_process_rxdesc_edma); void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah) { + int i; + ah->ts_tail = 0; - memset((void *) ah->ts_ring, 0, - ah->ts_size * sizeof(struct ar9003_txs)); + for (i = 0; i < ah->ts_size; i++) + ath9k_hw_clear_txstatus(&ah->ts_ring[i]); ath_dbg(ath9k_hw_common(ah), XMIT, "TS Start 0x%x End 0x%x Virt %p, Size %d\n", diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 6274d1624261..5d0433f2ae37 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1811,10 +1811,10 @@ static void ar9003_hw_tx99_stop(struct ath_hw *ah) static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower) { - static u8 p_pwr_array[ar9300RateSize] = { 0 }; + u8 p_pwr_array[ar9300RateSize]; unsigned int i; - txpower = txpower <= MAX_RATE_POWER ? txpower : MAX_RATE_POWER; + txpower = min(txpower, MAX_RATE_POWER); for (i = 0; i < ar9300RateSize; i++) p_pwr_array[i] = txpower; diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c index fe1013a3a588..b9ef34709202 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c @@ -137,24 +137,6 @@ static void owl_fw_cb(const struct firmware *fw, void *context) release_firmware(fw); } -static const char *owl_get_eeprom_name(struct pci_dev *pdev) -{ - struct device *dev = &pdev->dev; - char *eeprom_name; - - dev_dbg(dev, "using auto-generated eeprom filename\n"); - - eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL); - if (!eeprom_name) - return NULL; - - /* this should match the pattern used in ath9k/init.c */ - scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin", - dev_name(dev)); - - return eeprom_name; -} - static void owl_nvmem_work(struct work_struct *work) { struct owl_ctx *ctx = container_of(work, struct owl_ctx, work); @@ -195,8 +177,9 @@ static int owl_nvmem_probe(struct owl_ctx *ctx) static int owl_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + char eeprom_name[EEPROM_FILENAME_LEN]; + struct device *dev = &pdev->dev; struct owl_ctx *ctx; - const char *eeprom_name; int err = 0; if (pci_enable_device(pdev)) @@ -215,11 +198,11 @@ static int owl_probe(struct pci_dev *pdev, if (err <= 0) return err; - eeprom_name = owl_get_eeprom_name(pdev); - if (!eeprom_name) { - dev_err(&pdev->dev, "no eeprom filename found.\n"); - return -ENODEV; - } + dev_dbg(dev, "using auto-generated eeprom filename\n"); + + /* this should match the pattern used in ath9k/init.c */ + scnprintf(eeprom_name, sizeof(eeprom_name), "ath9k-eeprom-pci-%s.bin", + dev_name(dev)); err = request_firmware_nowait(THIS_MODULE, true, eeprom_name, &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb); diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c index da102c791712..52e02e0752d8 100644 --- a/drivers/net/wireless/ath/ath9k/common-init.c +++ b/drivers/net/wireless/ath/ath9k/common-init.c @@ -133,13 +133,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common) ATH9K_NUM_CHANNELS); if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { - channels = devm_kzalloc(ah->dev, + channels = devm_kmemdup(ah->dev, ath9k_2ghz_chantable, sizeof(ath9k_2ghz_chantable), GFP_KERNEL); if (!channels) return -ENOMEM; - memcpy(channels, ath9k_2ghz_chantable, - sizeof(ath9k_2ghz_chantable)); common->sbands[NL80211_BAND_2GHZ].channels = channels; common->sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ; common->sbands[NL80211_BAND_2GHZ].n_channels = @@ -150,13 +148,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common) } if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { - channels = devm_kzalloc(ah->dev, + channels = devm_kmemdup(ah->dev, ath9k_5ghz_chantable, sizeof(ath9k_5ghz_chantable), GFP_KERNEL); if (!channels) return -ENOMEM; - memcpy(channels, ath9k_5ghz_chantable, - sizeof(ath9k_5ghz_chantable)); common->sbands[NL80211_BAND_5GHZ].channels = channels; common->sbands[NL80211_BAND_5GHZ].band = NL80211_BAND_5GHZ; common->sbands[NL80211_BAND_5GHZ].n_channels = diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 821909b81ea9..515267f48d80 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -454,7 +454,6 @@ static void hif_usb_stop(void *hif_handle) usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); - kfree(tx_buf->buf); kfree(tx_buf); spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); } @@ -811,7 +810,6 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) &hif_dev->tx.tx_buf, list) { list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); - kfree(tx_buf->buf); kfree(tx_buf); } spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); @@ -828,7 +826,6 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); - kfree(tx_buf->buf); kfree(tx_buf); spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); } @@ -849,14 +846,10 @@ static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) init_usb_anchor(&hif_dev->mgmt_submitted); for (i = 0; i < MAX_TX_URB_NUM; i++) { - tx_buf = kzalloc_obj(*tx_buf); + tx_buf = kzalloc_flex(*tx_buf, buf, MAX_TX_BUF_SIZE); if (!tx_buf) goto err; - tx_buf->buf = kzalloc(MAX_TX_BUF_SIZE, GFP_KERNEL); - if (!tx_buf->buf) - goto err; - tx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); if (!tx_buf->urb) goto err; @@ -871,10 +864,7 @@ static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) return 0; err: - if (tx_buf) { - kfree(tx_buf->buf); - kfree(tx_buf); - } + kfree(tx_buf); ath9k_hif_usb_dealloc_tx_urbs(hif_dev); return -ENOMEM; } @@ -1530,12 +1520,4 @@ static struct usb_driver ath9k_hif_usb_driver = { .disable_hub_initiated_lpm = 1, }; -int ath9k_hif_usb_init(void) -{ - return usb_register(&ath9k_hif_usb_driver); -} - -void ath9k_hif_usb_exit(void) -{ - usb_deregister(&ath9k_hif_usb_driver); -} +module_usb_driver(ath9k_hif_usb_driver); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index b3e66b0485a5..dc0b0fa5c325 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -77,13 +77,13 @@ extern int htc_use_dev_fw; #define HIF_USB_MAX_TXPIPES 4 struct tx_buf { - u8 *buf; u16 len; u16 offset; struct urb *urb; struct sk_buff_head skb_queue; struct hif_device_usb *hif_dev; struct list_head list; + u8 buf[]; }; struct rx_buf { @@ -136,8 +136,6 @@ struct hif_device_usb { u8 flags; /* HIF_USB_* */ }; -int ath9k_hif_usb_init(void); -void ath9k_hif_usb_exit(void); void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev); #endif /* HTC_USB_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 5270d0c0af17..6de78ae85726 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -1021,21 +1021,3 @@ int ath9k_htc_resume(struct htc_target *htc_handle) return ret; } #endif - -static int __init ath9k_htc_init(void) -{ - if (ath9k_hif_usb_init() < 0) { - pr_err("No USB devices found, driver not installed\n"); - return -ENODEV; - } - - return 0; -} -module_init(ath9k_htc_init); - -static void __exit ath9k_htc_exit(void) -{ - ath9k_hif_usb_exit(); - pr_info("Driver unloaded\n"); -} -module_exit(ath9k_htc_exit); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index a45351afcf6e..e08ab73fcacb 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include "hw.h" @@ -2498,19 +2498,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); - if (eeval & AR5416_OPFLAGS_11A) { - if (ah->disable_5ghz) - ath_warn(common, "disabling 5GHz band\n"); - else - pCap->hw_caps |= ATH9K_HW_CAP_5GHZ; - } + if (eeval & AR5416_OPFLAGS_11A) + pCap->hw_caps |= ATH9K_HW_CAP_5GHZ; - if (eeval & AR5416_OPFLAGS_11G) { - if (ah->disable_2ghz) - ath_warn(common, "disabling 2GHz band\n"); - else - pCap->hw_caps |= ATH9K_HW_CAP_2GHZ; - } + if (eeval & AR5416_OPFLAGS_11G) + pCap->hw_caps |= ATH9K_HW_CAP_2GHZ; if ((pCap->hw_caps & (ATH9K_HW_CAP_2GHZ | ATH9K_HW_CAP_5GHZ)) == 0) { ath_err(common, "both bands are disabled\n"); @@ -2719,19 +2711,28 @@ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah, u32 gpio, u32 type) static void ath9k_hw_gpio_cfg_soc(struct ath_hw *ah, u32 gpio, bool out, const char *label) { + enum gpiod_flags flags = out ? GPIOD_OUT_LOW : GPIOD_IN; + struct gpio_desc *gpiod; int err; - if (ah->caps.gpio_requested & BIT(gpio)) + if (ah->gpiods[gpio]) return; - err = devm_gpio_request_one(ah->dev, gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label); + /* + * Obtains a system specific GPIO descriptor from another GPIO controller. + * Ideally this should come from the device tree, this is a legacy code + * path. + */ + gpiod = gpiod_get_index(NULL, "ath9k", gpio, flags); + err = PTR_ERR_OR_ZERO(gpiod); if (err) { ath_err(ath9k_hw_common(ah), "request GPIO%d failed:%d\n", gpio, err); return; } - ah->caps.gpio_requested |= BIT(gpio); + gpiod_set_consumer_name(gpiod, label); + ah->gpiods[gpio] = gpiod; } static void ath9k_hw_gpio_cfg_wmac(struct ath_hw *ah, u32 gpio, bool out, @@ -2791,10 +2792,12 @@ void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio) if (!AR_SREV_SOC(ah)) return; - WARN_ON(gpio >= ah->caps.num_gpio_pins); + if (ah->gpiods[gpio]) { + gpiod_put(ah->gpiods[gpio]); + ah->gpiods[gpio] = NULL; + } - if (ah->caps.gpio_requested & BIT(gpio)) - ah->caps.gpio_requested &= ~BIT(gpio); + WARN_ON(gpio >= ah->caps.num_gpio_pins); } EXPORT_SYMBOL(ath9k_hw_gpio_free); @@ -2822,8 +2825,8 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) val = REG_READ(ah, AR_GPIO_IN(ah)) & BIT(gpio); else val = MS_REG_READ(AR, gpio); - } else if (BIT(gpio) & ah->caps.gpio_requested) { - val = gpio_get_value(gpio) & BIT(gpio); + } else if (ah->gpiods[gpio]) { + val = gpiod_get_value(ah->gpiods[gpio]); } else { WARN_ON(1); } @@ -2846,8 +2849,8 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) AR7010_GPIO_OUT : AR_GPIO_IN_OUT(ah); REG_RMW(ah, out_addr, val << gpio, BIT(gpio)); - } else if (BIT(gpio) & ah->caps.gpio_requested) { - gpio_set_value(gpio, val); + } else if (ah->gpiods[gpio]) { + gpiod_set_value(ah->gpiods[gpio], val); } else { WARN_ON(1); } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index eaa07d6dbde0..b942b8303d8f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -302,7 +303,6 @@ struct ath9k_hw_capabilities { u8 max_rxchains; u8 num_gpio_pins; u32 gpio_mask; - u32 gpio_requested; u8 rx_hp_qdepth; u8 rx_lp_qdepth; u8 rx_status_len; @@ -783,6 +783,7 @@ struct ath_hw { struct ath9k_hw_capabilities caps; struct ath9k_channel channels[ATH9K_NUM_CHANNELS]; struct ath9k_channel *curchan; + struct gpio_desc *gpiods[32]; union { struct ar5416_eeprom_def def; @@ -973,8 +974,6 @@ struct ath_hw { bool is_clk_25mhz; int (*get_mac_revision)(void); int (*external_reset)(void); - bool disable_2ghz; - bool disable_5ghz; const struct firmware *eeprom_blob; u16 *nvmem_blob; /* devres managed */ diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index ee951493e993..2f0c4ef86b7e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -285,7 +285,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, { struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ds; - int i, bsize, desc_len; + int i, desc_len; ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", name, nbuf, ndesc); @@ -339,8 +339,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, if (is_tx) { struct ath_buf *bf; - bsize = sizeof(struct ath_buf) * nbuf; - bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + bf = devm_kcalloc(sc->dev, sizeof(*bf), nbuf, GFP_KERNEL); if (!bf) return -ENOMEM; @@ -370,8 +369,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, } else { struct ath_rxbuf *bf; - bsize = sizeof(struct ath_rxbuf) * nbuf; - bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbuf, GFP_KERNEL); if (!bf) return -ENOMEM; @@ -576,7 +574,7 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) size_t len; int err; - cell = devm_nvmem_cell_get(sc->dev, "calibration"); + cell = nvmem_cell_get(sc->dev, "calibration"); if (IS_ERR(cell)) { err = PTR_ERR(cell); @@ -593,6 +591,7 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc) } buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); if (IS_ERR(buf)) return PTR_ERR(buf); @@ -958,7 +957,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 34c74ed99b7b..93b41a1bb2af 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -202,7 +202,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) struct sk_buff *skb; struct ath_rxbuf *bf; int error = 0, i; - u32 size; ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize - ah->caps.rx_status_len); @@ -212,8 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP], ah->caps.rx_hp_qdepth); - size = sizeof(struct ath_rxbuf) * nbufs; - bf = devm_kzalloc(sc->dev, size, GFP_KERNEL); + bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbufs, GFP_KERNEL); if (!bf) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 0ac9212e42f7..89d8b3178784 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1993,7 +1993,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) ieee80211_txq_schedule_start(hw, txq->mac80211_qnum); spin_lock_bh(&sc->chan_lock); - rcu_read_lock(); if (sc->cur_chan->stopped) goto out; @@ -2011,7 +2010,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) } out: - rcu_read_unlock(); spin_unlock_bh(&sc->chan_lock); ieee80211_txq_schedule_end(hw, txq->mac80211_qnum); } @@ -2746,6 +2744,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) continue; } + if (ts.qid >= ATH9K_NUM_TX_QUEUES) { + ath_dbg(common, XMIT, "invalid qid %d\n", ts.qid); + continue; + } + txq = &sc->tx.txq[ts.qid]; ath_txq_lock(sc, txq); diff --git a/drivers/net/wireless/ath/testmode_i.h b/drivers/net/wireless/ath/testmode_i.h index 980ef2f3f05f..78109d21a655 100644 --- a/drivers/net/wireless/ath/testmode_i.h +++ b/drivers/net/wireless/ath/testmode_i.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ /* "API" level of the ath testmode interface. Bump it after every diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 5a0ef2295a3a..44020ec265fb 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -16,8 +16,8 @@ /* DXE - DMA transfer engine * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX. - * through low channels data packets are transfered - * through high channels managment packets are transfered + * through low channels data packets are transferred + * through high channels management packets are transferred */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index c3f0860873de..ad8a4bd910d2 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1568,7 +1568,8 @@ static int wcn36xx_probe(struct platform_device *pdev) wcnss = dev_get_drvdata(pdev->dev.parent); - hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); + n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; + hw = ieee80211_alloc_hw(struct_size(wcn, chan_survey, n_channels), &wcn36xx_ops); if (!hw) { wcn36xx_err("failed to alloc hw\n"); ret = -ENOMEM; @@ -1590,16 +1591,6 @@ 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_kcalloc(wcn->dev, - n_channels, - sizeof(struct wcn36xx_chan_survey), - 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 813553edcb78..c0b477345832 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2599,6 +2599,9 @@ static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba if (rsp->candidate_cnt < 1) return rsp->status ? rsp->status : -EINVAL; + if (len < sizeof(*rsp) + sizeof(*candidate)) + return -EINVAL; + candidate = (struct wcn36xx_hal_trigger_ba_rsp_candidate *)(buf + sizeof(*rsp)); for (i = 0; i < STACFG_MAX_TC; i++) { @@ -2805,6 +2808,12 @@ static int wcn36xx_smd_print_reg_info_ind(struct wcn36xx *wcn, return -EIO; } + if (rsp->count > (len - sizeof(*rsp)) / sizeof(rsp->regs[0])) { + wcn36xx_warn("Truncated print reg info indication: count %u, len %zu\n", + rsp->count, len); + return -EIO; + } + wcn36xx_dbg(WCN36XX_DBG_HAL, "reginfo indication, scenario: 0x%x reason: 0x%x\n", rsp->scenario, rsp->reason); @@ -3293,6 +3302,10 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, case WCN36XX_HAL_EXIT_IMPS_RSP: case WCN36XX_HAL_UPDATE_CHANNEL_LIST_RSP: case WCN36XX_HAL_ADD_BCN_FILTER_RSP: + if (len > WCN36XX_HAL_BUF_SIZE) { + wcn36xx_warn("HAL response too large: %d\n", len); + break; + } memcpy(wcn->hal_buf, buf, len); wcn->hal_rsp_len = len; complete(&wcn->hal_rsp_compl); diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index 7ee79593cd23..8c43f67bd780 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -298,7 +298,7 @@ struct wcn36xx { struct ieee80211_channel *channel; spinlock_t survey_lock; /* protects chan_survey */ - struct wcn36xx_chan_survey *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/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 3d6e5aad48b1..d6ef92cfcbaf 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -1734,7 +1735,7 @@ static int wil_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, - u64 *cookie) + u64 *cookie, const u8 *rx_addr) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index b0e6aeb0b872..817448e58e7f 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -117,6 +117,7 @@ MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if over static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x15, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x16, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS), @@ -2344,6 +2345,10 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) if (phy->type == B43_PHYTYPE_N) filename = "ucode16_mimo"; break; + case 22: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode22_mimo"; + break; case 16 ... 19: if (phy->type == B43_PHYTYPE_N) filename = "ucode16_mimo"; @@ -2405,7 +2410,9 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else if (rev == 24) filename = "n0initvals24"; else if (rev == 23) - filename = "n0initvals16"; /* What about n0initvals22? */ + filename = "n0initvals16"; + else if (rev == 22) + filename = "n0initvals22"; else if (rev >= 16 && rev <= 18) filename = "n0initvals16"; else if (rev >= 11 && rev <= 12) @@ -2465,7 +2472,9 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else if (rev == 24) filename = "n0bsinitvals24"; else if (rev == 23) - filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */ + filename = "n0bsinitvals16"; + else if (rev == 22) + filename = "n0bsinitvals22"; else if (rev >= 16 && rev <= 18) filename = "n0bsinitvals16"; else if (rev >= 11 && rev <= 12) @@ -4554,7 +4563,11 @@ static int b43_phy_versioning(struct b43_wldev *dev) radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA); radio_ver = 0; /* Is there version somewhere? */ - } else if (core_rev >= 24) { + } else if (core_rev >= 24 || core_rev == 22) { + /* + * D11 corerev 22 pairs an older 802.11 core with a 2057 + * radio that requires the 24-bit indirect access path. + */ u16 radio24[3]; for (tmp = 0; tmp < 3; tmp++) { @@ -4604,7 +4617,8 @@ static int b43_phy_versioning(struct b43_wldev *dev) radio_id != 0x2057) unsupported = 1; if (radio_id == 0x2057 && - !(radio_rev == 9 || radio_rev == 14)) + !(radio_rev == 8 || radio_rev == 9 || + radio_rev == 14)) unsupported = 1; break; case B43_PHYTYPE_LP: diff --git a/drivers/net/wireless/broadcom/b43/radio_2057.c b/drivers/net/wireless/broadcom/b43/radio_2057.c index bd7dafb567ff..e761f899bacd 100644 --- a/drivers/net/wireless/broadcom/b43/radio_2057.c +++ b/drivers/net/wireless/broadcom/b43/radio_2057.c @@ -73,24 +73,112 @@ static u16 r2057_rev7_init[][2] = { { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; -/* TODO: Which devices should use it? +/* Extracted from MMIO dump of 6.30.102.7 */ static u16 r2057_rev8_init[][2] = { - { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, - { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, - { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, - { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, { 0x7C, 0x0f }, - { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, - { 0xA1, 0x20 }, { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, - { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0xF3, 0x58 }, - { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x0f }, { 0x102, 0xee }, - { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, - { 0x14E, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, - { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, - { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, - { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, - { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, + { 0x0000, 0x0008 }, { 0x0001, 0x0057 }, { 0x0002, 0x0020 }, { 0x0003, 0x001f }, + { 0x0004, 0x0004 }, { 0x0005, 0x0002 }, { 0x0006, 0x0001 }, { 0x0007, 0x0001 }, + { 0x0008, 0x0001 }, { 0x0009, 0x0069 }, { 0x000a, 0x0066 }, { 0x000b, 0x0006 }, + { 0x000c, 0x0018 }, { 0x000d, 0x0003 }, { 0x000e, 0x0020 }, { 0x000f, 0x0020 }, + { 0x0010, 0x0000 }, { 0x0011, 0x007c }, { 0x0012, 0x0042 }, { 0x0013, 0x00bd }, + { 0x0014, 0x0007 }, { 0x0015, 0x0087 }, { 0x0016, 0x0008 }, { 0x0017, 0x0017 }, + { 0x0018, 0x0007 }, { 0x0019, 0x0000 }, { 0x001a, 0x0002 }, { 0x001b, 0x0013 }, + { 0x001c, 0x003e }, { 0x001d, 0x003e }, { 0x001e, 0x0096 }, { 0x001f, 0x0004 }, + { 0x0020, 0x0000 }, { 0x0021, 0x0000 }, { 0x0022, 0x0017 }, { 0x0023, 0x0006 }, + { 0x0024, 0x0001 }, { 0x0025, 0x0006 }, { 0x0026, 0x0004 }, { 0x0027, 0x000d }, + { 0x0028, 0x000d }, { 0x0029, 0x0030 }, { 0x002a, 0x0032 }, { 0x002b, 0x0008 }, + { 0x002c, 0x001c }, { 0x002d, 0x0002 }, { 0x002e, 0x0004 }, { 0x002f, 0x007f }, + { 0x0030, 0x0027 }, { 0x0031, 0x0000 }, { 0x0032, 0x0000 }, { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, { 0x0035, 0x0020 }, { 0x0036, 0x0018 }, { 0x0037, 0x0007 }, + { 0x0038, 0x0066 }, { 0x0039, 0x0066 }, { 0x003a, 0x0066 }, { 0x003b, 0x0066 }, + { 0x003c, 0x00ff }, { 0x003d, 0x00ff }, { 0x003e, 0x00ff }, { 0x003f, 0x00ff }, + { 0x0040, 0x0016 }, { 0x0041, 0x0007 }, { 0x0042, 0x0029 }, { 0x0043, 0x0007 }, + { 0x0044, 0x0006 }, { 0x0045, 0x0003 }, { 0x0046, 0x0001 }, { 0x0047, 0x0007 }, + { 0x0048, 0x0088 }, { 0x0049, 0x0005 }, { 0x004a, 0x0077 }, { 0x004b, 0x0066 }, + { 0x004c, 0x0066 }, { 0x004d, 0x0000 }, { 0x004e, 0x0004 }, { 0x004f, 0x000c }, + { 0x0050, 0x0000 }, { 0x0051, 0x0070 }, { 0x0056, 0x0007 }, { 0x0057, 0x0000 }, + { 0x0058, 0x0000 }, { 0x0059, 0x0088 }, { 0x005a, 0x0000 }, { 0x005b, 0x001f }, + { 0x005c, 0x0020 }, { 0x005d, 0x0001 }, { 0x005e, 0x0030 }, { 0x005f, 0x0070 }, + { 0x0060, 0x0000 }, { 0x0061, 0x0000 }, { 0x0062, 0x0033 }, { 0x0063, 0x000f }, + { 0x0064, 0x0013 }, { 0x0065, 0x0000 }, { 0x0066, 0x00ee }, { 0x0069, 0x0000 }, + { 0x006a, 0x007e }, { 0x006b, 0x003f }, { 0x006c, 0x007f }, { 0x006d, 0x0078 }, + { 0x006e, 0x0058 }, { 0x006f, 0x0088 }, { 0x0070, 0x0008 }, { 0x0071, 0x000f }, + { 0x0072, 0x00bc }, { 0x0073, 0x0008 }, { 0x0074, 0x0060 }, { 0x0075, 0x001a }, + { 0x0076, 0x0070 }, { 0x0077, 0x0000 }, { 0x0078, 0x0000 }, { 0x0079, 0x0000 }, + { 0x007a, 0x0033 }, { 0x007b, 0x001a }, { 0x007c, 0x0014 }, { 0x007d, 0x00ee }, + { 0x0080, 0x003c }, { 0x0081, 0x0001 }, { 0x0082, 0x000a }, { 0x0083, 0x009d }, + { 0x0084, 0x000a }, { 0x0085, 0x0000 }, { 0x0086, 0x0040 }, { 0x0087, 0x0040 }, + { 0x0088, 0x0088 }, { 0x0089, 0x0010 }, { 0x008a, 0x00f0 }, { 0x008b, 0x0010 }, + { 0x008c, 0x00f0 }, { 0x008d, 0x0000 }, { 0x008e, 0x0000 }, { 0x008f, 0x0010 }, + { 0x0090, 0x0055 }, { 0x0091, 0x003f }, { 0x0092, 0x0036 }, { 0x0093, 0x0000 }, + { 0x0094, 0x0000 }, { 0x0095, 0x0000 }, { 0x0096, 0x0087 }, { 0x0097, 0x0011 }, + { 0x0098, 0x0000 }, { 0x0099, 0x0033 }, { 0x009a, 0x0088 }, { 0x009b, 0x0000 }, + { 0x009c, 0x0087 }, { 0x009d, 0x0011 }, { 0x009e, 0x0000 }, { 0x009f, 0x0033 }, + { 0x00a0, 0x0088 }, { 0x00a1, 0x0020 }, { 0x00a2, 0x003f }, { 0x00a3, 0x0044 }, + { 0x00a4, 0x008c }, { 0x00a5, 0x006c }, { 0x00a6, 0x0022 }, { 0x00a7, 0x00be }, + { 0x00a8, 0x0055 }, { 0x00aa, 0x000c }, { 0x00ab, 0x00aa }, { 0x00ac, 0x0002 }, + { 0x00ad, 0x0000 }, { 0x00ae, 0x0010 }, { 0x00af, 0x0001 }, { 0x00b0, 0x0000 }, + { 0x00b1, 0x0000 }, { 0x00b2, 0x0080 }, { 0x00b3, 0x0060 }, { 0x00b4, 0x0044 }, + { 0x00b5, 0x0055 }, { 0x00b6, 0x0001 }, { 0x00b7, 0x0055 }, { 0x00b8, 0x0001 }, + { 0x00b9, 0x0005 }, { 0x00ba, 0x0055 }, { 0x00bb, 0x0055 }, { 0x00c1, 0x0000 }, + { 0x00c2, 0x0000 }, { 0x00c3, 0x0000 }, { 0x00c4, 0x0000 }, { 0x00c5, 0x0000 }, + { 0x00c6, 0x0000 }, { 0x00c7, 0x0000 }, { 0x00c8, 0x0000 }, { 0x00c9, 0x0001 }, + { 0x00ca, 0x0000 }, { 0x00cb, 0x0000 }, { 0x00cc, 0x0000 }, { 0x00cd, 0x0000 }, + { 0x00ce, 0x005e }, { 0x00cf, 0x000c }, { 0x00d0, 0x000c }, { 0x00d1, 0x000c }, + { 0x00d2, 0x0000 }, { 0x00d3, 0x002b }, { 0x00d4, 0x000c }, { 0x00d5, 0x0000 }, + { 0x00d6, 0x0070 }, { 0x00db, 0x0007 }, { 0x00dc, 0x0000 }, { 0x00dd, 0x0000 }, + { 0x00de, 0x0088 }, { 0x00df, 0x0000 }, { 0x00e0, 0x001f }, { 0x00e1, 0x0020 }, + { 0x00e2, 0x0001 }, { 0x00e3, 0x0030 }, { 0x00e4, 0x0070 }, { 0x00e5, 0x0000 }, + { 0x00e6, 0x0000 }, { 0x00e7, 0x0033 }, { 0x00e8, 0x000f }, { 0x00e9, 0x0013 }, + { 0x00ea, 0x0000 }, { 0x00eb, 0x00ee }, { 0x00ee, 0x0000 }, { 0x00ef, 0x007e }, + { 0x00f0, 0x003f }, { 0x00f1, 0x007f }, { 0x00f2, 0x0078 }, { 0x00f3, 0x0058 }, + { 0x00f4, 0x0088 }, { 0x00f5, 0x0008 }, { 0x00f6, 0x000f }, { 0x00f7, 0x00bc }, + { 0x00f8, 0x0008 }, { 0x00f9, 0x0060 }, { 0x00fa, 0x001a }, { 0x00fb, 0x0070 }, + { 0x00fc, 0x0000 }, { 0x00fd, 0x0000 }, { 0x00fe, 0x0000 }, { 0x00ff, 0x0033 }, + { 0x0100, 0x001a }, { 0x0101, 0x0014 }, { 0x0102, 0x00ee }, { 0x0105, 0x003c }, + { 0x0106, 0x0001 }, { 0x0107, 0x000a }, { 0x0108, 0x009d }, { 0x0109, 0x000a }, + { 0x010a, 0x0000 }, { 0x010b, 0x0040 }, { 0x010c, 0x0040 }, { 0x010d, 0x0088 }, + { 0x010e, 0x0010 }, { 0x010f, 0x00f0 }, { 0x0110, 0x0010 }, { 0x0111, 0x00f0 }, + { 0x0112, 0x0000 }, { 0x0113, 0x0000 }, { 0x0114, 0x0010 }, { 0x0115, 0x0055 }, + { 0x0116, 0x003f }, { 0x0117, 0x0036 }, { 0x0118, 0x0000 }, { 0x0119, 0x0000 }, + { 0x011a, 0x0000 }, { 0x011b, 0x0087 }, { 0x011c, 0x0011 }, { 0x011d, 0x0000 }, + { 0x011e, 0x0033 }, { 0x011f, 0x0088 }, { 0x0120, 0x0000 }, { 0x0121, 0x0087 }, + { 0x0122, 0x0011 }, { 0x0123, 0x0000 }, { 0x0124, 0x0033 }, { 0x0125, 0x0088 }, + { 0x0126, 0x0020 }, { 0x0127, 0x003f }, { 0x0128, 0x0044 }, { 0x0129, 0x008c }, + { 0x012a, 0x006c }, { 0x012b, 0x0022 }, { 0x012c, 0x00be }, { 0x012d, 0x0055 }, + { 0x012f, 0x000c }, { 0x0130, 0x00aa }, { 0x0131, 0x0002 }, { 0x0132, 0x0000 }, + { 0x0133, 0x0010 }, { 0x0134, 0x0001 }, { 0x0135, 0x0000 }, { 0x0136, 0x0000 }, + { 0x0137, 0x0080 }, { 0x0138, 0x0060 }, { 0x0139, 0x0044 }, { 0x013a, 0x0055 }, + { 0x013b, 0x0001 }, { 0x013c, 0x0055 }, { 0x013d, 0x0001 }, { 0x013e, 0x0005 }, + { 0x013f, 0x0055 }, { 0x0140, 0x0055 }, { 0x0146, 0x0000 }, { 0x0147, 0x0000 }, + { 0x0148, 0x0000 }, { 0x0149, 0x0000 }, { 0x014a, 0x0000 }, { 0x014b, 0x0000 }, + { 0x014c, 0x0000 }, { 0x014d, 0x0000 }, { 0x014e, 0x0001 }, { 0x014f, 0x0000 }, + { 0x0150, 0x0000 }, { 0x0151, 0x0000 }, { 0x0154, 0x000c }, { 0x0155, 0x000c }, + { 0x0156, 0x000c }, { 0x0157, 0x0000 }, { 0x0158, 0x002b }, { 0x0159, 0x0084 }, + { 0x015a, 0x0015 }, { 0x015b, 0x000f }, { 0x015c, 0x0000 }, { 0x015d, 0x0000 }, + { 0x015e, 0x0000 }, { 0x015f, 0x0000 }, { 0x0160, 0x0000 }, { 0x0161, 0x0000 }, + { 0x0162, 0x0000 }, { 0x0163, 0x0000 }, { 0x0164, 0x0000 }, { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, { 0x0167, 0x0000 }, { 0x0168, 0x0000 }, { 0x0169, 0x0000 }, + { 0x016a, 0x0000 }, { 0x016b, 0x0000 }, { 0x016c, 0x0000 }, { 0x016d, 0x0000 }, + { 0x0170, 0x0000 }, { 0x0171, 0x0077 }, { 0x0172, 0x0077 }, { 0x0173, 0x0077 }, + { 0x0174, 0x0077 }, { 0x0175, 0x0000 }, { 0x0176, 0x0003 }, { 0x0177, 0x0037 }, + { 0x0178, 0x0003 }, { 0x0179, 0x0000 }, { 0x017a, 0x0021 }, { 0x017b, 0x0002 }, + { 0x017c, 0x0000 }, { 0x017d, 0x00aa }, { 0x017e, 0x0000 }, { 0x017f, 0x00aa }, + { 0x0180, 0x0000 }, { 0x0190, 0x0000 }, { 0x0191, 0x0077 }, { 0x0192, 0x0077 }, + { 0x0193, 0x0077 }, { 0x0194, 0x0077 }, { 0x0195, 0x0000 }, { 0x0196, 0x0003 }, + { 0x0197, 0x0037 }, { 0x0198, 0x0003 }, { 0x0199, 0x0000 }, { 0x019a, 0x0021 }, + { 0x019b, 0x0002 }, { 0x019c, 0x0000 }, { 0x019d, 0x00aa }, { 0x019e, 0x0000 }, + { 0x019f, 0x00aa }, { 0x01a0, 0x0000 }, { 0x01a1, 0x0002 }, { 0x01a2, 0x000f }, + { 0x01a3, 0x000f }, { 0x01a4, 0x0000 }, { 0x01a5, 0x0000 }, { 0x01a6, 0x0000 }, + { 0x01a7, 0x0002 }, { 0x01a8, 0x000f }, { 0x01a9, 0x000f }, { 0x01aa, 0x0000 }, + { 0x01ab, 0x0000 }, { 0x01ac, 0x0000 }, { 0x01ad, 0x0084 }, { 0x01ae, 0x0060 }, + { 0x01af, 0x0047 }, { 0x01b0, 0x0047 }, { 0x01b1, 0x0000 }, { 0x01b2, 0x0000 }, + { 0x01b3, 0x0000 }, { 0x01b4, 0x0000 }, { 0x01b5, 0x0000 }, { 0x01b6, 0x0000 }, + { 0x01b7, 0x0005 }, { 0x01b8, 0x0000 }, { 0x01b9, 0x0000 }, { 0x01ba, 0x0000 }, + { 0x01bb, 0x0000 }, { 0x01bc, 0x0000 }, { 0x01bd, 0x0000 }, { 0x01be, 0x0000 }, + { 0x01bf, 0x0000 }, { 0x01c0, 0x0000 }, { 0x01c1, 0x0000 }, { 0x01c2, 0x00a0 }, + { 0x01c3, 0x0000 }, { 0x01c4, 0x0000 }, { 0x01c5, 0x0000 }, { 0x01c6, 0x0000 }, + { 0x01c7, 0x0000 }, { 0x01c8, 0x0000 }, { 0x01c9, 0x0000 }, { 0x01ca, 0x0000 }, }; -*/ /* Extracted from MMIO dump of 6.30.223.141 */ static u16 r2057_rev9_init[][2] = { @@ -357,6 +445,109 @@ static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio }, }; +/* Extracted from MMIO dump of 6.30.102.7 */ +static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev8[] = { + { + .freq = 2412, + RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0f, 0x09, 0x07, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0f, 0x09, 0x07, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 2467, + RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, + 0x09, 0x0f, 0x07, 0x04, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { + .freq = 2472, + RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, + 0x09, 0x0f, 0x07, 0x04, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 2484, + RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, + 0x09, 0x0f, 0x07, 0x04, 0x61, 0x73, 0xe0, 0x61, + 0x73, 0xe0), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + } +}; + + /* Extracted from MMIO dump of 6.30.223.141 */ static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { { @@ -539,6 +730,10 @@ void r2057_upload_inittabs(struct b43_wldev *dev) } else if (phy->radio_rev == 7) { table = r2057_rev7_init[0]; size = ARRAY_SIZE(r2057_rev7_init); + } else if (phy->radio_rev == 8) { + /* BCM6362 single-die 2.4 GHz. */ + table = r2057_rev8_init[0]; + size = ARRAY_SIZE(r2057_rev8_init); } break; case 9: @@ -586,6 +781,9 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, if (phy->radio_rev == 5) { e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5; len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5); + } else if (phy->radio_rev == 8) { + e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev8; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev8); } break; case 16: diff --git a/drivers/net/wireless/broadcom/b43/tables_nphy.c b/drivers/net/wireless/broadcom/b43/tables_nphy.c index 41a25d909d0d..ecd660b9c75a 100644 --- a/drivers/net/wireless/broadcom/b43/tables_nphy.c +++ b/drivers/net/wireless/broadcom/b43/tables_nphy.c @@ -2715,6 +2715,43 @@ static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = { 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, }; +/* Extracted from MMIO dump of 6.30.102.7 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev8_2g[] = { + 0x40ff0031, 0x40e70031, 0x40e7002e, 0x40cf002e, + 0x40bf002e, 0x40af002e, 0x409f002f, 0x407f0033, + 0x407f0031, 0x407f002e, 0x4077002e, 0x406f002e, + 0x4067002e, 0x405f002f, 0x40570030, 0x4057002d, + 0x404f002e, 0x40470031, 0x4047002e, 0x4047002c, + 0x40470029, 0x403f002c, 0x403f0029, 0x4037002d, + 0x4037002a, 0x40370028, 0x402f002c, 0x402f002a, + 0x402f0028, 0x402f0026, 0x4027002c, 0x40270029, + 0x40270027, 0x40270025, 0x40270023, 0x401f002c, + 0x401f002a, 0x401f0028, 0x401f0025, 0x401f0024, + 0x401f0022, 0x401f001f, 0x4017002d, 0x4017002b, + 0x40170028, 0x40170026, 0x40170024, 0x40170022, + 0x40170020, 0x4017001e, 0x4017001d, 0x4017001b, + 0x4017001a, 0x40170018, 0x40170017, 0x40170015, + 0x400f002c, 0x400f0029, 0x400f0027, 0x400f0024, + 0x400f0022, 0x400f0021, 0x400f001f, 0x400f001d, + 0x400f001b, 0x400f001a, 0x400f0018, 0x400f0017, + 0x400f0016, 0x400f0015, 0x400f0115, 0x400f0215, + 0x400f0315, 0x400f0415, 0x400f0515, 0x400f0615, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, +}; + + /* Extracted from MMIO dump of 6.30.223.141 */ static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, @@ -2886,6 +2923,21 @@ static const s16 b43_ntab_rf_pwr_offset_2057_rev9_5g[] = { 0, }; +/* Sourced from the rev 5 sibling: the rev 8 IPA TX gain table + * shares the low 24 bits of every entry with rev 5 (only the + * PAD-gain selector byte differs), so the same gain index maps to + * the same physical PAD gain code on both revisions. + */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev8_2g[] = { + -109, -109, -82, -68, -58, + -50, -44, -39, -35, -31, + -28, -26, -23, -21, -19, + -17, -16, -14, -13, -11, + -10, -9, -8, -7, -5, + -5, -4, -3, -2, -1, + -1, 0, +}; + /* Extracted from MMIO dump of 6.30.223.248 * Entries: 0, 26, 28, 29, 30, 31 were guessed */ @@ -3651,6 +3703,8 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) case 8: if (phy->radio_rev == 5) return b43_ntab_tx_gain_ipa_2057_rev5_2g; + if (phy->radio_rev == 8) + return b43_ntab_tx_gain_ipa_2057_rev8_2g; break; case 6: if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) @@ -3743,6 +3797,10 @@ const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev) if (phy->radio_rev == 9) return b43_ntab_rf_pwr_offset_2057_rev9_2g; break; + case 8: + if (phy->radio_rev == 8) + return b43_ntab_rf_pwr_offset_2057_rev8_2g; + break; } b43err(dev->wl, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c index df7e3bee19f2..35cbcea0abc9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c @@ -360,20 +360,15 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) struct brcmf_flowring *flow; u32 i; - flow = kzalloc_obj(*flow); + flow = kzalloc_flex(*flow, rings, nrofrings); if (flow) { - flow->dev = dev; flow->nrofrings = nrofrings; + flow->dev = dev; spin_lock_init(&flow->block_lock); for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) flow->addr_mode[i] = ADDR_INDIRECT; for (i = 0; i < ARRAY_SIZE(flow->hash); i++) flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; - flow->rings = kzalloc_objs(*flow->rings, nrofrings); - if (!flow->rings) { - kfree(flow); - flow = NULL; - } } return flow; @@ -399,7 +394,6 @@ void brcmf_flowring_detach(struct brcmf_flowring *flow) search = search->next; kfree(remove); } - kfree(flow->rings); kfree(flow); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h index 818882b0fd01..f3d511f9a3c9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h @@ -38,12 +38,12 @@ struct brcmf_flowring_tdls_entry { struct brcmf_flowring { struct device *dev; struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; - struct brcmf_flowring_ring **rings; spinlock_t block_lock; enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; u16 nrofrings; bool tdls_active; struct brcmf_flowring_tdls_entry *tdls_entry; + struct brcmf_flowring_ring *rings[] __counted_by(nrofrings); }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index e1752a513c73..92c16a317328 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -970,10 +970,12 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) * @channel: channel to stay on. * @duration: time in ms to remain on channel. * @cookie: cookie. + * @rx_addr: Address to match against the destination of received frames */ int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *channel, - unsigned int duration, u64 *cookie) + unsigned int duration, u64 *cookie, + const u8 *rx_addr) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index d3137ebd7158..9f3f01ade2b7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -157,7 +157,8 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif); int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *channel, - unsigned int duration, u64 *cookie); + unsigned int duration, u64 *cookie, + const u8 *rx_addr); int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 45b342ea0637..13662aa4b4ea 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2708,17 +2708,18 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { #define BRCMF_PCIE_DEVICE(dev_id, fw_vend) \ { \ - BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ - PCI_ANY_ID, PCI_ANY_ID, \ - PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ - BRCMF_DRVDATA_ ## fw_vend \ + PCI_DEVICE(BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id)), \ + .class = PCI_CLASS_NETWORK_OTHER << 8, \ + .class_mask = 0xffff00, \ + .driver_data = BRCMF_DRVDATA_ ## fw_vend, \ } #define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev, fw_vend) \ { \ - BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ - (subvend), (subdev), \ - PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, \ - BRCMF_DRVDATA_ ## fw_vend \ + PCI_DEVICE_SUB(BRCM_PCIE_VENDOR_ID_BROADCOM, (dev_id), \ + (subvend), (subdev)), \ + .class = PCI_CLASS_NETWORK_OTHER << 8, \ + .class_mask = 0xffff00, \ + .driver_data = BRCMF_DRVDATA_ ## fw_vend, \ } static const struct pci_device_id brcmf_pcie_devid_table[] = { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index c7eaf160e1fa..ec3aeaa7bab9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -418,21 +418,12 @@ static int brcms_chspec_bw(u16 chanspec) return BRCMS_10_MHZ; } -static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) -{ - if (cfg == NULL) - return; - - kfree(cfg->current_bss); - kfree(cfg); -} - static void brcms_c_detach_mfree(struct brcms_c_info *wlc) { if (wlc == NULL) return; - brcms_c_bsscfg_mfree(wlc->bsscfg); + kfree(wlc->bsscfg); kfree(wlc->pub); kfree(wlc->modulecb); kfree(wlc->default_bss); @@ -453,25 +444,6 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc) kfree(wlc); } -static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) -{ - struct brcms_bss_cfg *cfg; - - cfg = kzalloc_obj(*cfg, GFP_ATOMIC); - if (cfg == NULL) - goto fail; - - cfg->current_bss = kzalloc_obj(*cfg->current_bss, GFP_ATOMIC); - if (cfg->current_bss == NULL) - goto fail; - - return cfg; - - fail: - brcms_c_bsscfg_mfree(cfg); - return NULL; -} - static struct brcms_c_info * brcms_c_attach_malloc(uint unit, uint *err, uint devid) { @@ -527,7 +499,7 @@ brcms_c_attach_malloc(uint unit, uint *err, uint devid) goto fail; } - wlc->bsscfg = brcms_c_bsscfg_malloc(unit); + wlc->bsscfg = kzalloc_obj(*wlc->bsscfg, GFP_ATOMIC); if (wlc->bsscfg == NULL) { *err = 1011; goto fail; @@ -3813,7 +3785,7 @@ static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) wlc->home_chanspec = chanspec; if (wlc->pub->associated) - wlc->bsscfg->current_bss->chanspec = chanspec; + wlc->bsscfg->current_bss.chanspec = chanspec; } } @@ -5423,7 +5395,7 @@ void brcms_c_get_current_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs; if (wlc->pub->associated) - rs = &wlc->bsscfg->current_bss->rateset; + rs = &wlc->bsscfg->current_bss.rateset; else rs = &wlc->default_bss->rateset; @@ -5450,7 +5422,7 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs) if (wlc->pub->_n_enab & SUPPORT_11N) { struct brcms_bss_info *mcsset_bss; if (wlc->pub->associated) - mcsset_bss = wlc->bsscfg->current_bss; + mcsset_bss = &wlc->bsscfg->current_bss; else mcsset_bss = wlc->default_bss; memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0], @@ -7809,7 +7781,7 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) u32 bi; /* get beacon period and convert to uS */ - bi = wlc->bsscfg->current_bss->beacon_period << 10; + bi = wlc->bsscfg->current_bss.beacon_period << 10; /* * update since init path would reset * to default value diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h index b7ca0d9891c4..2d133264f4a7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h @@ -614,7 +614,7 @@ struct brcms_bss_cfg { u8 SSID_len; u8 SSID[IEEE80211_MAX_SSID_LEN]; u8 BSSID[ETH_ALEN]; - struct brcms_bss_info *current_bss; + struct brcms_bss_info current_bss; }; int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c index 8cec5ad79fda..d65a68045ca8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c @@ -1607,7 +1607,6 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) { uint i; const struct chan_info_2064_lcnphy *ci; - u8 rfpll_doubler = 0; u8 pll_pwrup, pll_pwrup_ovr; s32 qFcal; u8 d15, d16, f16, e44, e45; @@ -1618,18 +1617,12 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) u16 g30, d28; ci = &chan_info_2064_lcnphy[0]; - rfpll_doubler = 1; mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2); write_radio_reg(pi, RADIO_2064_REG09E, 0xf); - if (!rfpll_doubler) { - loop_bw = PLL_2064_LOOP_BW; - d30 = PLL_2064_D30; - } else { - loop_bw = PLL_2064_LOOP_BW_DOUBLER; - d30 = PLL_2064_D30_DOUBLER; - } + loop_bw = PLL_2064_LOOP_BW_DOUBLER; + d30 = PLL_2064_D30_DOUBLER; if (CHSPEC_IS2G(pi->radio_chanspec)) { for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++) @@ -1669,7 +1662,7 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) e44 = 0; e45 = 0; - fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq); + fpfd = pi->xtalfreq << 1; if (pi->xtalfreq > 26000000) e44 = 1; if (pi->xtalfreq > 52000000) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index dd64d21174e3..4bc9bb406e8e 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -11366,37 +11366,41 @@ static int ipw_wdev_init(struct net_device *dev) /* PCI driver stuff */ static const struct pci_device_id card_ids[] = { - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0}, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2701) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2702) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2711) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2712) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2721) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2722) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2731) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2732) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2741) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x103c, 0x2741) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2742) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2751) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2752) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2753) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2754) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2761) }, + { PCI_VDEVICE_SUB(INTEL, 0x1043, 0x8086, 0x2762) }, + /* * This ID conflicts with i40e, but the devices can be differentiated * because i40e devices use PCI_CLASS_NETWORK_ETHERNET and ipw2200 * devices use PCI_CLASS_NETWORK_OTHER. */ - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x104f), - PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0}, - {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */ - {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */ - {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */ - {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */ + { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x104f), + .class = PCI_CLASS_NETWORK_OTHER << 8, + .class_mask = 0xffff00, + }, + { PCI_VDEVICE(INTEL, 0x4220) }, /* BG */ + { PCI_VDEVICE(INTEL, 0x4221) }, /* BG */ + { PCI_VDEVICE(INTEL, 0x4223) }, /* ABG */ + { PCI_VDEVICE(INTEL, 0x4224) }, /* ABG */ /* required last entry */ - {0,} + { } }; MODULE_DEVICE_TABLE(pci, card_ids); diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 941257b811b4..2a74efe09cf0 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -32,7 +32,7 @@ iwlwifi-objs += iwl-dbg-tlv.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/img.o fw/notif-wait.o fw/rs.o -iwlwifi-objs += fw/dbg.o fw/pnvm.o fw/dump.o +iwlwifi-objs += fw/dbg.o fw/dbg-old.o fw/pnvm.o fw/dump.o iwlwifi-objs += fw/regulatory.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o iwlwifi-$(CONFIG_IWLMLD) += fw/smem.o fw/init.o diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index f0453f3f6ba6..01ca65eb5acd 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include @@ -15,26 +15,26 @@ /* Lowest firmware API version supported */ #define IWL_22000_UCODE_API_MIN 77 -/* Memory offsets and lengths */ -#define IWL_22000_SMEM_OFFSET 0x400000 -#define IWL_22000_SMEM_LEN 0xD0000 - #define IWL_CC_A_FW_PRE "iwlwifi-cc-a0" +#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0" +#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0" +#define IWL_QUZ_A_HR_B_FW_PRE "iwlwifi-QuZ-a0-hr-b0" #define IWL_CC_A_MODULE_FIRMWARE(api) \ IWL_CC_A_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_QU_B_HR_B_MODULE_FIRMWARE(api) \ + IWL_QU_B_HR_B_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_QUZ_A_HR_B_MODULE_FIRMWARE(api) \ + IWL_QUZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_QU_C_HR_B_MODULE_FIRMWARE(api) \ + IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode" static const struct iwl_family_base_params iwl_22000_base = { .num_of_queues = 512, .max_tfd_queue_size = 256, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, - .smem_offset = IWL_22000_SMEM_OFFSET, - .smem_len = IWL_22000_SMEM_LEN, .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .apmg_not_supported = true, .mac_addr_from_csr = 0x380, @@ -113,4 +113,7 @@ const char iwl_ax201_killer_1650s_name[] = const char iwl_ax201_killer_1650i_name[] = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)"; +MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_CC_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c index f987ad3192c1..1be72d71fccf 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2020, 2023, 2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2020, 2023, 2025-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 Intel Deutschland GmbH */ @@ -53,10 +53,7 @@ static const struct iwl_family_base_params iwl7000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_16K, .num_of_queues = 31, .max_tfd_queue_size = 256, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, .apmg_wake_up_wa = true, diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c index 3c844cd419e8..834aa520ab0c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2014, 2018-2020, 2023, 2025 Intel Corporation + * Copyright (C) 2014, 2018-2020, 2023, 2025-2026 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -39,10 +39,7 @@ static const struct iwl_family_base_params iwl8000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, .num_of_queues = 31, .max_tfd_queue_size = 256, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, .nvm_hw_section_num = 10, diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index 5872fc9b8caf..2954434ce851 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021, 2023, 2025 Intel Corporation + * Copyright (C) 2018-2021, 2023, 2025-2026 Intel Corporation */ #include #include @@ -30,10 +30,7 @@ static const struct iwl_family_base_params iwl9000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, .num_of_queues = 31, .max_tfd_queue_size = 256, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, .smem_offset = IWL9000_SMEM_OFFSET, diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index 582f61661062..2519f577669e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include @@ -15,21 +15,30 @@ /* Lowest firmware API version supported */ #define IWL_AX210_UCODE_API_MIN 89 -/* Memory offsets and lengths */ -#define IWL_AX210_SMEM_OFFSET 0x400000 -#define IWL_AX210_SMEM_LEN 0xD0000 +#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_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0" +#define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0" +#define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0" +#define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0" +#define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0" +#define IWL_SO_A_HR_B_FW_PRE "iwlwifi-so-a0-hr-b0" +#define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0" +#define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0" + +#define IWL_SO_A_HR_B_MODULE_FIRMWARE(api) \ + IWL_SO_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_MA_A_HR_B_MODULE_FIRMWARE(api) \ + IWL_MA_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_MA_B_HR_B_MODULE_FIRMWARE(api) \ + IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode" static const struct iwl_family_base_params iwl_ax210_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, - .smem_offset = IWL_AX210_SMEM_OFFSET, - .smem_len = IWL_AX210_SMEM_LEN, .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .apmg_not_supported = true, .mac_addr_from_csr = 0x380, @@ -121,3 +130,14 @@ const struct iwl_mac_cfg iwl_ma_mac_cfg = { .integrated = true, .umac_prph_offset = 0x300000 }; + +IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_TY_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_SO_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX); +MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_MA_B_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 3653ddbf3ce9..606362463dc7 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include @@ -10,26 +10,23 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_BZ_UCODE_CORE_MAX 102 +#define IWL_BZ_UCODE_CORE_MAX 106 -/* Lowest firmware API version supported */ -#define IWL_BZ_UCODE_API_MIN 100 +/* Lowest firmware core release supported */ +#define IWL_BZ_UCODE_CORE_MIN 102 -/* Memory offsets and lengths */ -#define IWL_BZ_SMEM_OFFSET 0x400000 -#define IWL_BZ_SMEM_LEN 0xD0000 +#define IWL_BZ_A_FM_B_FW_PRE "iwlwifi-bz-a0-fm-b0" +#define IWL_BZ_A_FM_C_FW_PRE "iwlwifi-bz-a0-fm-c0" +#define IWL_BZ_A_FM4_B_FW_PRE "iwlwifi-bz-a0-fm4-b0" +#define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0" +#define IWL_GL_C_FM_C_FW_PRE "iwlwifi-gl-c0-fm-c0" static const struct iwl_family_base_params iwl_bz_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, - .smem_offset = IWL_BZ_SMEM_OFFSET, - .smem_len = IWL_BZ_SMEM_LEN, .apmg_not_supported = true, .mac_addr_from_csr = 0x30, .d3_debug_data_base_addr = 0x401000, @@ -69,7 +66,7 @@ static const struct iwl_family_base_params iwl_bz_base = { }, .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .ucode_api_max = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MAX), - .ucode_api_min = IWL_BZ_UCODE_API_MIN, + .ucode_api_min = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MIN), }; const struct iwl_mac_cfg iwl_bz_mac_cfg = { @@ -94,3 +91,9 @@ const struct iwl_mac_cfg iwl_gl_mac_cfg = { .xtal_latency = 12000, .low_latency_xtal = true, }; + +IWL_CORE_FW(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 83d893b10f8e..946975294b4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include @@ -9,28 +9,19 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_DR_UCODE_CORE_MAX 102 +#define IWL_DR_UCODE_CORE_MAX 106 -/* Lowest firmware API version supported */ -#define IWL_DR_UCODE_API_MIN 100 - -/* Memory offsets and lengths */ -#define IWL_DR_SMEM_OFFSET 0x400000 -#define IWL_DR_SMEM_LEN 0xD0000 +/* Lowest firmware core release supported */ +#define IWL_DR_UCODE_CORE_MIN 102 #define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0" static const struct iwl_family_base_params iwl_dr_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, - .smem_offset = IWL_DR_SMEM_OFFSET, - .smem_len = IWL_DR_SMEM_LEN, .apmg_not_supported = true, .mac_addr_from_csr = 0x30, .d3_debug_data_base_addr = 0x401000, @@ -70,7 +61,7 @@ static const struct iwl_family_base_params iwl_dr_base = { }, .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .ucode_api_max = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MAX), - .ucode_api_min = IWL_DR_UCODE_API_MIN, + .ucode_api_min = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MIN), }; const struct iwl_mac_cfg iwl_dr_mac_cfg = { diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c index ad2536f53084..35b1618f3474 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c @@ -1,19 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-config.h" -#define IWL_BZ_A_FM_B_FW_PRE "iwlwifi-bz-a0-fm-b0" -#define IWL_BZ_A_FM_C_FW_PRE "iwlwifi-bz-a0-fm-c0" -#define IWL_BZ_A_FM4_B_FW_PRE "iwlwifi-bz-a0-fm4-b0" -#define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0" -#define IWL_GL_C_FM_C_FW_PRE "iwlwifi-gl-c0-fm-c0" - -/* NVM versions */ -#define IWL_FM_NVM_VERSION 0x0a1d - #define IWL_DEVICE_FM \ .ht_params = { \ .stbc = true, \ @@ -27,7 +18,6 @@ .uhb_supported = true, \ .eht_supported = true, \ .num_rbds = IWL_NUM_RBDS_EHT, \ - .nvm_ver = IWL_FM_NVM_VERSION, \ .nvm_type = IWL_NVM_EXT const struct iwl_rf_cfg iwl_rf_fm = { @@ -51,14 +41,10 @@ const char iwl_killer_be1790s_name[] = "Killer(R) Wi-Fi 7 BE1790s 320MHz Wireless Network Adapter (BE401D2W)"; const char iwl_killer_be1790i_name[] = "Killer(R) Wi-Fi 7 BE1790i 320MHz Wireless Network Adapter (BE401NGW)"; +const char iwl_killer_be1730x_name[] = + "Killer(TM) Wi-Fi 7 BE1730x 160MHz Wireless Network Adapter (BE202)"; const char iwl_be201_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz"; const char iwl_be200_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz"; const char iwl_be202_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz"; const char iwl_be401_name[] = "Intel(R) Wi-Fi 7 BE401 320MHz"; - -IWL_CORE_FW(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c index c16cda087a7c..8b63cdb3d66c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-config.h" @@ -11,13 +11,6 @@ /* Lowest firmware API version supported */ #define IWL_GF_UCODE_API_MIN 100 -#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_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0" -#define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0" -#define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0" -#define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0" -#define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0" #define IWL_BZ_A_GF_A_FW_PRE "iwlwifi-bz-a0-gf-a0" #define IWL_BZ_A_GF4_A_FW_PRE "iwlwifi-bz-a0-gf4-a0" #define IWL_SC_A_GF_A_FW_PRE "iwlwifi-sc-a0-gf-a0" @@ -35,9 +28,6 @@ #define IWL_SC_A_GF4_A_MODULE_FIRMWARE(api) \ IWL_SC_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode" -/* NVM versions */ -#define IWL_GF_NVM_VERSION 0x0a1d - const struct iwl_rf_cfg iwl_rf_gf = { .uhb_supported = true, .led_mode = IWL_LED_RF_STATE, @@ -49,7 +39,6 @@ const struct iwl_rf_cfg iwl_rf_gf = { .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), }, - .nvm_ver = IWL_GF_NVM_VERSION, .nvm_type = IWL_NVM_EXT, .num_rbds = IWL_NUM_RBDS_HE, .ucode_api_min = IWL_GF_UCODE_API_MIN, @@ -71,14 +60,9 @@ const char iwl_ax411_killer_1690i_name[] = const char iwl_ax210_name[] = "Intel(R) Wi-Fi 6E AX210 160MHz"; const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; +const char iwl_ax231_name[] = "Intel(R) Wi-Fi 6 AX231"; const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz"; -IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX); -IWL_FW_AND_PNVM(IWL_TY_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX); -IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX); -IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX); -IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX); -IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX); MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c index 6cf187d92dbf..16b9075acdd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-config.h" @@ -11,28 +11,10 @@ /* Lowest firmware API version supported */ #define IWL_HR_UCODE_API_MIN 100 -#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0" -#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0" -#define IWL_QUZ_A_HR_B_FW_PRE "iwlwifi-QuZ-a0-hr-b0" -#define IWL_SO_A_HR_B_FW_PRE "iwlwifi-so-a0-hr-b0" -#define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0" -#define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0" #define IWL_BZ_A_HR_B_FW_PRE "iwlwifi-bz-a0-hr-b0" #define IWL_SC_A_HR_A_FW_PRE "iwlwifi-sc-a0-hr-b0" #define IWL_SC_A_HR_B_FW_PRE "iwlwifi-sc-a0-hr-b0" -#define IWL_QU_B_HR_B_MODULE_FIRMWARE(api) \ - IWL_QU_B_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_QUZ_A_HR_B_MODULE_FIRMWARE(api) \ - IWL_QUZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_QU_C_HR_B_MODULE_FIRMWARE(api) \ - IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_SO_A_HR_B_MODULE_FIRMWARE(api) \ - IWL_SO_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(api) \ - IWL_MA_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(api) \ - IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode" #define IWL_BZ_A_HR_B_MODULE_FIRMWARE(api) \ IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" #define IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(api) \ @@ -40,9 +22,6 @@ #define IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(api) \ IWL_SC_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -/* NVM versions */ -#define IWL_HR_NVM_VERSION 0x0a1d - #define IWL_DEVICE_HR \ .led_mode = IWL_LED_RF_STATE, \ .non_shared_ant = ANT_B, \ @@ -54,7 +33,6 @@ BIT(NL80211_BAND_5GHZ), \ }, \ .num_rbds = IWL_NUM_RBDS_HE, \ - .nvm_ver = IWL_HR_NVM_VERSION, \ .nvm_type = IWL_NVM_EXT, \ .ucode_api_min = IWL_HR_UCODE_API_MIN, \ .ucode_api_max = IWL_HR_UCODE_API_MAX @@ -78,12 +56,6 @@ 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"; -MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c index 2c29054ce7b8..7a04cb120b1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c @@ -1,10 +1,28 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2025-2026 Intel Corporation */ #include "iwl-config.h" -/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */ +#define IWL_DEVICE_PE \ + .ht_params = { \ + .stbc = true, \ + .ldpc = true, \ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | \ + BIT(NL80211_BAND_5GHZ), \ + }, \ + .led_mode = IWL_LED_RF_STATE, \ + .non_shared_ant = ANT_B, \ + .vht_mu_mimo_supported = true, \ + .uhb_supported = true, \ + .eht_supported = true, \ + .uhr_supported = true, \ + .num_rbds = IWL_NUM_RBDS_EHT, \ + .nvm_type = IWL_NVM_EXT + +const struct iwl_rf_cfg iwl_rf_pe = { + IWL_DEVICE_PE, +}; const char iwl_killer_bn1850w2_name[] = "Killer(R) Wi-Fi 8 BN1850w2 320MHz Wireless Network Adapter (BN201.D2W)"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c index b5803ea1eb78..c432aa1a0af6 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c @@ -1,12 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2025-2026 Intel Corporation */ #include "iwl-config.h" -/* NVM versions */ -#define IWL_WH_NVM_VERSION 0x0a1d - #define IWL_DEVICE_WH \ .ht_params = { \ .stbc = true, \ @@ -19,7 +16,6 @@ .vht_mu_mimo_supported = true, \ .uhb_supported = true, \ .num_rbds = IWL_NUM_RBDS_EHT, \ - .nvm_ver = IWL_WH_NVM_VERSION, \ .nvm_type = IWL_NVM_EXT /* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */ @@ -33,6 +29,8 @@ const char iwl_killer_be1775s_name[] = "Killer(R) Wi-Fi 7 BE1775s 320MHz Wireless Network Adapter (BE211D2W)"; const char iwl_killer_be1775i_name[] = "Killer(R) Wi-Fi 7 BE1775i 320MHz Wireless Network Adapter (BE211NGW)"; +const char iwl_killer_be1735x_name[] = + "Killer(TM) Wi-Fi 7 BE1735x 160MHz Wireless Network Adapter (BE213)"; const char iwl_be211_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz"; const char iwl_be213_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 749d46dc0236..e8240c1782ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include @@ -10,17 +10,10 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_SC_UCODE_CORE_MAX 102 +#define IWL_SC_UCODE_CORE_MAX 106 -/* Lowest firmware API version supported */ -#define IWL_SC_UCODE_API_MIN 100 - -/* NVM versions */ -#define IWL_SC_NVM_VERSION 0x0a1d - -/* Memory offsets and lengths */ -#define IWL_SC_SMEM_OFFSET 0x400000 -#define IWL_SC_SMEM_LEN 0xD0000 +/* Lowest firmware core release supported */ +#define IWL_SC_UCODE_CORE_MIN 102 #define IWL_SC_A_FM_B_FW_PRE "iwlwifi-sc-a0-fm-b0" #define IWL_SC_A_FM_C_FW_PRE "iwlwifi-sc-a0-fm-c0" @@ -31,14 +24,9 @@ static const struct iwl_family_base_params iwl_sc_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, - .shadow_ram_support = true, - .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, - .smem_offset = IWL_SC_SMEM_OFFSET, - .smem_len = IWL_SC_SMEM_LEN, .apmg_not_supported = true, .mac_addr_from_csr = 0x30, .d3_debug_data_base_addr = 0x401000, @@ -78,7 +66,7 @@ static const struct iwl_family_base_params iwl_sc_base = { }, .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, .ucode_api_max = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MAX), - .ucode_api_min = IWL_SC_UCODE_API_MIN, + .ucode_api_min = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MIN), }; const struct iwl_mac_cfg iwl_sc_mac_cfg = { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 36159a769916..f76f76e54c4e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022, 2024-2025 Intel Corporation + * Copyright (C) 2018-2022, 2024-2026 Intel Corporation */ #ifndef __iwl_fw_api_commands_h__ #define __iwl_fw_api_commands_h__ @@ -22,7 +22,6 @@ * &enum iwl_data_path_subcmd_ids * @SCAN_GROUP: scan group, uses command IDs from * &enum iwl_scan_subcmd_ids - * @NAN_GROUP: NAN group, uses command IDs from &enum iwl_nan_subcmd_ids * @LOCATION_GROUP: location group, uses command IDs from * &enum iwl_location_subcmd_ids * @BT_COEX_GROUP: bt coex group, uses command IDs from @@ -43,7 +42,6 @@ enum iwl_mvm_command_groups { PHY_OPS_GROUP = 0x4, DATA_PATH_GROUP = 0x5, SCAN_GROUP = 0x6, - NAN_GROUP = 0x7, LOCATION_GROUP = 0x8, BT_COEX_GROUP = 0x9, PROT_OFFLOAD_GROUP = 0xb, @@ -59,8 +57,7 @@ enum iwl_legacy_cmds { /** * @UCODE_ALIVE_NTFY: * Alive data from the firmware, as described in - * &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v4 or - * &struct iwl_alive_ntf_v5 or &struct iwl_alive_ntf_v7. + * &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v7. */ UCODE_ALIVE_NTFY = 0x1, @@ -386,7 +383,7 @@ enum iwl_legacy_cmds { * @STATISTICS_NOTIFICATION: * one of &struct iwl_notif_statistics_v10, * &struct iwl_notif_statistics_v11, - * &struct iwl_notif_statistic, + * &struct iwl_notif_statistics, * &struct iwl_statistics_operational_ntfy_ver_14 * &struct iwl_statistics_operational_ntfy */ @@ -469,8 +466,8 @@ enum iwl_legacy_cmds { MCC_CHUB_UPDATE_CMD = 0xc9, /** - * @MARKER_CMD: trace marker command, uses &struct iwl_mvm_marker - * with &struct iwl_mvm_marker_rsp + * @MARKER_CMD: trace marker command, uses &struct iwl_marker + * with &struct iwl_marker_rsp */ MARKER_CMD = 0xcb, @@ -560,7 +557,7 @@ enum iwl_legacy_cmds { WOWLAN_CONFIGURATION = 0xe1, /** - * @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd_v4, + * @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd_ver_2, * &struct iwl_wowlan_rsc_tsc_params_cmd */ WOWLAN_TSC_RSC_PARAM = 0xe2, @@ -653,7 +650,7 @@ enum iwl_system_subcmd_ids { enum iwl_statistics_subcmd_ids { /** * @STATISTICS_OPER_NOTIF: Notification about operational - * statistics &struct iwl_system_statistics_notif_oper + * statistics &struct iwl_system_statistics_notif_oper_v3 */ STATISTICS_OPER_NOTIF = 0x0, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h index 1fc65469990e..84fa5ff7a57f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2012-2014, 2018-2019, 2023-2024 Intel Corporation + * Copyright (C) 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -99,14 +100,4 @@ struct iwl_phy_cfg_cmd_v3 { struct iwl_phy_specific_cfg phy_specific_cfg; } __packed; /* PHY_CONFIGURATION_CMD_API_S_VER_3 */ -/* - * enum iwl_dc2dc_config_id - flag ids - * - * Ids of dc2dc configuration flags - */ -enum iwl_dc2dc_config_id { - DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ - DCDC_FREQ_TUNE_SET = 0x2, -}; /* MARKER_ID_API_E_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 06370c161fe4..c8eb016e1a7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 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 @@ -57,7 +57,7 @@ enum iwl_data_path_subcmd_ids { /** * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 or - * &struct iwl_tlc_config_cmd_v5 or &struct iwl_tlc_config_cmd. + * &struct iwl_tlc_config_cmd. */ TLC_MNG_CONFIG_CMD = 0xF, @@ -91,6 +91,13 @@ enum iwl_data_path_subcmd_ids { */ SEC_KEY_CMD = 0x18, + /** + * @RSC_NOTIF: notification to update each Rx queue with the RSC. This + * notification is sent after resume and uses + * &struct iwl_wowlan_all_rsc_tsc_v5. + */ + RSC_NOTIF = 0xF1, + /** * @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode, * uses &struct iwl_esr_mode_notif or &struct iwl_esr_mode_notif_v1 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 61a850de26fc..382d1c2581ad 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-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -262,20 +262,20 @@ struct iwl_mfu_assert_dump_notif { } __packed; /* MFU_DUMP_ASSERT_API_S_VER_1 */ /** - * enum iwl_mvm_marker_id - marker ids + * enum iwl_marker_id - marker ids * * The ids for different type of markers to insert into the usniffer logs * * @MARKER_ID_TX_FRAME_LATENCY: TX latency marker * @MARKER_ID_SYNC_CLOCK: sync FW time and systime */ -enum iwl_mvm_marker_id { +enum iwl_marker_id { MARKER_ID_TX_FRAME_LATENCY = 1, MARKER_ID_SYNC_CLOCK = 2, }; /* MARKER_ID_API_E_VER_2 */ /** - * struct iwl_mvm_marker - mark info into the usniffer logs + * struct iwl_marker - mark info into the usniffer logs * * (MARKER_CMD = 0xcb) * @@ -284,12 +284,12 @@ enum iwl_mvm_marker_id { * In the command response the ucode will return the GP2 time. * * @dw_len: The amount of dwords following this byte including this byte. - * @marker_id: A unique marker id (iwl_mvm_marker_id). + * @marker_id: A unique marker id (iwl_marker_id). * @reserved: reserved. * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC * @metadata: additional meta data that will be written to the unsiffer log */ -struct iwl_mvm_marker { +struct iwl_marker { u8 dw_len; u8 marker_id; __le16 reserved; @@ -298,11 +298,11 @@ struct iwl_mvm_marker { } __packed; /* MARKER_API_S_VER_1 */ /** - * struct iwl_mvm_marker_rsp - Response to marker cmd + * struct iwl_marker_rsp - Response to marker cmd * * @gp2: The gp2 clock value in the FW */ -struct iwl_mvm_marker_rsp { +struct iwl_marker_rsp { __le32 gp2; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h index 2ee3a48aa5df..421ea94ace01 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2015-2017 Intel Deutschland GmbH * Copyright (C) 2018-2022 Intel Corporation - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_fw_api_location_h__ #define __iwl_fw_api_location_h__ @@ -81,12 +81,53 @@ enum iwl_location_subcmd_ids { * @TOF_RANGE_RESPONSE_NOTIF: ranging response, using one of * &struct iwl_tof_range_rsp_ntfy_v5, * &struct iwl_tof_range_rsp_ntfy_v6, - * &struct iwl_tof_range_rsp_ntfy_v7 or - * &struct iwl_tof_range_rsp_ntfy_v8 + * &struct iwl_tof_range_rsp_ntfy_v7, + * &struct iwl_tof_range_rsp_ntfy_v9 or + * &struct iwl_tof_range_rsp_ntfy */ TOF_RANGE_RESPONSE_NOTIF = 0xFF, }; +/** + * enum iwl_location_frame_format - location frame formats + * @IWL_LOCATION_FRAME_FORMAT_LEGACY: legacy + * @IWL_LOCATION_FRAME_FORMAT_HT: HT + * @IWL_LOCATION_FRAME_FORMAT_VHT: VHT + * @IWL_LOCATION_FRAME_FORMAT_HE: HE + */ +enum iwl_location_frame_format { + IWL_LOCATION_FRAME_FORMAT_LEGACY, + IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FRAME_FORMAT_VHT, + IWL_LOCATION_FRAME_FORMAT_HE, +}; + +/** + * enum iwl_location_bw - location bandwidth selection + * @IWL_LOCATION_BW_20MHZ: 20 MHz + * @IWL_LOCATION_BW_40MHZ: 40 MHz + * @IWL_LOCATION_BW_80MHZ: 80 MHz + * @IWL_LOCATION_BW_160MHZ: 160 MHz + * @IWL_LOCATION_BW_320MHZ: 320 MHz + */ +enum iwl_location_bw { + IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_BW_40MHZ, + IWL_LOCATION_BW_80MHZ, + IWL_LOCATION_BW_160MHZ, + IWL_LOCATION_BW_320MHZ, +}; + +/** + * enum iwl_location_format_bw - format/BW encoding + * @IWL_LOCATION_FMT_BW_FORMAT: &enum iwl_location_frame_format + * @IWL_LOCATION_FMT_BW_BANDWIDTH: &enum iwl_location_bw + */ +enum iwl_location_format_bw { + IWL_LOCATION_FMT_BW_FORMAT = 0x0f, + IWL_LOCATION_FMT_BW_BANDWIDTH = 0xf0, +}; + /** * struct iwl_tof_config_cmd - ToF configuration * @tof_disabled: indicates if ToF is disabled (or not) @@ -263,8 +304,7 @@ struct iwl_tof_responder_config_cmd_v6 { * struct iwl_tof_responder_config_cmd_v7 - ToF AP mode (for debug) * @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field * @responder_cfg_flags: &iwl_tof_responder_cfg_flags - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @rate: current AP rate * @channel_num: current AP Channel * @ctrl_ch_position: coding of the control channel position relative to @@ -302,8 +342,7 @@ struct iwl_tof_responder_config_cmd_v7 { * struct iwl_tof_responder_config_cmd_v8 - ToF AP mode (for debug) * @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field * @responder_cfg_flags: &iwl_tof_responder_cfg_flags - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @rate: current AP rate * @channel_num: current AP Channel * @ctrl_ch_position: coding of the control channel position relative to @@ -348,8 +387,7 @@ struct iwl_tof_responder_config_cmd_v8 { * struct iwl_tof_responder_config_cmd_v9 - ToF AP mode (for debug) * @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field * @responder_cfg_flags: &iwl_tof_responder_cfg_flags - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @bss_color: current AP bss_color * @channel_num: current AP Channel * @ctrl_ch_position: coding of the control channel position relative to @@ -400,8 +438,7 @@ struct iwl_tof_responder_config_cmd_v9 { * struct iwl_tof_responder_config_cmd - ToF AP mode * @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field * @responder_cfg_flags: &iwl_tof_responder_cfg_flags - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @bss_color: current AP bss_color * @channel_num: current AP Channel * @ctrl_ch_position: coding of the control channel position relative to @@ -676,44 +713,13 @@ struct iwl_tof_range_req_ap_entry_v3 { __le32 tsf_delta; } __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_3 */ -/** - * enum iwl_location_frame_format - location frame formats - * @IWL_LOCATION_FRAME_FORMAT_LEGACY: legacy - * @IWL_LOCATION_FRAME_FORMAT_HT: HT - * @IWL_LOCATION_FRAME_FORMAT_VHT: VHT - * @IWL_LOCATION_FRAME_FORMAT_HE: HE - */ -enum iwl_location_frame_format { - IWL_LOCATION_FRAME_FORMAT_LEGACY, - IWL_LOCATION_FRAME_FORMAT_HT, - IWL_LOCATION_FRAME_FORMAT_VHT, - IWL_LOCATION_FRAME_FORMAT_HE, -}; - -/** - * enum iwl_location_bw - location bandwidth selection - * @IWL_LOCATION_BW_20MHZ: 20MHz - * @IWL_LOCATION_BW_40MHZ: 40MHz - * @IWL_LOCATION_BW_80MHZ: 80MHz - * @IWL_LOCATION_BW_160MHZ: 160MHz - */ -enum iwl_location_bw { - IWL_LOCATION_BW_20MHZ, - IWL_LOCATION_BW_40MHZ, - IWL_LOCATION_BW_80MHZ, - IWL_LOCATION_BW_160MHZ, -}; - #define TK_11AZ_LEN 32 -#define LOCATION_BW_POS 4 - /** * struct iwl_tof_range_req_ap_entry_v4 - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @channel_num: AP Channel number - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @ctrl_ch_position: Coding of the control channel position relative to the * center frequency, see iwl_mvm_get_ctrl_pos(). * @ftmr_max_retries: Max number of retries to send the FTMR in case of no @@ -763,8 +769,7 @@ enum iwl_location_cipher { * struct iwl_tof_range_req_ap_entry_v6 - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @channel_num: AP Channel number - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @ctrl_ch_position: Coding of the control channel position relative to the * center frequency, see iwl_mvm_get_ctrl_pos(). * @ftmr_max_retries: Max number of retries to send the FTMR in case of no @@ -810,8 +815,7 @@ struct iwl_tof_range_req_ap_entry_v6 { * struct iwl_tof_range_req_ap_entry_v7 - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @channel_num: AP Channel number - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @ctrl_ch_position: Coding of the control channel position relative to the * center frequency, see iwl_mvm_get_ctrl_pos(). * @ftmr_max_retries: Max number of retries to send the FTMR in case of no @@ -868,8 +872,7 @@ struct iwl_tof_range_req_ap_entry_v7 { * struct iwl_tof_range_req_ap_entry_v8 - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @channel_num: AP Channel number - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @ctrl_ch_position: Coding of the control channel position relative to the * center frequency, see iwl_mvm_get_ctrl_pos(). * @ftmr_max_retries: Max number of retries to send the FTMR in case of no @@ -939,8 +942,7 @@ struct iwl_tof_range_req_ap_entry_v8 { * struct iwl_tof_range_req_ap_entry_v9 - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @channel_num: AP Channel number - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @ctrl_ch_position: Coding of the control channel position relative to the * center frequency, see iwl_mvm_get_ctrl_pos(). * @ftmr_max_retries: Max number of retries to send the FTMR in case of no @@ -1024,8 +1026,7 @@ struct iwl_tof_range_req_ap_entry_v9 { * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @band: 0 for 5.2 GHz, 1 for 2.4 GHz, 2 for 6GHz * @channel_num: AP Channel number - * @format_bw: bits 0 - 3: &enum iwl_location_frame_format. - * bits 4 - 7: &enum iwl_location_bw. + * @format_bw: &enum iwl_location_format_bw * @ctrl_ch_position: Coding of the control channel position relative to the * center frequency, see iwl_mvm_get_ctrl_pos(). * @bssid: AP's BSSID 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 b398c582b867..09b2cddc4ad2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -8,6 +8,7 @@ #define __iwl_fw_api_mac_cfg_h__ #include "mac.h" +#include "phy-ctxt.h" /** * enum iwl_mac_conf_subcmd_ids - mac configuration command IDs @@ -71,6 +72,23 @@ enum iwl_mac_conf_subcmd_ids { * @NAN_CFG_CMD: &struct iwl_nan_config_cmd */ NAN_CFG_CMD = 0x12, + /** + * @NAN_SCHEDULE_CMD: &struct iwl_nan_schedule_cmd + */ + NAN_SCHEDULE_CMD = 0x13, + /** + * @NAN_PEER_CMD: &struct iwl_nan_peer_cmd + */ + NAN_PEER_CMD = 0x14, + /** + * @NAN_ULW_ATTR_NOTIF: &struct iwl_nan_ulw_attr_notif + */ + NAN_ULW_ATTR_NOTIF = 0xf2, + /** + * @NAN_SCHED_UPDATE_COMPLETED_NOTIF: + * &struct iwl_nan_sched_update_completed_notif + */ + NAN_SCHED_UPDATE_COMPLETED_NOTIF = 0xf3, /** * @NAN_DW_END_NOTIF: &struct iwl_nan_dw_end_notif */ @@ -536,12 +554,20 @@ enum iwl_link_ctx_protection_flags { * radar pulses). * @LINK_FLG_NDP_FEEDBACK_ENABLED: mark support for NDP feedback and change * of threshold + * @LINK_FLG_NPCA: NPCA enabled + * @LINK_FLG_DPS: AP is a DPS assisting AP + * @LINK_FLG_MLPM: AP supports UHR multi-link PM + * @LINK_FLG_DUO: AP supports UHR DUO */ enum iwl_link_ctx_flags { LINK_FLG_BSS_COLOR_DIS = BIT(0), LINK_FLG_MU_EDCA_CW = BIT(1), LINK_FLG_RU_2MHZ_BLOCK = BIT(2), LINK_FLG_NDP_FEEDBACK_ENABLED = BIT(3), + LINK_FLG_NPCA = BIT(4), + LINK_FLG_DPS = BIT(6), + LINK_FLG_MLPM = BIT(7), + LINK_FLG_DUO = BIT(8), }; /* LINK_CONTEXT_FLAG_E_VER_1 */ /** @@ -578,7 +604,7 @@ enum iwl_npca_flags { * @initial_qsrc: Indicates the value that is used to initialize the * EDCAF QSRC[AC] variables * @min_dur_threshold: minimum PPDU time to switch to the non-primary - * NPCA channel (usec) + * NPCA channel (spec representation) * @flags: NPCA flags, see &enum iwl_npca_flags * @reserved: reserved for alignment purposes */ @@ -726,6 +752,12 @@ struct iwl_link_config_cmd { * @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station * of this type can have any number of links (even none) set in the * link_mask. (Supported since version 3.) + * @STATION_TYPE_NAN_BCAST: NAN station used for synchronization and + * discovery. No queue is associated with this station. + * @STATION_TYPE_NAN_MGMT: NAN station used for NAN management frames, e.g., + * SDFs and NAFs. + * @STATION_TYPE_NAN_MCAST_DATA: NAN station used for multicast NAN data + * frames. * @STATION_TYPE_MAX: maximum number of FW station types * @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type * for the aux sta, so this type is only for driver - internal use. @@ -736,6 +768,9 @@ enum iwl_fw_sta_type { STATION_TYPE_MCAST, STATION_TYPE_NAN_PEER_NMI, STATION_TYPE_NAN_PEER_NDI, + STATION_TYPE_NAN_BCAST, + STATION_TYPE_NAN_MGMT, + STATION_TYPE_NAN_MCAST_DATA, STATION_TYPE_MAX, STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */ }; /* STATION_TYPE_E_VER_1, _VER_2 */ @@ -873,7 +908,9 @@ struct iwl_sta_cfg_cmd_v2 { * ( STA_CONFIG_CMD = 0xA ) * * @sta_id: index of station in uCode's station table - * @link_mask: bitmap of link FW IDs used with this STA + * @link_mask: bitmap of link FW IDs used with this STA. Should be set to 0 + * for STATION_TYPE_NAN_BCAST and STATION_TYPE_NAN_MGMT as they are not + * associated with any link added by the driver. * @peer_mld_address: the peers mld address * @reserved_for_peer_mld_address: reserved * @peer_link_address: the address of the link that is used to communicate @@ -908,7 +945,8 @@ struct iwl_sta_cfg_cmd_v2 { * @mic_prep_pad_delay: MIC prep time padding * @mic_compute_pad_delay: MIC compute time padding * @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to - * @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address + * @ndi_local_addr: for an NDI peer STA or NAN multicast data station, + * the local NDI interface MAC address * @reserved: Reserved for alignment */ struct iwl_sta_cfg_cmd { @@ -1204,7 +1242,8 @@ enum iwl_nan_flags { * @discovery_beacon_interval: discovery beacon interval in TUs * @cluster_id: lower last two bytes of the cluster ID, in case the local * device starts a cluster - * @sta_id: station ID of the NAN station + * @sta_id: station ID of the NAN station. Used only in version 1, in version 2 + * it is reserved. * @hb_channel: channel for 5 GHz if the device supports operation on 5 GHz. * Valid values are 44 and 149, which correspond to the 5 GHz channel, and * 0 which means that NAN operation on the 5 GHz band is disabled. @@ -1242,7 +1281,102 @@ struct iwl_nan_config_cmd { __le32 nan_attr_len; __le32 nan_vendor_elems_len; u8 beacon_data[]; -} __packed; /* NAN_CONFIG_CMD_API_S_VER_1 */ +} __packed; /* NAN_CONFIG_CMD_API_S_VER_1, NAN_CONFIG_CMD_API_S_VER_2 */ + +/** + * struct iwl_nan_schedule_cmd_v1 - NAN schedule command + * @channels: per channel information + * @channels.availability_map: bitmap of slots this channel is advertising + * availability on, will be ULW'ed out if no link/inactive link is + * referenced by the link ID below + * @channels.channel_entry: NAN channel entry descriptor + * @channels.link_id: FW link ID, or %0xFF for unset + * @channels.reserved: (reserved) + */ +struct iwl_nan_schedule_cmd_v1 { + struct { + __le32 availability_map; + u8 channel_entry[6]; + u8 link_id; + u8 reserved; + } __packed channels[NUM_PHY_CTX]; +} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_1 */ + +#define IWL_MAX_AVAILABILITY_ATTR_LEN 54 + +/** + * struct iwl_nan_schedule_cmd - NAN schedule command + * @channels: per channel information + * @channels.availability_map: bitmap of slots this channel is advertising + * availability on, will be ULW'ed out if no link/inactive link is + * referenced by the link ID below + * @channels.channel_entry: NAN channel entry descriptor + * @channels.link_id: FW link ID, or %0xFF for unset + * @channels.reserved: (reserved) + * @avail_attr: NAN availability attribute information + * @avail_attr.attr_len: length of the availability attribute + * @avail_attr.reserved: reserved + * @avail_attr.attr: the availability attribute including the attribute header + * @deferred: true if the firmware should defer applying the schedule until + * notifying all peers. For a deferred schedule update, the firmware should + * send a notification to the driver after the new schedule is applied. + * @reserved: reserved + */ +struct iwl_nan_schedule_cmd { + struct { + __le32 availability_map; + u8 channel_entry[6]; + u8 link_id; + u8 reserved; + } __packed channels[NUM_PHY_CTX]; + + struct { + u8 attr_len; + u8 reserved; + u8 attr[IWL_MAX_AVAILABILITY_ATTR_LEN]; + } __packed avail_attr; + + u8 deferred; + u8 reserved[3]; +} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_2 */ + +/** + * struct iwl_nan_peer_cmd - NAN peer command + * @nmi_sta_id: NAN management station ID + * @sequence_id: NAN Availability attribute sequence ID + * @committed_dw_info: committed DW info from the NAN Device + * Capability attribute + * @max_channel_switch_time: maximum channel switch time + * (in microseconds); 0 means unavailable + * @reserved: (reserved) + * @per_phy: per-PHY information for this peer, indexed by PHY ID + * @per_phy.availability_map: bitmap of which slots this peer + * is available in on this PHY. 0 indicates the this per-PHY entry + * is unused. + * @per_phy.channel_entry: the channel description the peer is using, + * used for comparisons in ULW management + * @per_phy.link_id: FW link ID, should be a valid id. + * @per_phy.map_id: map ID from peer's NAN Availability attributec + * @initial_ulw_size: size of the initial ULW blob + * @initial_ulw: initial ULW data from the peer + */ +struct iwl_nan_peer_cmd { + u8 nmi_sta_id; + u8 sequence_id; + __le16 committed_dw_info; + __le16 max_channel_switch_time; + __le16 reserved; + + struct { + __le32 availability_map; + u8 channel_entry[6]; + u8 link_id; + u8 map_id; + } __packed per_phy[NUM_PHY_CTX]; + + __le32 initial_ulw_size; + u8 initial_ulw[]; +} __packed; /* NAN_PEER_SCHEDULE_CMD_API_S_VER_1 */ /** * enum iwl_nan_cluster_notif_flags - flags for the cluster notification @@ -1280,4 +1414,44 @@ struct iwl_nan_dw_end_notif { u8 reserved[3]; } __packed; /* NAN_DW_END_NTF_API_S_VER_1 */ +#define IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN 48 + +/** + * struct iwl_nan_ulw_attr_notif - sent to notify the host of a change in the + * ULW attribute + * + * @attr_len: length of the ULW attribute in bytes + * @reserved: reserved + * @attr: the ULW attribute including the attribute header + */ +struct iwl_nan_ulw_attr_notif { + u8 attr_len; + u8 reserved[3]; + u8 attr[IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN]; +} __packed; /* NAN_ULW_ATTR_NOTIF_API_S_VER_1 */ + +/** + * enum iwl_nan_sched_update_status - NAN schedule update status + * + * @IWL_NAN_SCHED_UPDATE_SUCCESS: schedule update completed successfully + * @IWL_NAN_SCHED_UPDATE_FAILURE: schedule update failed. Currently not expected + * to happen, but reserved for future use. + */ +enum iwl_nan_sched_update_status { + IWL_NAN_SCHED_UPDATE_SUCCESS = 0, + IWL_NAN_SCHED_UPDATE_FAILURE = 1, +}; + +/** + * struct iwl_nan_sched_update_completed_notif - NAN schedule update completed + * + * @status: status of the schedule update operation. See + * &enum iwl_nan_sched_update_status + * @reserved: reserved + */ +struct iwl_nan_sched_update_completed_notif { + u8 status; + u8 reserved[3]; +} __packed; /* NAN_SCHED_UPDATE_COMPLETED_NTF_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index a3f916630df2..115e65ba19f8 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-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -85,12 +85,13 @@ struct iwl_ltr_config_cmd { * '1' PM could sleep over DTIM till listen Interval. * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all * access categories are both delivery and trigger enabled. + * (Not supported since version 3) * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and * PBW Snoozing enabled * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. * @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving - * detection enablement + * detection enablement (Not supported since version 3) * @POWER_FLAGS_ENABLE_SMPS_MSK: SMPS is allowed for this vif */ enum iwl_power_flags { @@ -175,9 +176,9 @@ struct iwl_device_power_cmd { } __packed; /** - * struct iwl_mac_power_cmd - New power command containing uAPSD support + * struct iwl_mac_power_cmd_v2 - power command V2 containing uAPSD support * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) - * @id_and_color: MAC contex identifier, &enum iwl_ctxt_id_and_color + * @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color * @flags: Power table command flags from POWER_FLAGS_* * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. * Minimum allowed:- 3 * DTIM. Keep alive period must be @@ -216,7 +217,7 @@ struct iwl_device_power_cmd { * @limited_ps_threshold: (unused) * @reserved: reserved (padding) */ -struct iwl_mac_power_cmd { +struct iwl_mac_power_cmd_v2 { /* CONTEXT_DESC_API_T_VER_1 */ __le32 id_and_color; @@ -242,6 +243,43 @@ struct iwl_mac_power_cmd { u8 reserved; } __packed; /* CLIENT_PM_POWER_TABLE_S_VER_1, VER_2 */ +/** + * struct iwl_mac_power_cmd - power command + * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) + * @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color + * @flags: Power table command flags from POWER_FLAGS_* + * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. + * Minimum allowed:- 3 * DTIM. Keep alive period must be + * set regardless of power scheme or current power state. + * FW use this value also when PM is disabled. + * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to + * PSM transition - legacy PM + * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to + * PSM transition - legacy PM + * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. + * Default: 80dbm + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). + * @qndp_tid: TID client shall use for uAPSD QNDP triggers + * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for + * each corresponding AC. + * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. + */ +struct iwl_mac_power_cmd { + /* CONTEXT_DESC_API_T_VER_1 */ + __le32 id_and_color; + + __le16 flags; + __le16 keep_alive_seconds; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + u8 lprx_rssi_threshold; + u8 skip_dtim_periods; + u8 qndp_tid; + u8 uapsd_ac_flags; +} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_3 */ + /* * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when * associated AP is identified as improperly implementing uAPSD protocol. diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index ae6be3ed32f8..f1254546cc9d 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-2022, 2024-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022, 2024-2026 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_rs_h__ @@ -206,37 +206,6 @@ struct iwl_tlc_config_cmd_v4 { __le16 max_tx_op; } __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_4 */ -/** - * struct iwl_tlc_config_cmd_v5 - TLC configuration - * @sta_id: station id - * @reserved1: reserved - * @max_ch_width: max supported channel width from &enum iwl_tlc_mng_cfg_cw - * @mode: &enum iwl_tlc_mng_cfg_mode - * @chains: bitmask of &enum iwl_tlc_mng_cfg_chains - * @sgi_ch_width_supp: bitmap of SGI support per channel width - * use BIT(&enum iwl_tlc_mng_cfg_cw) - * @flags: bitmask of &enum iwl_tlc_mng_cfg_flags - * @non_ht_rates: bitmap of supported legacy rates - * @ht_rates: bitmap of &enum iwl_tlc_mng_ht_rates, per - * pair (0 - 80mhz width and below, 1 - 160mhz, 2 - 320mhz). - * @max_mpdu_len: max MPDU length, in bytes - * @max_tx_op: max TXOP in uSecs for all AC (BK, BE, VO, VI), - * set zero for no limit. - */ -struct iwl_tlc_config_cmd_v5 { - u8 sta_id; - u8 reserved1[3]; - u8 max_ch_width; - u8 mode; - u8 chains; - u8 sgi_ch_width_supp; - __le16 flags; - __le16 non_ht_rates; - __le32 ht_rates[IWL_TLC_NSS_MAX][IWL_TLC_MCS_PER_BW_NUM_V4]; - __le16 max_mpdu_len; - __le16 max_tx_op; -} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_5 */ - /** * struct iwl_tlc_config_cmd - TLC configuration * @sta_mask: station mask (in NAN we can have multiple logical stations of @@ -889,7 +858,6 @@ struct iwl_lq_cmd { __le32 ss_params; }; /* LINK_QUALITY_CMD_API_S_VER_1 */ -u8 iwl_fw_rate_idx_to_plcp(int idx); const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx); const char *iwl_rs_pretty_ant(u8 ant); const char *iwl_rs_pretty_bw(int bw); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index ac6c1ef2cbcd..699343cf0279 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -263,6 +263,9 @@ enum iwl_rx_mpdu_reorder_data { enum iwl_rx_mpdu_phy_info { IWL_RX_MPDU_PHY_EOF_INDICATION = BIT(0), + IWL_RX_MPDU_PHY_UEQM = BIT(1) | BIT(2), + IWL_RX_MPDU_PHY_2X_LDPC = BIT(3), + IWL_RX_MPDU_PHY_NPCA = BIT(4), IWL_RX_MPDU_PHY_AMPDU = BIT(5), IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6), IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7), @@ -732,6 +735,8 @@ struct iwl_rx_mpdu_desc { * RX_MPDU_RES_START_API_S_VER_4, * RX_MPDU_RES_START_API_S_VER_5, * RX_MPDU_RES_START_API_S_VER_6 + * RX_MPDU_RES_START_API_S_VER_7 + * RX_MPDU_RES_START_API_S_VER_8 */ #define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1) @@ -1169,8 +1174,14 @@ struct iwl_he_tb_sigs { #define OFDM_UCODE_TRIG_BASE_RX_BANDWIDTH 0x00007000 #define OFDM_UCODE_TRIG_BASE_PS160 0x00008000 #define OFDM_UCODE_EHT_TRIG_CONTROL_CHANNEL 0x000f0000 +/* for UHR, since PE RF: */ +#define OFDM_UCODE_TRIG_BASE_RX_MCS_UHR_AND_LATER 0x01f00000 +#define OFDM_UCODE_RX_IS_DRU 0x02000000 +#define OFDM_UCODE_RX_IS_2XLDPC_ENABLED 0x04000000 +#define OFDM_UCODE_RX_DRU_DBW 0x60000000 __le32 tb_rx0; -#define OFDM_UCODE_TRIG_BASE_RX_MCS 0x0000000f +/* prior to PE RF, cannot have 5 bits needed for UHR: */ +#define OFDM_UCODE_TRIG_BASE_RX_MCS_PRE_UHR 0x0000000f #define OFDM_UCODE_TRIG_BASE_RX_DCM 0x00000010 #define OFDM_UCODE_TRIG_BASE_RX_GI_LTF_TYPE 0x00000060 #define OFDM_UCODE_TRIG_BASE_RX_NSTS 0x00000380 @@ -1275,15 +1286,16 @@ struct iwl_uhr_sigs { __le32 usig_a1; #define OFDM_RX_FRAME_UHR_BSS_COLOR2 0x0000003f __le32 usig_a1_uhr; -#define OFDM_RX_FRAME_UHR_PPDU_TYPE 0x00000003 -#define OFDM_RX_FRAME_UHR_COBF_CSR_DISABLE 0x00000004 -#define OFDM_RX_FRAME_UHR_PUNC_CHANNEL 0x000000f8 -#define OFDM_RX_FRAME_UHR_USIG2_VALIDATE_B8 0x00000100 -#define OFDM_RX_FRAME_UHR_SIG_MCS 0x00000600 -#define OFDM_RX_FRAME_UHR_SIG_SYM_NUM 0x0000f800 -#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_1 0x000f0000 -#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_2 0x00f00000 -#define OFDM_RX_FRAME_UHR_TRIG_USIG2_DISREGARD 0x1f000000 +#define OFDM_RX_FRAME_UHR_PPDU_TYPE 0x00000003 +#define OFDM_RX_FRAME_UHR_COBF_CSR_DISABLE 0x00000004 +#define OFDM_RX_FRAME_UHR_PUNC_CHANNEL 0x000000f8 +#define OFDM_RX_FRAME_UHR_USIG2_VALIDATE_B8 0x00000100 +#define OFDM_RX_FRAME_UHR_SIG_MCS 0x00000600 +#define OFDM_RX_FRAME_UHR_SIG_SYM_NUM 0x0000f800 +#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_1 0x000f0000 +#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_2 0x00f00000 +#define OFDM_RX_FRAME_UHR_TRIG_USIG2_DISREGARD 0x1f000000 +#define OFDM_RX_FRAME_UHR_USIG_CRC_OK 0x40000000 __le32 usig_a2_uhr; #define OFDM_RX_FRAME_UHR_SPATIAL_REUSE 0x0000000f #define OFDM_RX_FRAME_UHR_GI_LTF_TYPE 0x00000030 @@ -1305,6 +1317,7 @@ struct iwl_uhr_sigs { #define OFDM_RX_FRAME_UHR_CODING 0x00000200 #define OFDM_RX_FRAME_UHR_SPATIAL_CONFIG 0x00003c00 #define OFDM_RX_FRAME_UHR_STA_RU 0x003fc000 +#define OFDM_RX_FRAME_UHR_STA_RU_P80 0x00004000 #define OFDM_RX_FRAME_UHR_STA_RU_PS160 0x00400000 #define OFDM_RX_FRAME_UHR_UEQM 0x00800000 #define OFDM_RX_FRAME_UHR_2XLDPC 0x01000000 @@ -1330,7 +1343,12 @@ struct iwl_uhr_tb_sigs { struct iwl_uhr_elr_sigs { /* same as UHR above */ - __le32 usig_a1, usig_a2_uhr; + __le32 usig_a1; +#define OFDM_RX_VECTOR_UHR_ELR_PPDU_TYPE 0x00000003 +#define OFDM_RX_VECTOR_UHR_ELR_USIG_STA_ID 0x00001ffc +#define OFDM_RX_VECTOR_UHR_ELR_VALIDATE 0x0000e000 +#define OFDM_RX_VECTOR_UHR_ELR_CRC_OK 0x00010000 + __le32 usig_a2_uhr_elr; #define OFDM_RX_VECTOR_UHR_ELR_VER_ID 0x00000007 #define OFDM_RX_VECTOR_UHR_ELR_UPLINK_FLAG 0x00000008 #define OFDM_RX_VECTOR_UHR_ELR_MCS 0x00000010 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index e6f9abdfa546..3e62a458b131 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021, 2023, 2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021, 2023, 2025-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -191,7 +191,6 @@ enum iwl_sta_sleep_flag { #define STA_KEY_IDX_INVALID (0xff) #define STA_KEY_MAX_DATA_KEY_NUM (4) #define IWL_MAX_GLOBAL_KEYS (4) -#define IWL_MAX_NUM_IGTKS 2 #define STA_KEY_LEN_WEP40 (5) #define STA_KEY_LEN_WEP104 (13) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h index 68983f6a0026..8d16782a129c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -557,7 +557,7 @@ struct iwl_stats_ntfy_per_mac { #define IWL_STATS_MAX_BW_INDEX 5 /** - * struct iwl_stats_ntfy_per_phy - per PHY statistics + * struct iwl_stats_ntfy_per_phy_v1 - per PHY statistics * @channel_load: channel load * @channel_load_by_us: device contribution to MCLM * @channel_load_not_by_us: other devices' contribution to MCLM @@ -572,7 +572,7 @@ struct iwl_stats_ntfy_per_mac { * per channel BW. note BACK counted as 1 * @last_tx_ch_width_indx: last txed frame channel width index */ -struct iwl_stats_ntfy_per_phy { +struct iwl_stats_ntfy_per_phy_v1 { __le32 channel_load; __le32 channel_load_by_us; __le32 channel_load_not_by_us; @@ -585,6 +585,67 @@ struct iwl_stats_ntfy_per_phy { __le32 last_tx_ch_width_indx; } __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */ +/** + * struct iwl_stats_ntfy_coex - coex statistics + * + * @wifi_kill_cnt: count of wifi frames killed by BT + * @wifi_tx_cts_kill_cnt: count of wifi Tx CTS frames killed by BT + * @ttc2_ppdu_error_count: Count PPDU errors on TTC2 - BT Tx indication rises + * within wifi Tx packet on non-shared antenna and wifi is NOT killed by + * PTA/TCL. + * @trc2_ppdu_error_count: count PPDU errors on TRC2 - BT Rx indication rises + * within wifi Tx packet on non-shared antenna and wifi is NOT killed by + * PTA/TCL + * @rrc1_collision_count: count RRC1 - BT Rx indication rises within wifi Rx + * packet on shared antenna + * @rrc2_collision_count: count RRC2 - BT Rx indication rises within wifi Rx + * packet on non-shared antenna + * @rtc2_collision_count: count RTC2 - BT Tx indication rises within wifi Rx + * packet on non-shared antenna + * @reserved: reserved + */ +struct iwl_stats_ntfy_coex { + __le16 wifi_kill_cnt; + __le16 wifi_tx_cts_kill_cnt; + __le16 ttc2_ppdu_error_count; + __le16 trc2_ppdu_error_count; + __le16 rrc1_collision_count; + __le16 rrc2_collision_count; + __le16 rtc2_collision_count; + __le16 reserved; +} __packed; /* STATISTICS_FW_NTFY_COEX_TELEMETRY_API_S_VER_1 */ + +/** + * struct iwl_stats_ntfy_per_phy - per PHY statistics + * @channel_load: channel load + * @channel_load_by_us: device contribution to MCLM + * @channel_load_not_by_us: other devices' contribution to MCLM + * @clt: CLT HW timer (TIM_CH_LOAD2) + * @act: active accumulator SW + * @elp: elapsed time accumulator SW + * @rx_detected_per_ch_width: number of deferred TX per channel width, + * 0 - 20, 1/2/3 - 40/80/160 + * @success_per_ch_width: number of frames that got ACK/BACK/CTS + * per channel BW. note, BACK counted as 1 + * @fail_per_ch_width: number of frames that didn't get ACK/BACK/CTS + * per channel BW. note BACK counted as 1 + * @last_tx_ch_width_indx: last txed frame channel width index + * @coex: coex related data + */ +struct iwl_stats_ntfy_per_phy { + __le32 channel_load; + __le32 channel_load_by_us; + __le32 channel_load_not_by_us; + __le32 clt; + __le32 act; + __le32 elp; + __le32 rx_detected_per_ch_width[IWL_STATS_MAX_BW_INDEX]; + __le32 success_per_ch_width[IWL_STATS_MAX_BW_INDEX]; + __le32 fail_per_ch_width[IWL_STATS_MAX_BW_INDEX]; + __le32 last_tx_ch_width_indx; + struct iwl_stats_ntfy_coex coex; +} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_2 */ + /* unknown channel load (due to not being active on channel) */ #define IWL_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff @@ -599,12 +660,27 @@ struct iwl_stats_ntfy_per_sta { #define IWL_STATS_MAX_PHY_OPERATIONAL 3 +/** + * struct iwl_system_statistics_notif_oper_v3 - statistics notification + * + * @time_stamp: time when the notification is sent from firmware + * @per_link: per link statistics, &struct iwl_stats_ntfy_per_link + * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1 + * @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta + */ +struct iwl_system_statistics_notif_oper_v3 { + __le32 time_stamp; + struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS]; + struct iwl_stats_ntfy_per_phy_v1 per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; + struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX]; +} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */ + /** * struct iwl_system_statistics_notif_oper - statistics notification * * @time_stamp: time when the notification is sent from firmware * @per_link: per link statistics, &struct iwl_stats_ntfy_per_link - * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy + * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1 * @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta */ struct iwl_system_statistics_notif_oper { @@ -612,7 +688,7 @@ struct iwl_system_statistics_notif_oper { struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS]; struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX]; -} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */ +} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_4 */ /** * struct iwl_system_statistics_part1_notif_oper - part1 stats notification @@ -642,7 +718,7 @@ struct iwl_system_statistics_end_notif { * @hdr: general statistics header * @flags: bitmap of possible notification structures * @per_mac: per mac statistics, &struct iwl_stats_ntfy_per_mac - * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy + * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1 * @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta * @rx_time: rx time * @tx_time: usec the radio is transmitting. @@ -653,7 +729,7 @@ struct iwl_statistics_operational_ntfy { struct iwl_statistics_ntfy_hdr hdr; __le32 flags; struct iwl_stats_ntfy_per_mac per_mac[MAC_INDEX_AUX]; - struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; + struct iwl_stats_ntfy_per_phy_v1 per_phy[IWL_STATS_MAX_PHY_OPERATIONAL]; struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX]; __le64 rx_time; __le64 tx_time; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c new file mode 100644 index 000000000000..19b1bfd0abee --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c @@ -0,0 +1,1022 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation + * Copyright (C) 2013-2015 Intel Mobile Communications GmbH + * Copyright (C) 2015-2017 Intel Deutschland GmbH + */ +#include +#include "iwl-drv.h" +#include "runtime.h" +#include "dbg.h" +#include "debugfs.h" +#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 + * + * @fwrt_ptr: pointer to the buffer coming from fwrt + * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the + * transport's data. + * @fwrt_len: length of the valid data in fwrt_ptr + */ +struct iwl_fw_dump_ptrs { + struct iwl_trans_dump_data *trans_ptr; + void *fwrt_ptr; + u32 fwrt_len; +}; + +#define RADIO_REG_MAX_READ 0x2ad +static void iwl_read_radio_regs(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data) +{ + u8 *pos = (void *)(*dump_data)->data; + int i; + + IWL_DEBUG_INFO(fwrt, "WRT radio registers dump\n"); + + if (!iwl_trans_grab_nic_access(fwrt->trans)) + return; + + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); + (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); + + for (i = 0; i < RADIO_REG_MAX_READ; i++) { + u32 rd_cmd = RADIO_RSP_RD_CMD; + + rd_cmd |= i << RADIO_RSP_ADDR_POS; + iwl_trans_write_prph(fwrt->trans, RSP_RADIO_CMD, rd_cmd); + *pos = (u8)iwl_trans_read_prph(fwrt->trans, RSP_RADIO_RDDAT); + + pos++; + } + + *dump_data = iwl_fw_error_next_data(*dump_data); + + iwl_trans_release_nic_access(fwrt->trans); +} + +static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data, + int size, u32 offset, int fifo_num) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + int i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = size; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + return; + + /* Add a TLV for the RXF */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(fifo_num); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_D_SPACE + offset)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_WR_PTR + offset)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_RD_PTR + offset)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_RD_FENCE_PTR + offset)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + RXF_SET_FENCE_MODE + offset)); + + /* Lock fence */ + iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offset, 0x1); + /* Set fence pointer to the same place like WR pointer */ + iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offset, 0x1); + /* Set fence offset */ + iwl_trans_write_prph(fwrt->trans, + RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (i = 0; i < fifo_len; i++) + fifo_data[i] = iwl_trans_read_prph(fwrt->trans, + RXF_FIFO_RD_FENCE_INC + + offset); + *dump_data = iwl_fw_error_next_data(*dump_data); +} + +static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data, + int size, u32 offset, int fifo_num) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + int i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = size; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + return; + + /* Add a TLV for the FIFO */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(fifo_num); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_FIFO_ITEM_CNT + offset)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_WR_PTR + offset)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_RD_PTR + offset)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_FENCE_PTR + offset)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_LOCK_FENCE + offset)); + + /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ + iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offset, + TXF_WR_PTR + offset); + + /* Dummy-read to advance the read pointer to the head */ + iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offset); + + /* Read FIFO */ + for (i = 0; i < fifo_len / sizeof(u32); i++) + fifo_data[i] = iwl_trans_read_prph(fwrt->trans, + TXF_READ_MODIFY_DATA + + offset); + + if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) + fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx, + fifo_data, fifo_len); + + *dump_data = iwl_fw_error_next_data(*dump_data); +} + +static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data) +{ + struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; + + IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n"); + + if (!iwl_trans_grab_nic_access(fwrt->trans)) + return; + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) { + /* Pull RXF1 */ + iwl_fwrt_dump_rxf(fwrt, dump_data, + cfg->lmac[0].rxfifo1_size, 0, 0); + /* Pull RXF2 */ + iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size, + RXF_DIFF_FROM_PREV + + fwrt->trans->mac_cfg->umac_prph_offset, 1); + /* Pull LMAC2 RXF1 */ + if (fwrt->smem_cfg.num_lmacs > 1) + iwl_fwrt_dump_rxf(fwrt, dump_data, + cfg->lmac[1].rxfifo1_size, + LMAC2_PRPH_OFFSET, 2); + } + + iwl_trans_release_nic_access(fwrt->trans); +} + +static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; + u32 *fifo_data; + u32 fifo_len; + int i, j; + + IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n"); + + if (!iwl_trans_grab_nic_access(fwrt->trans)) + return; + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) { + /* Pull TXF data from LMAC1 */ + for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM, i); + iwl_fwrt_dump_txf(fwrt, dump_data, + cfg->lmac[0].txfifo_size[i], 0, i); + } + + /* Pull TXF data from LMAC2 */ + if (fwrt->smem_cfg.num_lmacs > 1) { + for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; + i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(fwrt->trans, + TXF_LARC_NUM + + LMAC2_PRPH_OFFSET, i); + iwl_fwrt_dump_txf(fwrt, dump_data, + cfg->lmac[1].txfifo_size[i], + LMAC2_PRPH_OFFSET, + i + cfg->num_txfifo_entries); + } + } + } + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) && + fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { + /* Pull UMAC internal TXF data from all TXFs */ + for (i = 0; + i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size); + i++) { + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = fwrt->smem_cfg.internal_txfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the internal FIFOs */ + (*dump_data)->type = + cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); + (*dump_data)->len = + cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(fwrt->trans, TXF_CPU2_NUM, i + + fwrt->smem_cfg.num_txfifo_entries); + + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_FIFO_ITEM_CNT)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_WR_PTR)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_RD_PTR)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_FENCE_PTR)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_LOCK_FENCE)); + + /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ + iwl_trans_write_prph(fwrt->trans, + TXF_CPU2_READ_MODIFY_ADDR, + TXF_CPU2_WR_PTR); + + /* Dummy-read to advance the read pointer to head */ + iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_READ_MODIFY_DATA); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = + iwl_trans_read_prph(fwrt->trans, + TXF_CPU2_READ_MODIFY_DATA); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + } + + iwl_trans_release_nic_access(fwrt->trans); +} + +struct iwl_prph_range { + u32 start, end; +}; + +static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = { + { .start = 0x00a00000, .end = 0x00a00000 }, + { .start = 0x00a0000c, .end = 0x00a00024 }, + { .start = 0x00a0002c, .end = 0x00a0003c }, + { .start = 0x00a00410, .end = 0x00a00418 }, + { .start = 0x00a00420, .end = 0x00a00420 }, + { .start = 0x00a00428, .end = 0x00a00428 }, + { .start = 0x00a00430, .end = 0x00a0043c }, + { .start = 0x00a00444, .end = 0x00a00444 }, + { .start = 0x00a004c0, .end = 0x00a004cc }, + { .start = 0x00a004d8, .end = 0x00a004d8 }, + { .start = 0x00a004e0, .end = 0x00a004f0 }, + { .start = 0x00a00840, .end = 0x00a00840 }, + { .start = 0x00a00850, .end = 0x00a00858 }, + { .start = 0x00a01004, .end = 0x00a01008 }, + { .start = 0x00a01010, .end = 0x00a01010 }, + { .start = 0x00a01018, .end = 0x00a01018 }, + { .start = 0x00a01024, .end = 0x00a01024 }, + { .start = 0x00a0102c, .end = 0x00a01034 }, + { .start = 0x00a0103c, .end = 0x00a01040 }, + { .start = 0x00a01048, .end = 0x00a01094 }, + { .start = 0x00a01c00, .end = 0x00a01c20 }, + { .start = 0x00a01c58, .end = 0x00a01c58 }, + { .start = 0x00a01c7c, .end = 0x00a01c7c }, + { .start = 0x00a01c28, .end = 0x00a01c54 }, + { .start = 0x00a01c5c, .end = 0x00a01c5c }, + { .start = 0x00a01c60, .end = 0x00a01cdc }, + { .start = 0x00a01ce0, .end = 0x00a01d0c }, + { .start = 0x00a01d18, .end = 0x00a01d20 }, + { .start = 0x00a01d2c, .end = 0x00a01d30 }, + { .start = 0x00a01d40, .end = 0x00a01d5c }, + { .start = 0x00a01d80, .end = 0x00a01d80 }, + { .start = 0x00a01d98, .end = 0x00a01d9c }, + { .start = 0x00a01da8, .end = 0x00a01da8 }, + { .start = 0x00a01db8, .end = 0x00a01df4 }, + { .start = 0x00a01dc0, .end = 0x00a01dfc }, + { .start = 0x00a01e00, .end = 0x00a01e2c }, + { .start = 0x00a01e40, .end = 0x00a01e60 }, + { .start = 0x00a01e68, .end = 0x00a01e6c }, + { .start = 0x00a01e74, .end = 0x00a01e74 }, + { .start = 0x00a01e84, .end = 0x00a01e90 }, + { .start = 0x00a01e9c, .end = 0x00a01ec4 }, + { .start = 0x00a01ed0, .end = 0x00a01ee0 }, + { .start = 0x00a01f00, .end = 0x00a01f1c }, + { .start = 0x00a01f44, .end = 0x00a01ffc }, + { .start = 0x00a02000, .end = 0x00a02048 }, + { .start = 0x00a02068, .end = 0x00a020f0 }, + { .start = 0x00a02100, .end = 0x00a02118 }, + { .start = 0x00a02140, .end = 0x00a0214c }, + { .start = 0x00a02168, .end = 0x00a0218c }, + { .start = 0x00a021c0, .end = 0x00a021c0 }, + { .start = 0x00a02400, .end = 0x00a02410 }, + { .start = 0x00a02418, .end = 0x00a02420 }, + { .start = 0x00a02428, .end = 0x00a0242c }, + { .start = 0x00a02434, .end = 0x00a02434 }, + { .start = 0x00a02440, .end = 0x00a02460 }, + { .start = 0x00a02468, .end = 0x00a024b0 }, + { .start = 0x00a024c8, .end = 0x00a024cc }, + { .start = 0x00a02500, .end = 0x00a02504 }, + { .start = 0x00a0250c, .end = 0x00a02510 }, + { .start = 0x00a02540, .end = 0x00a02554 }, + { .start = 0x00a02580, .end = 0x00a025f4 }, + { .start = 0x00a02600, .end = 0x00a0260c }, + { .start = 0x00a02648, .end = 0x00a02650 }, + { .start = 0x00a02680, .end = 0x00a02680 }, + { .start = 0x00a026c0, .end = 0x00a026d0 }, + { .start = 0x00a02700, .end = 0x00a0270c }, + { .start = 0x00a02804, .end = 0x00a02804 }, + { .start = 0x00a02818, .end = 0x00a0281c }, + { .start = 0x00a02c00, .end = 0x00a02db4 }, + { .start = 0x00a02df4, .end = 0x00a02fb0 }, + { .start = 0x00a03000, .end = 0x00a03014 }, + { .start = 0x00a0301c, .end = 0x00a0302c }, + { .start = 0x00a03034, .end = 0x00a03038 }, + { .start = 0x00a03040, .end = 0x00a03048 }, + { .start = 0x00a03060, .end = 0x00a03068 }, + { .start = 0x00a03070, .end = 0x00a03074 }, + { .start = 0x00a0307c, .end = 0x00a0307c }, + { .start = 0x00a03080, .end = 0x00a03084 }, + { .start = 0x00a0308c, .end = 0x00a03090 }, + { .start = 0x00a03098, .end = 0x00a03098 }, + { .start = 0x00a030a0, .end = 0x00a030a0 }, + { .start = 0x00a030a8, .end = 0x00a030b4 }, + { .start = 0x00a030bc, .end = 0x00a030bc }, + { .start = 0x00a030c0, .end = 0x00a0312c }, + { .start = 0x00a03c00, .end = 0x00a03c5c }, + { .start = 0x00a04400, .end = 0x00a04454 }, + { .start = 0x00a04460, .end = 0x00a04474 }, + { .start = 0x00a044c0, .end = 0x00a044ec }, + { .start = 0x00a04500, .end = 0x00a04504 }, + { .start = 0x00a04510, .end = 0x00a04538 }, + { .start = 0x00a04540, .end = 0x00a04548 }, + { .start = 0x00a04560, .end = 0x00a0457c }, + { .start = 0x00a04590, .end = 0x00a04598 }, + { .start = 0x00a045c0, .end = 0x00a045f4 }, +}; + +static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = { + { .start = 0x00a05c00, .end = 0x00a05c18 }, + { .start = 0x00a05400, .end = 0x00a056e8 }, + { .start = 0x00a08000, .end = 0x00a098bc }, + { .start = 0x00a02400, .end = 0x00a02758 }, + { .start = 0x00a04764, .end = 0x00a0476c }, + { .start = 0x00a04770, .end = 0x00a04774 }, + { .start = 0x00a04620, .end = 0x00a04624 }, +}; + +static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = { + { .start = 0x00a00000, .end = 0x00a00000 }, + { .start = 0x00a0000c, .end = 0x00a00024 }, + { .start = 0x00a0002c, .end = 0x00a00034 }, + { .start = 0x00a0003c, .end = 0x00a0003c }, + { .start = 0x00a00410, .end = 0x00a00418 }, + { .start = 0x00a00420, .end = 0x00a00420 }, + { .start = 0x00a00428, .end = 0x00a00428 }, + { .start = 0x00a00430, .end = 0x00a0043c }, + { .start = 0x00a00444, .end = 0x00a00444 }, + { .start = 0x00a00840, .end = 0x00a00840 }, + { .start = 0x00a00850, .end = 0x00a00858 }, + { .start = 0x00a01004, .end = 0x00a01008 }, + { .start = 0x00a01010, .end = 0x00a01010 }, + { .start = 0x00a01018, .end = 0x00a01018 }, + { .start = 0x00a01024, .end = 0x00a01024 }, + { .start = 0x00a0102c, .end = 0x00a01034 }, + { .start = 0x00a0103c, .end = 0x00a01040 }, + { .start = 0x00a01048, .end = 0x00a01050 }, + { .start = 0x00a01058, .end = 0x00a01058 }, + { .start = 0x00a01060, .end = 0x00a01070 }, + { .start = 0x00a0108c, .end = 0x00a0108c }, + { .start = 0x00a01c20, .end = 0x00a01c28 }, + { .start = 0x00a01d10, .end = 0x00a01d10 }, + { .start = 0x00a01e28, .end = 0x00a01e2c }, + { .start = 0x00a01e60, .end = 0x00a01e60 }, + { .start = 0x00a01e80, .end = 0x00a01e80 }, + { .start = 0x00a01ea0, .end = 0x00a01ea0 }, + { .start = 0x00a02000, .end = 0x00a0201c }, + { .start = 0x00a02024, .end = 0x00a02024 }, + { .start = 0x00a02040, .end = 0x00a02048 }, + { .start = 0x00a020c0, .end = 0x00a020e0 }, + { .start = 0x00a02400, .end = 0x00a02404 }, + { .start = 0x00a0240c, .end = 0x00a02414 }, + { .start = 0x00a0241c, .end = 0x00a0243c }, + { .start = 0x00a02448, .end = 0x00a024bc }, + { .start = 0x00a024c4, .end = 0x00a024cc }, + { .start = 0x00a02508, .end = 0x00a02508 }, + { .start = 0x00a02510, .end = 0x00a02514 }, + { .start = 0x00a0251c, .end = 0x00a0251c }, + { .start = 0x00a0252c, .end = 0x00a0255c }, + { .start = 0x00a02564, .end = 0x00a025a0 }, + { .start = 0x00a025a8, .end = 0x00a025b4 }, + { .start = 0x00a025c0, .end = 0x00a025c0 }, + { .start = 0x00a025e8, .end = 0x00a025f4 }, + { .start = 0x00a02c08, .end = 0x00a02c18 }, + { .start = 0x00a02c2c, .end = 0x00a02c38 }, + { .start = 0x00a02c68, .end = 0x00a02c78 }, + { .start = 0x00a03000, .end = 0x00a03000 }, + { .start = 0x00a03010, .end = 0x00a03014 }, + { .start = 0x00a0301c, .end = 0x00a0302c }, + { .start = 0x00a03034, .end = 0x00a03038 }, + { .start = 0x00a03040, .end = 0x00a03044 }, + { .start = 0x00a03060, .end = 0x00a03068 }, + { .start = 0x00a03070, .end = 0x00a03070 }, + { .start = 0x00a0307c, .end = 0x00a03084 }, + { .start = 0x00a0308c, .end = 0x00a03090 }, + { .start = 0x00a03098, .end = 0x00a03098 }, + { .start = 0x00a030a0, .end = 0x00a030a0 }, + { .start = 0x00a030a8, .end = 0x00a030b4 }, + { .start = 0x00a030bc, .end = 0x00a030c0 }, + { .start = 0x00a030c8, .end = 0x00a030f4 }, + { .start = 0x00a03100, .end = 0x00a0312c }, + { .start = 0x00a03c00, .end = 0x00a03c5c }, + { .start = 0x00a04400, .end = 0x00a04454 }, + { .start = 0x00a04460, .end = 0x00a04474 }, + { .start = 0x00a044c0, .end = 0x00a044ec }, + { .start = 0x00a04500, .end = 0x00a04504 }, + { .start = 0x00a04510, .end = 0x00a04538 }, + { .start = 0x00a04540, .end = 0x00a04548 }, + { .start = 0x00a04560, .end = 0x00a04560 }, + { .start = 0x00a04570, .end = 0x00a0457c }, + { .start = 0x00a04590, .end = 0x00a04590 }, + { .start = 0x00a04598, .end = 0x00a04598 }, + { .start = 0x00a045c0, .end = 0x00a045f4 }, + { .start = 0x00a05c18, .end = 0x00a05c1c }, + { .start = 0x00a0c000, .end = 0x00a0c018 }, + { .start = 0x00a0c020, .end = 0x00a0c028 }, + { .start = 0x00a0c038, .end = 0x00a0c094 }, + { .start = 0x00a0c0c0, .end = 0x00a0c104 }, + { .start = 0x00a0c10c, .end = 0x00a0c118 }, + { .start = 0x00a0c150, .end = 0x00a0c174 }, + { .start = 0x00a0c17c, .end = 0x00a0c188 }, + { .start = 0x00a0c190, .end = 0x00a0c198 }, + { .start = 0x00a0c1a0, .end = 0x00a0c1a8 }, + { .start = 0x00a0c1b0, .end = 0x00a0c1b8 }, +}; + +static const struct iwl_prph_range iwl_prph_dump_addr_ax210[] = { + { .start = 0x00d03c00, .end = 0x00d03c64 }, + { .start = 0x00d05c18, .end = 0x00d05c1c }, + { .start = 0x00d0c000, .end = 0x00d0c174 }, +}; + +static void iwl_read_prph_block(struct iwl_trans *trans, u32 start, + u32 len_bytes, __le32 *data) +{ + u32 i; + + for (i = 0; i < len_bytes; i += 4) + *data++ = cpu_to_le32(iwl_trans_read_prph(trans, start + i)); +} + +static void iwl_dump_prph(struct iwl_fw_runtime *fwrt, + const struct iwl_prph_range *iwl_prph_dump_addr, + u32 range_len, void *ptr) +{ + struct iwl_fw_error_dump_prph *prph; + struct iwl_trans *trans = fwrt->trans; + struct iwl_fw_error_dump_data **data = + (struct iwl_fw_error_dump_data **)ptr; + u32 i; + + if (!data) + return; + + IWL_DEBUG_INFO(trans, "WRT PRPH dump\n"); + + if (!iwl_trans_grab_nic_access(trans)) + return; + + for (i = 0; i < range_len; i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); + (*data)->len = cpu_to_le32(sizeof(*prph) + + num_bytes_in_chunk); + prph = (void *)(*data)->data; + prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); + + iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, + /* our range is inclusive, hence + 4 */ + iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4, + (void *)prph->data); + + *data = iwl_fw_error_next_data(*data); + } + + iwl_trans_release_nic_access(trans); +} + +static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt, + const struct iwl_prph_range *iwl_prph_dump_addr, + u32 range_len, void *ptr) +{ + u32 *prph_len = (u32 *)ptr; + int i, num_bytes_in_chunk; + + if (!prph_len) + return; + + for (i = 0; i < range_len; i++) { + /* The range includes both boundaries */ + num_bytes_in_chunk = + iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + *prph_len += sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } +} + +static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr, + void (*handler)(struct iwl_fw_runtime *, + const struct iwl_prph_range *, + u32, void *)) +{ + u32 range_len; + + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210); + handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr); + } else if (fwrt->trans->mac_cfg->device_family >= + IWL_DEVICE_FAMILY_22000) { + range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000); + handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr); + } else { + range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm); + handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr); + + if (fwrt->trans->mac_cfg->mq_rx_supported) { + range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000); + handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr); + } + } +} + +static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **dump_data, + u32 len, u32 ofs, u32 type) +{ + struct iwl_fw_error_dump_mem *dump_mem; + + if (!len) + return; + + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + (*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem)); + dump_mem = (void *)(*dump_data)->data; + dump_mem->type = cpu_to_le32(type); + dump_mem->offset = cpu_to_le32(ofs); + iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len); + *dump_data = iwl_fw_error_next_data(*dump_data); + + if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) + fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, ofs, + dump_mem->data, len); + + IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type); +} + +#define ADD_LEN(len, item_len, const_len) \ + do {size_t item = item_len; len += (!!item) * const_len + item; } \ + while (0) + +static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_shared_mem_cfg *mem_cfg) +{ + size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + u32 fifo_len = 0; + int i; + + if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) + return 0; + + /* Count RXF2 size */ + ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len); + + /* Count RXF1 sizes */ + if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC)) + mem_cfg->num_lmacs = MAX_NUM_LMAC; + + for (i = 0; i < mem_cfg->num_lmacs; i++) + ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len); + + return fifo_len; +} + +static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_shared_mem_cfg *mem_cfg) +{ + size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + u32 fifo_len = 0; + int i; + + if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) + goto dump_internal_txf; + + /* Count TXF sizes */ + if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC)) + mem_cfg->num_lmacs = MAX_NUM_LMAC; + + for (i = 0; i < mem_cfg->num_lmacs; i++) { + int j; + + for (j = 0; j < mem_cfg->num_txfifo_entries; j++) + ADD_LEN(fifo_len, mem_cfg->lmac[i].txfifo_size[j], + hdr_len); + } + +dump_internal_txf: + if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) && + fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG))) + goto out; + + for (i = 0; i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); i++) + ADD_LEN(fifo_len, mem_cfg->internal_txfifo_size[i], hdr_len); + +out: + return fifo_len; +} + +static void iwl_dump_paging(struct iwl_fw_runtime *fwrt, + struct iwl_fw_error_dump_data **data) +{ + int i; + + IWL_DEBUG_INFO(fwrt, "WRT paging dump\n"); + for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) { + struct iwl_fw_error_dump_paging *paging; + struct page *pages = + fwrt->fw_paging_db[i].fw_paging_block; + dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); + (*data)->len = cpu_to_le32(sizeof(*paging) + + PAGING_BLOCK_SIZE); + paging = (void *)(*data)->data; + paging->index = cpu_to_le32(i); + dma_sync_single_for_cpu(fwrt->trans->dev, addr, + PAGING_BLOCK_SIZE, + DMA_BIDIRECTIONAL); + memcpy(paging->data, page_address(pages), + PAGING_BLOCK_SIZE); + dma_sync_single_for_device(fwrt->trans->dev, addr, + PAGING_BLOCK_SIZE, + DMA_BIDIRECTIONAL); + (*data) = iwl_fw_error_next_data(*data); + + if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) + fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, + fwrt->fw_paging_db[i].fw_offs, + paging->data, + PAGING_BLOCK_SIZE); + } +} + +static struct iwl_fw_error_dump_file * +iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, + struct iwl_fw_dump_ptrs *fw_error_dump, + struct iwl_fwrt_dump_data *data) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; + struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg; + struct iwl_fw_error_dump_trigger_desc *dump_trig; + u32 sram_len, sram_ofs; + const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv; + struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; + u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0; + u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->mac_cfg->base->smem_len; + u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ? + 0 : fwrt->trans->cfg->dccm2_len; + int i; + + /* SRAM - include stack CCM if driver knows the values for it */ + if (!fwrt->trans->cfg->dccm_offset || + !fwrt->trans->cfg->dccm_len) { + const struct fw_img *img; + + if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX) + return NULL; + img = &fwrt->fw->img[fwrt->cur_fw_img]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + sram_ofs = fwrt->trans->cfg->dccm_offset; + sram_len = fwrt->trans->cfg->dccm_len; + } + + /* reading RXF/TXF sizes */ + if (iwl_trans_is_fw_error(fwrt->trans)) { + fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg); + fifo_len += iwl_fw_txf_len(fwrt, mem_cfg); + + /* Make room for PRPH registers */ + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH)) + iwl_fw_prph_handler(fwrt, &prph_len, + iwl_fw_get_prph_len); + + if (fwrt->trans->mac_cfg->device_family == + IWL_DEVICE_FAMILY_7000 && + iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG)) + radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; + } + + file_len = sizeof(*dump_file) + fifo_len + prph_len + radio_len; + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) + file_len += sizeof(*dump_data) + sizeof(*dump_info); + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) + file_len += sizeof(*dump_data) + sizeof(*dump_smem_cfg); + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) { + size_t hdr_len = sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_mem); + + /* Dump SRAM only if no mem_tlvs */ + if (!fwrt->fw->dbg.n_mem_tlv) + ADD_LEN(file_len, sram_len, hdr_len); + + /* Make room for all mem types that exist */ + ADD_LEN(file_len, smem_len, hdr_len); + ADD_LEN(file_len, sram2_len, hdr_len); + + for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) + ADD_LEN(file_len, le32_to_cpu(fw_mem[i].len), hdr_len); + } + + /* Make room for fw's virtual image pages, if it exists */ + if (iwl_fw_dbg_is_paging_enabled(fwrt)) + file_len += fwrt->num_of_paging_blk * + (sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_paging) + + PAGING_BLOCK_SIZE); + + if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { + file_len += sizeof(*dump_data) + + fwrt->trans->mac_cfg->base->d3_debug_data_length * 2; + } + + /* If we only want a monitor dump, reset the file length */ + if (data->monitor_only) { + file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + + sizeof(*dump_info) + sizeof(*dump_smem_cfg); + } + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) && + data->desc) + file_len += sizeof(*dump_data) + sizeof(*dump_trig) + + data->desc->len; + + dump_file = vzalloc(file_len); + if (!dump_file) + return NULL; + + fw_error_dump->fwrt_ptr = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_data = (void *)dump_file->data; + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *)dump_data->data; + dump_info->hw_type = + cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev)); + dump_info->hw_step = + cpu_to_le32(fwrt->trans->info.hw_rev_step); + memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strscpy_pad(dump_info->dev_human_readable, + fwrt->trans->info.name, + sizeof(dump_info->dev_human_readable)); + strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs; + dump_info->lmac_err_id[0] = + cpu_to_le32(fwrt->dump.lmac_err_id[0]); + if (fwrt->smem_cfg.num_lmacs > 1) + dump_info->lmac_err_id[1] = + cpu_to_le32(fwrt->dump.lmac_err_id[1]); + dump_info->umac_err_id = cpu_to_le32(fwrt->dump.umac_err_id); + + dump_data = iwl_fw_error_next_data(dump_data); + } + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) { + /* Dump shared memory configuration */ + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG); + dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg)); + dump_smem_cfg = (void *)dump_data->data; + dump_smem_cfg->num_lmacs = cpu_to_le32(mem_cfg->num_lmacs); + dump_smem_cfg->num_txfifo_entries = + cpu_to_le32(mem_cfg->num_txfifo_entries); + for (i = 0; i < MAX_NUM_LMAC; i++) { + int j; + u32 *txf_size = mem_cfg->lmac[i].txfifo_size; + + for (j = 0; j < TX_FIFO_MAX_NUM; j++) + dump_smem_cfg->lmac[i].txfifo_size[j] = + cpu_to_le32(txf_size[j]); + dump_smem_cfg->lmac[i].rxfifo1_size = + cpu_to_le32(mem_cfg->lmac[i].rxfifo1_size); + } + dump_smem_cfg->rxfifo2_size = + cpu_to_le32(mem_cfg->rxfifo2_size); + dump_smem_cfg->internal_txfifo_addr = + cpu_to_le32(mem_cfg->internal_txfifo_addr); + for (i = 0; i < TX_FIFO_INTERNAL_MAX_NUM; i++) { + dump_smem_cfg->internal_txfifo_size[i] = + cpu_to_le32(mem_cfg->internal_txfifo_size[i]); + } + + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* We only dump the FIFOs if the FW is in error state */ + if (fifo_len) { + iwl_fw_dump_rxf(fwrt, &dump_data); + iwl_fw_dump_txf(fwrt, &dump_data); + } + + if (radio_len) + iwl_read_radio_regs(fwrt, &dump_data); + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) && + data->desc) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_trig) + + data->desc->len); + dump_trig = (void *)dump_data->data; + memcpy(dump_trig, &data->desc->trig_desc, + sizeof(*dump_trig) + data->desc->len); + + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* In case we only want monitor dump, skip to dump transport data */ + if (data->monitor_only) + goto out; + + if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) { + const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = + fwrt->fw->dbg.mem_tlv; + + if (!fwrt->fw->dbg.n_mem_tlv) + iwl_fw_dump_mem(fwrt, &dump_data, sram_len, sram_ofs, + IWL_FW_ERROR_DUMP_MEM_SRAM); + + for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) { + u32 len = le32_to_cpu(fw_dbg_mem[i].len); + u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); + + iwl_fw_dump_mem(fwrt, &dump_data, len, ofs, + le32_to_cpu(fw_dbg_mem[i].data_type)); + } + + iwl_fw_dump_mem(fwrt, &dump_data, smem_len, + fwrt->trans->mac_cfg->base->smem_offset, + IWL_FW_ERROR_DUMP_MEM_SMEM); + + iwl_fw_dump_mem(fwrt, &dump_data, sram2_len, + fwrt->trans->cfg->dccm2_offset, + IWL_FW_ERROR_DUMP_MEM_SRAM); + } + + if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { + u32 addr = fwrt->trans->mac_cfg->base->d3_debug_data_base_addr; + size_t data_size = fwrt->trans->mac_cfg->base->d3_debug_data_length; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); + dump_data->len = cpu_to_le32(data_size * 2); + + memcpy(dump_data->data, fwrt->dump.d3_debug_data, data_size); + + kfree(fwrt->dump.d3_debug_data); + fwrt->dump.d3_debug_data = NULL; + + iwl_trans_read_mem_bytes(fwrt->trans, addr, + dump_data->data + data_size, + data_size); + + if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) + fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, addr, + dump_data->data + data_size, + data_size); + + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* Dump fw's virtual image */ + if (iwl_fw_dbg_is_paging_enabled(fwrt)) + iwl_dump_paging(fwrt, &dump_data); + + if (prph_len) + iwl_fw_prph_handler(fwrt, &dump_data, iwl_dump_prph); + +out: + dump_file->file_len = cpu_to_le32(file_len); + return dump_file; +} + +void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data) +{ + struct iwl_fw_dump_ptrs fw_error_dump = {}; + struct iwl_fw_error_dump_file *dump_file; + struct scatterlist *sg_dump_data; + u32 file_len; + u32 dump_mask = fwrt->fw->dbg.dump_mask; + + dump_file = iwl_fw_error_dump_file(fwrt, &fw_error_dump, dump_data); + if (!dump_file) + return; + + if (dump_data->monitor_only) + dump_mask &= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR); + + fw_error_dump.trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask, + fwrt->sanitize_ops, + fwrt->sanitize_ctx); + file_len = le32_to_cpu(dump_file->file_len); + fw_error_dump.fwrt_len = file_len; + + if (fw_error_dump.trans_ptr) { + file_len += fw_error_dump.trans_ptr->len; + dump_file->file_len = cpu_to_le32(file_len); + } + + sg_dump_data = iwl_fw_dbg_alloc_sgtable(file_len); + if (sg_dump_data) { + sg_pcopy_from_buffer(sg_dump_data, + sg_nents(sg_dump_data), + fw_error_dump.fwrt_ptr, + fw_error_dump.fwrt_len, 0); + if (fw_error_dump.trans_ptr) + sg_pcopy_from_buffer(sg_dump_data, + sg_nents(sg_dump_data), + fw_error_dump.trans_ptr->data, + fw_error_dump.trans_ptr->len, + fw_error_dump.fwrt_len); + dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len, + GFP_KERNEL); + } + vfree(fw_error_dump.fwrt_ptr); + vfree(fw_error_dump.trans_ptr); +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 0cffa5493704..6621ef9358bc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -13,556 +13,13 @@ #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 - * - * @fwrt_ptr: pointer to the buffer coming from fwrt - * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the - * transport's data. - * @fwrt_len: length of the valid data in fwrt_ptr - */ -struct iwl_fw_dump_ptrs { - struct iwl_trans_dump_data *trans_ptr; - void *fwrt_ptr; - u32 fwrt_len; -}; - -#define RADIO_REG_MAX_READ 0x2ad -static void iwl_read_radio_regs(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data) -{ - u8 *pos = (void *)(*dump_data)->data; - int i; - - IWL_DEBUG_INFO(fwrt, "WRT radio registers dump\n"); - - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return; - - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); - (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); - - for (i = 0; i < RADIO_REG_MAX_READ; i++) { - u32 rd_cmd = RADIO_RSP_RD_CMD; - - rd_cmd |= i << RADIO_RSP_ADDR_POS; - iwl_write_prph_no_grab(fwrt->trans, RSP_RADIO_CMD, rd_cmd); - *pos = (u8)iwl_read_prph_no_grab(fwrt->trans, RSP_RADIO_RDDAT); - - pos++; - } - - *dump_data = iwl_fw_error_next_data(*dump_data); - - iwl_trans_release_nic_access(fwrt->trans); -} - -static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data, - int size, u32 offset, int fifo_num) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - int i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = size; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - return; - - /* Add a TLV for the RXF */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(fifo_num); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - RXF_RD_D_SPACE + offset)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - RXF_RD_WR_PTR + offset)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - RXF_RD_RD_PTR + offset)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - RXF_RD_FENCE_PTR + offset)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - RXF_SET_FENCE_MODE + offset)); - - /* Lock fence */ - iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offset, 0x1); - /* Set fence pointer to the same place like WR pointer */ - iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offset, 0x1); - /* Set fence offset */ - iwl_trans_write_prph(fwrt->trans, - RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (i = 0; i < fifo_len; i++) - fifo_data[i] = iwl_trans_read_prph(fwrt->trans, - RXF_FIFO_RD_FENCE_INC + - offset); - *dump_data = iwl_fw_error_next_data(*dump_data); -} - -static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data, - int size, u32 offset, int fifo_num) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - int i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = size; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - return; - - /* Add a TLV for the FIFO */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(fifo_num); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_FIFO_ITEM_CNT + offset)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_WR_PTR + offset)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_RD_PTR + offset)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_FENCE_PTR + offset)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_LOCK_FENCE + offset)); - - /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offset, - TXF_WR_PTR + offset); - - /* Dummy-read to advance the read pointer to the head */ - iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offset); - - /* Read FIFO */ - for (i = 0; i < fifo_len / sizeof(u32); i++) - fifo_data[i] = iwl_trans_read_prph(fwrt->trans, - TXF_READ_MODIFY_DATA + - offset); - - if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) - fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx, - fifo_data, fifo_len); - - *dump_data = iwl_fw_error_next_data(*dump_data); -} - -static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data) -{ - struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; - - IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n"); - - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return; - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) { - /* Pull RXF1 */ - iwl_fwrt_dump_rxf(fwrt, dump_data, - cfg->lmac[0].rxfifo1_size, 0, 0); - /* Pull RXF2 */ - iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size, - RXF_DIFF_FROM_PREV + - fwrt->trans->mac_cfg->umac_prph_offset, 1); - /* Pull LMAC2 RXF1 */ - if (fwrt->smem_cfg.num_lmacs > 1) - iwl_fwrt_dump_rxf(fwrt, dump_data, - cfg->lmac[1].rxfifo1_size, - LMAC2_PRPH_OFFSET, 2); - } - - iwl_trans_release_nic_access(fwrt->trans); -} - -static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; - u32 *fifo_data; - u32 fifo_len; - int i, j; - - IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n"); - - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return; - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) { - /* Pull TXF data from LMAC1 */ - for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM, i); - iwl_fwrt_dump_txf(fwrt, dump_data, - cfg->lmac[0].txfifo_size[i], 0, i); - } - - /* Pull TXF data from LMAC2 */ - if (fwrt->smem_cfg.num_lmacs > 1) { - for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; - i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(fwrt->trans, - TXF_LARC_NUM + - LMAC2_PRPH_OFFSET, i); - iwl_fwrt_dump_txf(fwrt, dump_data, - cfg->lmac[1].txfifo_size[i], - LMAC2_PRPH_OFFSET, - i + cfg->num_txfifo_entries); - } - } - } - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) && - fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { - /* Pull UMAC internal TXF data from all TXFs */ - for (i = 0; - i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size); - i++) { - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = fwrt->smem_cfg.internal_txfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the internal FIFOs */ - (*dump_data)->type = - cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); - (*dump_data)->len = - cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(fwrt->trans, TXF_CPU2_NUM, i + - fwrt->smem_cfg.num_txfifo_entries); - - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_FIFO_ITEM_CNT)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_WR_PTR)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_RD_PTR)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_FENCE_PTR)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_LOCK_FENCE)); - - /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ - iwl_trans_write_prph(fwrt->trans, - TXF_CPU2_READ_MODIFY_ADDR, - TXF_CPU2_WR_PTR); - - /* Dummy-read to advance the read pointer to head */ - iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_READ_MODIFY_DATA); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = - iwl_trans_read_prph(fwrt->trans, - TXF_CPU2_READ_MODIFY_DATA); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - } - - iwl_trans_release_nic_access(fwrt->trans); -} - -struct iwl_prph_range { - u32 start, end; -}; - -static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = { - { .start = 0x00a00000, .end = 0x00a00000 }, - { .start = 0x00a0000c, .end = 0x00a00024 }, - { .start = 0x00a0002c, .end = 0x00a0003c }, - { .start = 0x00a00410, .end = 0x00a00418 }, - { .start = 0x00a00420, .end = 0x00a00420 }, - { .start = 0x00a00428, .end = 0x00a00428 }, - { .start = 0x00a00430, .end = 0x00a0043c }, - { .start = 0x00a00444, .end = 0x00a00444 }, - { .start = 0x00a004c0, .end = 0x00a004cc }, - { .start = 0x00a004d8, .end = 0x00a004d8 }, - { .start = 0x00a004e0, .end = 0x00a004f0 }, - { .start = 0x00a00840, .end = 0x00a00840 }, - { .start = 0x00a00850, .end = 0x00a00858 }, - { .start = 0x00a01004, .end = 0x00a01008 }, - { .start = 0x00a01010, .end = 0x00a01010 }, - { .start = 0x00a01018, .end = 0x00a01018 }, - { .start = 0x00a01024, .end = 0x00a01024 }, - { .start = 0x00a0102c, .end = 0x00a01034 }, - { .start = 0x00a0103c, .end = 0x00a01040 }, - { .start = 0x00a01048, .end = 0x00a01094 }, - { .start = 0x00a01c00, .end = 0x00a01c20 }, - { .start = 0x00a01c58, .end = 0x00a01c58 }, - { .start = 0x00a01c7c, .end = 0x00a01c7c }, - { .start = 0x00a01c28, .end = 0x00a01c54 }, - { .start = 0x00a01c5c, .end = 0x00a01c5c }, - { .start = 0x00a01c60, .end = 0x00a01cdc }, - { .start = 0x00a01ce0, .end = 0x00a01d0c }, - { .start = 0x00a01d18, .end = 0x00a01d20 }, - { .start = 0x00a01d2c, .end = 0x00a01d30 }, - { .start = 0x00a01d40, .end = 0x00a01d5c }, - { .start = 0x00a01d80, .end = 0x00a01d80 }, - { .start = 0x00a01d98, .end = 0x00a01d9c }, - { .start = 0x00a01da8, .end = 0x00a01da8 }, - { .start = 0x00a01db8, .end = 0x00a01df4 }, - { .start = 0x00a01dc0, .end = 0x00a01dfc }, - { .start = 0x00a01e00, .end = 0x00a01e2c }, - { .start = 0x00a01e40, .end = 0x00a01e60 }, - { .start = 0x00a01e68, .end = 0x00a01e6c }, - { .start = 0x00a01e74, .end = 0x00a01e74 }, - { .start = 0x00a01e84, .end = 0x00a01e90 }, - { .start = 0x00a01e9c, .end = 0x00a01ec4 }, - { .start = 0x00a01ed0, .end = 0x00a01ee0 }, - { .start = 0x00a01f00, .end = 0x00a01f1c }, - { .start = 0x00a01f44, .end = 0x00a01ffc }, - { .start = 0x00a02000, .end = 0x00a02048 }, - { .start = 0x00a02068, .end = 0x00a020f0 }, - { .start = 0x00a02100, .end = 0x00a02118 }, - { .start = 0x00a02140, .end = 0x00a0214c }, - { .start = 0x00a02168, .end = 0x00a0218c }, - { .start = 0x00a021c0, .end = 0x00a021c0 }, - { .start = 0x00a02400, .end = 0x00a02410 }, - { .start = 0x00a02418, .end = 0x00a02420 }, - { .start = 0x00a02428, .end = 0x00a0242c }, - { .start = 0x00a02434, .end = 0x00a02434 }, - { .start = 0x00a02440, .end = 0x00a02460 }, - { .start = 0x00a02468, .end = 0x00a024b0 }, - { .start = 0x00a024c8, .end = 0x00a024cc }, - { .start = 0x00a02500, .end = 0x00a02504 }, - { .start = 0x00a0250c, .end = 0x00a02510 }, - { .start = 0x00a02540, .end = 0x00a02554 }, - { .start = 0x00a02580, .end = 0x00a025f4 }, - { .start = 0x00a02600, .end = 0x00a0260c }, - { .start = 0x00a02648, .end = 0x00a02650 }, - { .start = 0x00a02680, .end = 0x00a02680 }, - { .start = 0x00a026c0, .end = 0x00a026d0 }, - { .start = 0x00a02700, .end = 0x00a0270c }, - { .start = 0x00a02804, .end = 0x00a02804 }, - { .start = 0x00a02818, .end = 0x00a0281c }, - { .start = 0x00a02c00, .end = 0x00a02db4 }, - { .start = 0x00a02df4, .end = 0x00a02fb0 }, - { .start = 0x00a03000, .end = 0x00a03014 }, - { .start = 0x00a0301c, .end = 0x00a0302c }, - { .start = 0x00a03034, .end = 0x00a03038 }, - { .start = 0x00a03040, .end = 0x00a03048 }, - { .start = 0x00a03060, .end = 0x00a03068 }, - { .start = 0x00a03070, .end = 0x00a03074 }, - { .start = 0x00a0307c, .end = 0x00a0307c }, - { .start = 0x00a03080, .end = 0x00a03084 }, - { .start = 0x00a0308c, .end = 0x00a03090 }, - { .start = 0x00a03098, .end = 0x00a03098 }, - { .start = 0x00a030a0, .end = 0x00a030a0 }, - { .start = 0x00a030a8, .end = 0x00a030b4 }, - { .start = 0x00a030bc, .end = 0x00a030bc }, - { .start = 0x00a030c0, .end = 0x00a0312c }, - { .start = 0x00a03c00, .end = 0x00a03c5c }, - { .start = 0x00a04400, .end = 0x00a04454 }, - { .start = 0x00a04460, .end = 0x00a04474 }, - { .start = 0x00a044c0, .end = 0x00a044ec }, - { .start = 0x00a04500, .end = 0x00a04504 }, - { .start = 0x00a04510, .end = 0x00a04538 }, - { .start = 0x00a04540, .end = 0x00a04548 }, - { .start = 0x00a04560, .end = 0x00a0457c }, - { .start = 0x00a04590, .end = 0x00a04598 }, - { .start = 0x00a045c0, .end = 0x00a045f4 }, -}; - -static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = { - { .start = 0x00a05c00, .end = 0x00a05c18 }, - { .start = 0x00a05400, .end = 0x00a056e8 }, - { .start = 0x00a08000, .end = 0x00a098bc }, - { .start = 0x00a02400, .end = 0x00a02758 }, - { .start = 0x00a04764, .end = 0x00a0476c }, - { .start = 0x00a04770, .end = 0x00a04774 }, - { .start = 0x00a04620, .end = 0x00a04624 }, -}; - -static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = { - { .start = 0x00a00000, .end = 0x00a00000 }, - { .start = 0x00a0000c, .end = 0x00a00024 }, - { .start = 0x00a0002c, .end = 0x00a00034 }, - { .start = 0x00a0003c, .end = 0x00a0003c }, - { .start = 0x00a00410, .end = 0x00a00418 }, - { .start = 0x00a00420, .end = 0x00a00420 }, - { .start = 0x00a00428, .end = 0x00a00428 }, - { .start = 0x00a00430, .end = 0x00a0043c }, - { .start = 0x00a00444, .end = 0x00a00444 }, - { .start = 0x00a00840, .end = 0x00a00840 }, - { .start = 0x00a00850, .end = 0x00a00858 }, - { .start = 0x00a01004, .end = 0x00a01008 }, - { .start = 0x00a01010, .end = 0x00a01010 }, - { .start = 0x00a01018, .end = 0x00a01018 }, - { .start = 0x00a01024, .end = 0x00a01024 }, - { .start = 0x00a0102c, .end = 0x00a01034 }, - { .start = 0x00a0103c, .end = 0x00a01040 }, - { .start = 0x00a01048, .end = 0x00a01050 }, - { .start = 0x00a01058, .end = 0x00a01058 }, - { .start = 0x00a01060, .end = 0x00a01070 }, - { .start = 0x00a0108c, .end = 0x00a0108c }, - { .start = 0x00a01c20, .end = 0x00a01c28 }, - { .start = 0x00a01d10, .end = 0x00a01d10 }, - { .start = 0x00a01e28, .end = 0x00a01e2c }, - { .start = 0x00a01e60, .end = 0x00a01e60 }, - { .start = 0x00a01e80, .end = 0x00a01e80 }, - { .start = 0x00a01ea0, .end = 0x00a01ea0 }, - { .start = 0x00a02000, .end = 0x00a0201c }, - { .start = 0x00a02024, .end = 0x00a02024 }, - { .start = 0x00a02040, .end = 0x00a02048 }, - { .start = 0x00a020c0, .end = 0x00a020e0 }, - { .start = 0x00a02400, .end = 0x00a02404 }, - { .start = 0x00a0240c, .end = 0x00a02414 }, - { .start = 0x00a0241c, .end = 0x00a0243c }, - { .start = 0x00a02448, .end = 0x00a024bc }, - { .start = 0x00a024c4, .end = 0x00a024cc }, - { .start = 0x00a02508, .end = 0x00a02508 }, - { .start = 0x00a02510, .end = 0x00a02514 }, - { .start = 0x00a0251c, .end = 0x00a0251c }, - { .start = 0x00a0252c, .end = 0x00a0255c }, - { .start = 0x00a02564, .end = 0x00a025a0 }, - { .start = 0x00a025a8, .end = 0x00a025b4 }, - { .start = 0x00a025c0, .end = 0x00a025c0 }, - { .start = 0x00a025e8, .end = 0x00a025f4 }, - { .start = 0x00a02c08, .end = 0x00a02c18 }, - { .start = 0x00a02c2c, .end = 0x00a02c38 }, - { .start = 0x00a02c68, .end = 0x00a02c78 }, - { .start = 0x00a03000, .end = 0x00a03000 }, - { .start = 0x00a03010, .end = 0x00a03014 }, - { .start = 0x00a0301c, .end = 0x00a0302c }, - { .start = 0x00a03034, .end = 0x00a03038 }, - { .start = 0x00a03040, .end = 0x00a03044 }, - { .start = 0x00a03060, .end = 0x00a03068 }, - { .start = 0x00a03070, .end = 0x00a03070 }, - { .start = 0x00a0307c, .end = 0x00a03084 }, - { .start = 0x00a0308c, .end = 0x00a03090 }, - { .start = 0x00a03098, .end = 0x00a03098 }, - { .start = 0x00a030a0, .end = 0x00a030a0 }, - { .start = 0x00a030a8, .end = 0x00a030b4 }, - { .start = 0x00a030bc, .end = 0x00a030c0 }, - { .start = 0x00a030c8, .end = 0x00a030f4 }, - { .start = 0x00a03100, .end = 0x00a0312c }, - { .start = 0x00a03c00, .end = 0x00a03c5c }, - { .start = 0x00a04400, .end = 0x00a04454 }, - { .start = 0x00a04460, .end = 0x00a04474 }, - { .start = 0x00a044c0, .end = 0x00a044ec }, - { .start = 0x00a04500, .end = 0x00a04504 }, - { .start = 0x00a04510, .end = 0x00a04538 }, - { .start = 0x00a04540, .end = 0x00a04548 }, - { .start = 0x00a04560, .end = 0x00a04560 }, - { .start = 0x00a04570, .end = 0x00a0457c }, - { .start = 0x00a04590, .end = 0x00a04590 }, - { .start = 0x00a04598, .end = 0x00a04598 }, - { .start = 0x00a045c0, .end = 0x00a045f4 }, - { .start = 0x00a05c18, .end = 0x00a05c1c }, - { .start = 0x00a0c000, .end = 0x00a0c018 }, - { .start = 0x00a0c020, .end = 0x00a0c028 }, - { .start = 0x00a0c038, .end = 0x00a0c094 }, - { .start = 0x00a0c0c0, .end = 0x00a0c104 }, - { .start = 0x00a0c10c, .end = 0x00a0c118 }, - { .start = 0x00a0c150, .end = 0x00a0c174 }, - { .start = 0x00a0c17c, .end = 0x00a0c188 }, - { .start = 0x00a0c190, .end = 0x00a0c198 }, - { .start = 0x00a0c1a0, .end = 0x00a0c1a8 }, - { .start = 0x00a0c1b0, .end = 0x00a0c1b8 }, -}; - -static const struct iwl_prph_range iwl_prph_dump_addr_ax210[] = { - { .start = 0x00d03c00, .end = 0x00d03c64 }, - { .start = 0x00d05c18, .end = 0x00d05c1c }, - { .start = 0x00d0c000, .end = 0x00d0c174 }, -}; - -static void iwl_read_prph_block(struct iwl_trans *trans, u32 start, - u32 len_bytes, __le32 *data) -{ - u32 i; - - for (i = 0; i < len_bytes; i += 4) - *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i)); -} - -static void iwl_dump_prph(struct iwl_fw_runtime *fwrt, - const struct iwl_prph_range *iwl_prph_dump_addr, - u32 range_len, void *ptr) -{ - struct iwl_fw_error_dump_prph *prph; - struct iwl_trans *trans = fwrt->trans; - struct iwl_fw_error_dump_data **data = - (struct iwl_fw_error_dump_data **)ptr; - u32 i; - - if (!data) - return; - - IWL_DEBUG_INFO(trans, "WRT PRPH dump\n"); - - if (!iwl_trans_grab_nic_access(trans)) - return; - - for (i = 0; i < range_len; i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(sizeof(*prph) + - num_bytes_in_chunk); - prph = (void *)(*data)->data; - prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); - - iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, - /* our range is inclusive, hence + 4 */ - iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4, - (void *)prph->data); - - *data = iwl_fw_error_next_data(*data); - } - - iwl_trans_release_nic_access(trans); -} /* - * alloc_sgtable - allocates (chained) scatterlist in the given size, + * iwl_fw_dbg_alloc_sgtable - allocates (chained) scatterlist in the given size, * fills it with pages and returns it * @size: the size (in bytes) of the table */ -static struct scatterlist *alloc_sgtable(ssize_t size) +struct scatterlist *iwl_fw_dbg_alloc_sgtable(ssize_t size) { struct scatterlist *result = NULL, *prev; int nents, i, n_prev; @@ -625,423 +82,6 @@ static struct scatterlist *alloc_sgtable(ssize_t size) return result; } -static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt, - const struct iwl_prph_range *iwl_prph_dump_addr, - u32 range_len, void *ptr) -{ - u32 *prph_len = (u32 *)ptr; - int i, num_bytes_in_chunk; - - if (!prph_len) - return; - - for (i = 0; i < range_len; i++) { - /* The range includes both boundaries */ - num_bytes_in_chunk = - iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - *prph_len += sizeof(struct iwl_fw_error_dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } -} - -static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr, - void (*handler)(struct iwl_fw_runtime *, - const struct iwl_prph_range *, - u32, void *)) -{ - u32 range_len; - - if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210); - handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr); - } else if (fwrt->trans->mac_cfg->device_family >= - IWL_DEVICE_FAMILY_22000) { - range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000); - handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr); - } else { - range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm); - handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr); - - if (fwrt->trans->mac_cfg->mq_rx_supported) { - range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000); - handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr); - } - } -} - -static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data, - u32 len, u32 ofs, u32 type) -{ - struct iwl_fw_error_dump_mem *dump_mem; - - if (!len) - return; - - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - (*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem)); - dump_mem = (void *)(*dump_data)->data; - dump_mem->type = cpu_to_le32(type); - dump_mem->offset = cpu_to_le32(ofs); - iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len); - *dump_data = iwl_fw_error_next_data(*dump_data); - - if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) - fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, ofs, - dump_mem->data, len); - - IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type); -} - -#define ADD_LEN(len, item_len, const_len) \ - do {size_t item = item_len; len += (!!item) * const_len + item; } \ - while (0) - -static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt, - struct iwl_fwrt_shared_mem_cfg *mem_cfg) -{ - size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - u32 fifo_len = 0; - int i; - - if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) - return 0; - - /* Count RXF2 size */ - ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len); - - /* Count RXF1 sizes */ - if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC)) - mem_cfg->num_lmacs = MAX_NUM_LMAC; - - for (i = 0; i < mem_cfg->num_lmacs; i++) - ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len); - - return fifo_len; -} - -static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt, - struct iwl_fwrt_shared_mem_cfg *mem_cfg) -{ - size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - u32 fifo_len = 0; - int i; - - if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) - goto dump_internal_txf; - - /* Count TXF sizes */ - if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC)) - mem_cfg->num_lmacs = MAX_NUM_LMAC; - - for (i = 0; i < mem_cfg->num_lmacs; i++) { - int j; - - for (j = 0; j < mem_cfg->num_txfifo_entries; j++) - ADD_LEN(fifo_len, mem_cfg->lmac[i].txfifo_size[j], - hdr_len); - } - -dump_internal_txf: - if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) && - fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG))) - goto out; - - for (i = 0; i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); i++) - ADD_LEN(fifo_len, mem_cfg->internal_txfifo_size[i], hdr_len); - -out: - return fifo_len; -} - -static void iwl_dump_paging(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **data) -{ - int i; - - IWL_DEBUG_INFO(fwrt, "WRT paging dump\n"); - for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) { - struct iwl_fw_error_dump_paging *paging; - struct page *pages = - fwrt->fw_paging_db[i].fw_paging_block; - dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); - (*data)->len = cpu_to_le32(sizeof(*paging) + - PAGING_BLOCK_SIZE); - paging = (void *)(*data)->data; - paging->index = cpu_to_le32(i); - dma_sync_single_for_cpu(fwrt->trans->dev, addr, - PAGING_BLOCK_SIZE, - DMA_BIDIRECTIONAL); - memcpy(paging->data, page_address(pages), - PAGING_BLOCK_SIZE); - dma_sync_single_for_device(fwrt->trans->dev, addr, - PAGING_BLOCK_SIZE, - DMA_BIDIRECTIONAL); - (*data) = iwl_fw_error_next_data(*data); - - if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) - fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, - fwrt->fw_paging_db[i].fw_offs, - paging->data, - PAGING_BLOCK_SIZE); - } -} - -static struct iwl_fw_error_dump_file * -iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, - struct iwl_fw_dump_ptrs *fw_error_dump, - struct iwl_fwrt_dump_data *data) -{ - struct iwl_fw_error_dump_file *dump_file; - struct iwl_fw_error_dump_data *dump_data; - struct iwl_fw_error_dump_info *dump_info; - struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg; - struct iwl_fw_error_dump_trigger_desc *dump_trig; - u32 sram_len, sram_ofs; - const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv; - struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; - u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0; - u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->mac_cfg->base->smem_len; - u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ? - 0 : fwrt->trans->cfg->dccm2_len; - int i; - - /* SRAM - include stack CCM if driver knows the values for it */ - if (!fwrt->trans->cfg->dccm_offset || - !fwrt->trans->cfg->dccm_len) { - const struct fw_img *img; - - if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX) - return NULL; - img = &fwrt->fw->img[fwrt->cur_fw_img]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } else { - sram_ofs = fwrt->trans->cfg->dccm_offset; - sram_len = fwrt->trans->cfg->dccm_len; - } - - /* reading RXF/TXF sizes */ - if (iwl_trans_is_fw_error(fwrt->trans)) { - fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg); - fifo_len += iwl_fw_txf_len(fwrt, mem_cfg); - - /* Make room for PRPH registers */ - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH)) - iwl_fw_prph_handler(fwrt, &prph_len, - iwl_fw_get_prph_len); - - if (fwrt->trans->mac_cfg->device_family == - IWL_DEVICE_FAMILY_7000 && - iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG)) - radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; - } - - file_len = sizeof(*dump_file) + fifo_len + prph_len + radio_len; - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) - file_len += sizeof(*dump_data) + sizeof(*dump_info); - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) - file_len += sizeof(*dump_data) + sizeof(*dump_smem_cfg); - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) { - size_t hdr_len = sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_mem); - - /* Dump SRAM only if no mem_tlvs */ - if (!fwrt->fw->dbg.n_mem_tlv) - ADD_LEN(file_len, sram_len, hdr_len); - - /* Make room for all mem types that exist */ - ADD_LEN(file_len, smem_len, hdr_len); - ADD_LEN(file_len, sram2_len, hdr_len); - - for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) - ADD_LEN(file_len, le32_to_cpu(fw_mem[i].len), hdr_len); - } - - /* Make room for fw's virtual image pages, if it exists */ - if (iwl_fw_dbg_is_paging_enabled(fwrt)) - file_len += fwrt->num_of_paging_blk * - (sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_paging) + - PAGING_BLOCK_SIZE); - - if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { - file_len += sizeof(*dump_data) + - fwrt->trans->mac_cfg->base->d3_debug_data_length * 2; - } - - /* If we only want a monitor dump, reset the file length */ - if (data->monitor_only) { - file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + - sizeof(*dump_info) + sizeof(*dump_smem_cfg); - } - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) && - data->desc) - file_len += sizeof(*dump_data) + sizeof(*dump_trig) + - data->desc->len; - - dump_file = vzalloc(file_len); - if (!dump_file) - return NULL; - - fw_error_dump->fwrt_ptr = dump_file; - - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_data = (void *)dump_file->data; - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_info)); - dump_info = (void *)dump_data->data; - dump_info->hw_type = - cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev)); - dump_info->hw_step = - cpu_to_le32(fwrt->trans->info.hw_rev_step); - memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable, - sizeof(dump_info->fw_human_readable)); - strscpy_pad(dump_info->dev_human_readable, - fwrt->trans->info.name, - sizeof(dump_info->dev_human_readable)); - strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name, - sizeof(dump_info->bus_human_readable)); - dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs; - dump_info->lmac_err_id[0] = - cpu_to_le32(fwrt->dump.lmac_err_id[0]); - if (fwrt->smem_cfg.num_lmacs > 1) - dump_info->lmac_err_id[1] = - cpu_to_le32(fwrt->dump.lmac_err_id[1]); - dump_info->umac_err_id = cpu_to_le32(fwrt->dump.umac_err_id); - - dump_data = iwl_fw_error_next_data(dump_data); - } - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) { - /* Dump shared memory configuration */ - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG); - dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg)); - dump_smem_cfg = (void *)dump_data->data; - dump_smem_cfg->num_lmacs = cpu_to_le32(mem_cfg->num_lmacs); - dump_smem_cfg->num_txfifo_entries = - cpu_to_le32(mem_cfg->num_txfifo_entries); - for (i = 0; i < MAX_NUM_LMAC; i++) { - int j; - u32 *txf_size = mem_cfg->lmac[i].txfifo_size; - - for (j = 0; j < TX_FIFO_MAX_NUM; j++) - dump_smem_cfg->lmac[i].txfifo_size[j] = - cpu_to_le32(txf_size[j]); - dump_smem_cfg->lmac[i].rxfifo1_size = - cpu_to_le32(mem_cfg->lmac[i].rxfifo1_size); - } - dump_smem_cfg->rxfifo2_size = - cpu_to_le32(mem_cfg->rxfifo2_size); - dump_smem_cfg->internal_txfifo_addr = - cpu_to_le32(mem_cfg->internal_txfifo_addr); - for (i = 0; i < TX_FIFO_INTERNAL_MAX_NUM; i++) { - dump_smem_cfg->internal_txfifo_size[i] = - cpu_to_le32(mem_cfg->internal_txfifo_size[i]); - } - - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* We only dump the FIFOs if the FW is in error state */ - if (fifo_len) { - iwl_fw_dump_rxf(fwrt, &dump_data); - iwl_fw_dump_txf(fwrt, &dump_data); - } - - if (radio_len) - iwl_read_radio_regs(fwrt, &dump_data); - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) && - data->desc) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_trig) + - data->desc->len); - dump_trig = (void *)dump_data->data; - memcpy(dump_trig, &data->desc->trig_desc, - sizeof(*dump_trig) + data->desc->len); - - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* In case we only want monitor dump, skip to dump trasport data */ - if (data->monitor_only) - goto out; - - if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) { - const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = - fwrt->fw->dbg.mem_tlv; - - if (!fwrt->fw->dbg.n_mem_tlv) - iwl_fw_dump_mem(fwrt, &dump_data, sram_len, sram_ofs, - IWL_FW_ERROR_DUMP_MEM_SRAM); - - for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) { - u32 len = le32_to_cpu(fw_dbg_mem[i].len); - u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); - - iwl_fw_dump_mem(fwrt, &dump_data, len, ofs, - le32_to_cpu(fw_dbg_mem[i].data_type)); - } - - iwl_fw_dump_mem(fwrt, &dump_data, smem_len, - fwrt->trans->mac_cfg->base->smem_offset, - IWL_FW_ERROR_DUMP_MEM_SMEM); - - iwl_fw_dump_mem(fwrt, &dump_data, sram2_len, - fwrt->trans->cfg->dccm2_offset, - IWL_FW_ERROR_DUMP_MEM_SRAM); - } - - if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { - u32 addr = fwrt->trans->mac_cfg->base->d3_debug_data_base_addr; - size_t data_size = fwrt->trans->mac_cfg->base->d3_debug_data_length; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); - dump_data->len = cpu_to_le32(data_size * 2); - - memcpy(dump_data->data, fwrt->dump.d3_debug_data, data_size); - - kfree(fwrt->dump.d3_debug_data); - fwrt->dump.d3_debug_data = NULL; - - iwl_trans_read_mem_bytes(fwrt->trans, addr, - dump_data->data + data_size, - data_size); - - if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) - fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, addr, - dump_data->data + data_size, - data_size); - - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* Dump fw's virtual image */ - if (iwl_fw_dbg_is_paging_enabled(fwrt)) - iwl_dump_paging(fwrt, &dump_data); - - if (prph_len) - iwl_fw_prph_handler(fwrt, &dump_data, iwl_dump_prph); - -out: - dump_file->file_len = cpu_to_le32(file_len); - return dump_file; -} - /** * struct iwl_dump_ini_region_data - region data * @reg_tlv: region TLV @@ -1063,7 +103,7 @@ static int iwl_dump_ini_prph_mac_iter_common(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = size; for (i = 0; i < le32_to_cpu(size); i += 4) - *val++ = cpu_to_le32(iwl_read_prph(fwrt->trans, addr + i)); + *val++ = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, addr + i)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1071,7 +111,7 @@ static int iwl_dump_ini_prph_mac_iter_common(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; u32 addr = le32_to_cpu(reg->addrs[idx]) + @@ -1084,7 +124,7 @@ iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_prph_mac_block_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs; @@ -1118,9 +158,6 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, indirect_wr_addr += le32_to_cpu(offset); indirect_rd_addr += le32_to_cpu(offset); - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - dphy_addr = (offset) ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW; dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr); @@ -1132,16 +169,16 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, continue; } - iwl_write_prph_no_grab(fwrt->trans, indirect_wr_addr, - WMAL_INDRCT_CMD(addr + i)); + iwl_trans_write_prph(fwrt->trans, indirect_wr_addr, + WMAL_INDRCT_CMD(addr + i)); if (fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF1 && fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF2 && fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR1 && fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR2) { udelay(2); - prph_stts = iwl_read_prph_no_grab(fwrt->trans, - WMAL_MRSPF_STTS); + prph_stts = iwl_trans_read_prph(fwrt->trans, + WMAL_MRSPF_STTS); /* Abort dump if status is 0xA5A5A5A2 or FIFO1 empty */ if (prph_stts == WMAL_TIMEOUT_VAL || @@ -1149,19 +186,18 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, break; } - prph_val = iwl_read_prph_no_grab(fwrt->trans, - indirect_rd_addr); + prph_val = iwl_trans_read_prph(fwrt->trans, + indirect_rd_addr); *val++ = cpu_to_le32(prph_val); } - iwl_trans_release_nic_access(fwrt->trans); return sizeof(*range) + le32_to_cpu(range->range_data_size); } static int iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; u32 addr = le32_to_cpu(reg->addrs[idx]); @@ -1174,7 +210,7 @@ iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, static int iwl_dump_ini_prph_phy_block_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs; @@ -1187,7 +223,7 @@ iwl_dump_ini_prph_phy_block_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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1206,7 +242,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, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_trans *trans = fwrt->trans; struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; @@ -1234,7 +270,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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1243,8 +279,8 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->dev_addr.size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(reg->dev_addr.size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(reg->dev_addr.size)); if (reg->sub_type == IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_HW_SMEM && fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) @@ -1256,7 +292,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, u32 range_len, int idx) + void *range_ptr, int idx) { struct page *page = fwrt->fw_paging_db[idx].fw_paging_block; struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1276,7 +312,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, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_fw_ini_error_dump_range *range; u32 page_size; @@ -1285,7 +321,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, idx++; if (!fwrt->trans->mac_cfg->gen2) - return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx); + return _iwl_dump_ini_paging_iter(fwrt, range_ptr, idx); range = range_ptr; page_size = fwrt->trans->init_dram.paging[idx].size; @@ -1301,7 +337,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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1320,7 +356,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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1328,8 +364,8 @@ static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->internal_buffer.size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(reg->internal_buffer.size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(reg->internal_buffer.size)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1388,7 +424,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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1403,14 +439,11 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, if (!iwl_ini_txf_iter(fwrt, reg_data, idx)) return -EIO; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo); range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num); range->range_data_size = cpu_to_le32(iter->fifo_size + registers_size); - iwl_write_prph_no_grab(fwrt->trans, TXF_LARC_NUM + offs, iter->fifo); + iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM + offs, iter->fifo); /* * read txf registers. for each register, write to the dump the @@ -1420,8 +453,8 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, addr = le32_to_cpu(reg->addrs[i]) + offs; reg_dump->addr = cpu_to_le32(addr); - reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, - addr)); + reg_dump->data = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + addr)); reg_dump++; } @@ -1432,32 +465,30 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, } /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_write_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_ADDR + offs, - TXF_WR_PTR + offs); + iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offs, + TXF_WR_PTR + offs); /* Dummy-read to advance the read pointer to the head */ - iwl_read_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_DATA + offs); + iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offs); /* Read FIFO */ addr = TXF_READ_MODIFY_DATA + offs; data = (void *)reg_dump; for (i = 0; i < iter->fifo_size; i += sizeof(*data)) - *data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr)); + *data++ = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, addr)); if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx, reg_dump, iter->fifo_size); out: - iwl_trans_release_nic_access(fwrt->trans); - return sizeof(*range) + le32_to_cpu(range->range_data_size); } static int iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, 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; @@ -1471,9 +502,6 @@ iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->dev_addr.size; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - indirect_rd_wr_addr += le32_to_cpu(offset); dphy_addr = offset ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW; @@ -1487,17 +515,16 @@ iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt, continue; } - iwl_write_prph_no_grab(fwrt->trans, indirect_rd_wr_addr, - addr + i); + iwl_trans_write_prph(fwrt->trans, indirect_rd_wr_addr, + addr + i); /* wait a bit for value to be ready in register */ udelay(1); - prph_val = iwl_read_prph_no_grab(fwrt->trans, - indirect_rd_wr_addr); + prph_val = iwl_trans_read_prph(fwrt->trans, + indirect_rd_wr_addr); *val++ = cpu_to_le32((prph_val & DPHYIP_INDIRECT_RD_MSK) >> DPHYIP_INDIRECT_RD_SHIFT); } - iwl_trans_release_nic_access(fwrt->trans); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1570,7 +597,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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1586,9 +613,6 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, if (!rxf_data.size) return -EIO; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - range->fifo_hdr.fifo_num = cpu_to_le32(rxf_data.fifo_num); range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num); range->range_data_size = cpu_to_le32(rxf_data.size + registers_size); @@ -1601,8 +625,8 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, addr = le32_to_cpu(reg->addrs[i]) + offs; reg_dump->addr = cpu_to_le32(addr); - reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, - addr)); + reg_dump->data = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, + addr)); reg_dump++; } @@ -1615,29 +639,26 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, offs = rxf_data.offset; /* Lock fence */ - iwl_write_prph_no_grab(fwrt->trans, RXF_SET_FENCE_MODE + offs, 0x1); + iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offs, 0x1); /* Set fence pointer to the same place like WR pointer */ - iwl_write_prph_no_grab(fwrt->trans, RXF_LD_WR2FENCE + offs, 0x1); + iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offs, 0x1); /* Set fence offset */ - iwl_write_prph_no_grab(fwrt->trans, RXF_LD_FENCE_OFFSET_ADDR + offs, - 0x0); + iwl_trans_write_prph(fwrt->trans, RXF_LD_FENCE_OFFSET_ADDR + offs, 0x0); /* Read FIFO */ addr = RXF_FIFO_RD_FENCE_INC + offs; data = (void *)reg_dump; for (i = 0; i < rxf_data.size; i += sizeof(*data)) - *data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr)); + *data++ = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, addr)); out: - iwl_trans_release_nic_access(fwrt->trans); - return sizeof(*range) + le32_to_cpu(range->range_data_size); } static int iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, 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; @@ -1647,8 +668,8 @@ iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = err_table->size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(err_table->size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(err_table->size)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1656,7 +677,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, u32 range_len, int idx) + void *range_ptr, 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 = @@ -1668,8 +689,8 @@ iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = special_mem->size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(special_mem->size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(special_mem->size)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1677,7 +698,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, u32 range_len, int idx) + void *range_ptr, 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; @@ -1685,27 +706,24 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, u32 prph_data; int i; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - range->range_data_size = reg->dev_addr.size; 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 : - DBGI_SRAM_TARGET_ACCESS_RDATA_LSB); - if (iwl_trans_is_hw_error_value(prph_data)) { - iwl_trans_release_nic_access(fwrt->trans); + prph_data = + iwl_trans_read_prph(fwrt->trans, + (i % 2) ? + DBGI_SRAM_TARGET_ACCESS_RDATA_MSB : + DBGI_SRAM_TARGET_ACCESS_RDATA_LSB); + if (iwl_trans_is_hw_error_value(prph_data)) return -EBUSY; - } *val++ = cpu_to_le32(prph_data); } - iwl_trans_release_nic_access(fwrt->trans); + return sizeof(*range) + le32_to_cpu(range->range_data_size); } static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range_ptr, u32 range_len, int idx) + void *range_ptr, int idx) { struct iwl_fw_ini_error_dump_range *range = range_ptr; struct iwl_rx_packet *pkt = reg_data->dump_data->fw_pkt; @@ -1726,7 +744,7 @@ static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, 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) + void *range_ptr, int idx) { /* read the IMR memory and DMA it to SRAM */ struct iwl_fw_ini_error_dump_range *range = range_ptr; @@ -1746,8 +764,8 @@ static int iwl_dump_ini_imr_iter(struct iwl_fw_runtime *fwrt, 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); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, sram_addr, range->data, + size_to_dump); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1791,7 +809,7 @@ static __le32 iwl_get_mon_reg(struct iwl_fw_runtime *fwrt, u32 alloc_id, if (!reg_info || !reg_info->addr || !reg_info->mask) return 0; - val = iwl_read_prph_no_grab(fwrt->trans, reg_info->addr + offs); + val = iwl_trans_read_prph(fwrt->trans, reg_info->addr + offs); return cpu_to_le32(mask_apply_and_normalize(val, reg_info->mask)); } @@ -1801,11 +819,6 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id, struct iwl_fw_ini_monitor_dump *data, const struct iwl_fw_mon_regs *addrs) { - if (!iwl_trans_grab_nic_access(fwrt->trans)) { - IWL_ERR(fwrt, "Failed to get monitor header\n"); - return NULL; - } - data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id, &addrs->write_ptr); if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { @@ -1818,8 +831,6 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id, data->cur_frag = iwl_get_mon_reg(fwrt, alloc_id, &addrs->cur_frag); - iwl_trans_release_nic_access(fwrt->trans); - data->header.version = cpu_to_le32(IWL_INI_DUMP_VER); return data->data; @@ -2225,6 +1236,7 @@ iwl_dump_ini_imr_get_size(struct iwl_fw_runtime *fwrt, * the first range or NULL if failed to fill headers. * @fill_range: copies a given memory range into the dump. * Returns the size of the range or negative error value otherwise. + * @requires_nic_access: indicates to dump only if NIC access was acquired */ struct iwl_dump_ini_mem_ops { u32 (*get_num_of_ranges)(struct iwl_fw_runtime *fwrt, @@ -2236,24 +1248,23 @@ struct iwl_dump_ini_mem_ops { void *data, u32 data_len); int (*fill_range)(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, - void *range, u32 range_len, int idx); + void *range, int idx); + bool requires_nic_access; }; -/** - * iwl_dump_ini_mem - dump memory region - * - * @fwrt: fw runtime struct - * @list: list to add the dump tlv to - * @reg_data: memory region - * @ops: memory dump operations - * - * Creates a dump tlv and copy a memory region into it. - * - * Returns: the size of the current dump tlv or 0 if failed - */ -static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, - struct iwl_dump_ini_region_data *reg_data, - const struct iwl_dump_ini_mem_ops *ops) +struct iwl_fw_ini_dump_entry { + const struct iwl_dump_ini_mem_ops *ops; + struct iwl_dump_ini_region_data reg_data; + struct list_head list; + u32 region_dump_policy; + u32 size; + u8 data[]; +} __packed; + +static void iwl_dump_ini_mem_prep(struct iwl_fw_runtime *fwrt, + struct list_head *list, + struct iwl_dump_ini_region_data *reg_data, + const struct iwl_dump_ini_mem_ops *ops) { struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; struct iwl_fw_ini_dump_entry *entry; @@ -2261,58 +1272,59 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, struct iwl_fw_ini_error_dump_header *header; u32 type = reg->type; u32 id = le32_get_bits(reg->id, IWL_FW_INI_REGION_ID_MASK); - u32 num_of_ranges, i, size; - u8 *range; - u32 free_size; - u64 header_size; + u32 num_of_ranges, size; u32 dump_policy = IWL_FW_INI_DUMP_VERBOSE; + u32 dp; 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); + 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; + return; } 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; + return; } 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; + return; } + } else { + dp = 0; } if (!ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr || !ops->fill_range) { IWL_DEBUG_FW(fwrt, "WRT: no ops for collecting data\n"); - return 0; + return; } size = ops->get_size(fwrt, reg_data); if (size < sizeof(*header)) { IWL_DEBUG_FW(fwrt, "WRT: size didn't include space for header\n"); - return 0; + return; } entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + size); if (!entry) - return 0; + return; entry->size = sizeof(*tlv) + size; + entry->reg_data = *reg_data; + entry->region_dump_policy = dp; tlv = (void *)entry->data; tlv->type = reg->type; @@ -2329,7 +1341,33 @@ 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); - free_size = size; + entry->ops = ops; + list_add_tail(&entry->list, list); +} + +static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_dump_entry *entry, + bool have_nic_access) +{ + struct iwl_dump_ini_region_data *reg_data = &entry->reg_data; + struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; + const struct iwl_dump_ini_mem_ops *ops = entry->ops; + struct iwl_fw_ini_error_dump_data *tlv; + struct iwl_fw_ini_error_dump_header *header; + u32 type = reg->type; + u32 id = le32_get_bits(reg->id, IWL_FW_INI_REGION_ID_MASK); + u32 i; + u8 *range; + u32 free_size; + u64 header_size; + + if (!have_nic_access && ops->requires_nic_access) + goto out_err; + + tlv = (void *)entry->data; + header = (void *)tlv->data; + + free_size = entry->size - sizeof(*tlv); range = ops->fill_mem_hdr(fwrt, reg_data, header, free_size); if (!range) { IWL_ERR(fwrt, @@ -2350,9 +1388,8 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, free_size -= header_size; - for (i = 0; i < num_of_ranges; i++) { - int range_size = ops->fill_range(fwrt, reg_data, range, - free_size, i); + for (i = 0; i < le32_to_cpu(header->num_of_ranges); i++) { + int range_size = ops->fill_range(fwrt, reg_data, range, i); if (range_size < 0) { IWL_ERR(fwrt, @@ -2372,11 +1409,10 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, range = range + range_size; } - list_add_tail(&entry->list, list); - return entry->size; out_err: + list_del(&entry->list); vfree(entry); return 0; @@ -2481,36 +1517,42 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_mon_smem_get_size, .fill_mem_hdr = iwl_dump_ini_mon_smem_fill_header, .fill_range = iwl_dump_ini_mon_smem_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_DRAM_BUFFER] = { .get_num_of_ranges = iwl_dump_ini_mon_dram_ranges, .get_size = iwl_dump_ini_mon_dram_get_size, .fill_mem_hdr = iwl_dump_ini_mon_dram_fill_header, .fill_range = iwl_dump_ini_mon_dram_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_TXF] = { .get_num_of_ranges = iwl_dump_ini_txf_ranges, .get_size = iwl_dump_ini_txf_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_txf_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_RXF] = { .get_num_of_ranges = iwl_dump_ini_single_range, .get_size = iwl_dump_ini_rxf_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_rxf_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_LMAC_ERROR_TABLE] = { .get_num_of_ranges = iwl_dump_ini_single_range, .get_size = iwl_dump_ini_err_table_get_size, .fill_mem_hdr = iwl_dump_ini_err_table_fill_header, .fill_range = iwl_dump_ini_err_table_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_UMAC_ERROR_TABLE] = { .get_num_of_ranges = iwl_dump_ini_single_range, .get_size = iwl_dump_ini_err_table_get_size, .fill_mem_hdr = iwl_dump_ini_err_table_fill_header, .fill_range = iwl_dump_ini_err_table_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_RSP_OR_NOTIF] = { .get_num_of_ranges = iwl_dump_ini_single_range, @@ -2523,30 +1565,35 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_mem_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_dev_mem_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_MAC] = { .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, .fill_range = iwl_dump_ini_prph_mac_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_PHY] = { .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, .fill_range = iwl_dump_ini_prph_phy_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE] = { .get_num_of_ranges = iwl_dump_ini_mem_block_ranges, .get_size = iwl_dump_ini_mem_block_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_mac_block_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE] = { .get_num_of_ranges = iwl_dump_ini_mem_block_ranges, .get_size = iwl_dump_ini_mem_block_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_phy_block_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_AUX] = {}, [IWL_FW_INI_REGION_PAGING] = { @@ -2566,6 +1613,7 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_imr_get_size, .fill_mem_hdr = iwl_dump_ini_imr_fill_header, .fill_range = iwl_dump_ini_imr_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PCI_IOSF_CONFIG] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, @@ -2578,18 +1626,21 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_special_mem_get_size, .fill_mem_hdr = iwl_dump_ini_special_mem_fill_header, .fill_range = iwl_dump_ini_special_mem_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_DBGI_SRAM] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .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, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP] = { .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, .fill_range = iwl_dump_ini_prph_snps_dphyip_iter, + .requires_nic_access = true, }, }; @@ -2605,22 +1656,19 @@ static bool iwl_dump_due_to_error(enum iwl_fw_ini_time_point tp_id) tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR; } -static u32 -iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt, - struct iwl_fwrt_dump_data *dump_data, - struct list_head *list, - enum iwl_fw_ini_time_point tp_id, - u64 regions_mask, - struct iwl_dump_ini_region_data *imr_reg_data, - enum iwl_dump_ini_region_selector which) +static void +iwl_dump_ini_dump_regions_prep(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data, + struct list_head *list, + enum iwl_fw_ini_time_point tp_id, + u64 regions_mask, + struct iwl_ucode_tlv **imr_tlv) { - u32 size = 0; - for (int i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) { struct iwl_dump_ini_region_data reg_data = { .dump_data = dump_data, }; - u32 reg_type, dp; + u32 reg_type; struct iwl_fw_ini_region_tlv *reg; if (!(BIT_ULL(i) & regions_mask)) @@ -2638,8 +1686,6 @@ iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt, if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops)) continue; - dp = le32_get_bits(reg->id, IWL_FW_INI_REGION_DUMP_POLICY_MASK); - if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY || reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE || reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) && @@ -2650,6 +1696,42 @@ iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt, 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 (iwl_dump_due_to_error(tp_id)) + *imr_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; + } + + iwl_dump_ini_mem_prep(fwrt, list, ®_data, + &iwl_dump_ini_region_ops[reg_type]); + } +} + +static u32 +iwl_dump_ini_dump_entries(struct iwl_fw_runtime *fwrt, + struct list_head *list, + enum iwl_dump_ini_region_selector which) +{ + struct iwl_fw_ini_dump_entry *entry, *tmp; + bool have_nic_access; + u32 size = 0; + + have_nic_access = iwl_trans_grab_nic_access(fwrt->trans); + + list_for_each_entry_safe(entry, tmp, list, list) { + u32 dp = entry->region_dump_policy; + switch (which) { case IWL_INI_DUMP_ALL_REGIONS: break; @@ -2663,29 +1745,15 @@ iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt, break; } - /* - * 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 (iwl_dump_due_to_error(tp_id)) - 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, entry, have_nic_access); - - size += iwl_dump_ini_mem(fwrt, list, ®_data, - &iwl_dump_ini_region_ops[reg_type]); + if (have_nic_access) + iwl_trans_resched_with_nic_access(fwrt->trans); } + if (have_nic_access) + iwl_trans_release_nic_access(fwrt->trans); + return size; } @@ -2706,32 +1774,32 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) < ARRAY_SIZE(fwrt->trans->dbg.active_regions)); + iwl_dump_ini_dump_regions_prep(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data.reg_tlv); + + /* append DRAM_IMR region to be collected last */ + if (imr_reg_data.reg_tlv) + iwl_dump_ini_mem_prep(fwrt, list, &imr_reg_data, + &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); + if (trigger->apply_policy & cpu_to_le32(IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET)) { - size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, - regions_mask, &imr_reg_data, + size += iwl_dump_ini_dump_entries(fwrt, list, IWL_INI_DUMP_EARLY_REGIONS); iwl_trans_pcie_fw_reset_handshake(fwrt->trans); - size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, - regions_mask, &imr_reg_data, + size += iwl_dump_ini_dump_entries(fwrt, list, IWL_INI_DUMP_LATE_REGIONS); } else { if (fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT) && iwl_dump_due_to_error(tp_id)) iwl_trans_pcie_fw_reset_handshake(fwrt->trans); - size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, - regions_mask, &imr_reg_data, + size += iwl_dump_ini_dump_entries(fwrt, list, IWL_INI_DUMP_ALL_REGIONS); } - /* collect DRAM_IMR region in the last */ - if (imr_reg_data.reg_tlv) - size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data, - &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); - if (size) { + if (size) size += iwl_dump_ini_info(fwrt, trigger, list); - } return size; } @@ -2797,52 +1865,6 @@ static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt, fwrt->dump.umac_err_id = 0; } -static void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt, - struct iwl_fwrt_dump_data *dump_data) -{ - struct iwl_fw_dump_ptrs fw_error_dump = {}; - struct iwl_fw_error_dump_file *dump_file; - struct scatterlist *sg_dump_data; - u32 file_len; - u32 dump_mask = fwrt->fw->dbg.dump_mask; - - dump_file = iwl_fw_error_dump_file(fwrt, &fw_error_dump, dump_data); - if (!dump_file) - return; - - if (dump_data->monitor_only) - dump_mask &= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR); - - fw_error_dump.trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask, - fwrt->sanitize_ops, - fwrt->sanitize_ctx); - file_len = le32_to_cpu(dump_file->file_len); - fw_error_dump.fwrt_len = file_len; - - if (fw_error_dump.trans_ptr) { - file_len += fw_error_dump.trans_ptr->len; - dump_file->file_len = cpu_to_le32(file_len); - } - - sg_dump_data = alloc_sgtable(file_len); - if (sg_dump_data) { - sg_pcopy_from_buffer(sg_dump_data, - sg_nents(sg_dump_data), - fw_error_dump.fwrt_ptr, - fw_error_dump.fwrt_len, 0); - if (fw_error_dump.trans_ptr) - sg_pcopy_from_buffer(sg_dump_data, - sg_nents(sg_dump_data), - fw_error_dump.trans_ptr->data, - fw_error_dump.trans_ptr->len, - fw_error_dump.fwrt_len); - dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len, - GFP_KERNEL); - } - vfree(fw_error_dump.fwrt_ptr); - vfree(fw_error_dump.trans_ptr); -} - static void iwl_dump_ini_list_free(struct list_head *list) { while (!list_empty(list)) { @@ -2871,7 +1893,7 @@ static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, if (!file_len) return; - sg_dump_data = alloc_sgtable(file_len); + sg_dump_data = iwl_fw_dbg_alloc_sgtable(file_len); if (sg_dump_data) { struct iwl_fw_ini_dump_entry *entry; int sg_entries = sg_nents(sg_dump_data); @@ -3361,8 +2383,8 @@ static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans, int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt) { - struct iwl_mvm_marker marker = { - .dw_len = sizeof(struct iwl_mvm_marker) / 4, + struct iwl_marker marker = { + .dw_len = sizeof(struct iwl_marker) / 4, .marker_id = MARKER_ID_SYNC_CLOCK, }; struct iwl_host_cmd hcmd = { @@ -3370,7 +2392,7 @@ int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt) .id = WIDE_ID(LONG_GROUP, MARKER_CMD), .dataflags = {}, }; - struct iwl_mvm_marker_rsp *resp; + struct iwl_marker_rsp *resp; int cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, WIDE_ID(LONG_GROUP, MARKER_CMD), IWL_FW_CMD_VER_UNKNOWN); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 8034c9ecba69..fc962a320583 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-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2019, 2021-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -38,6 +38,11 @@ struct iwl_fw_dbg_params { u32 out_ctrl; }; +/* old-style dump entry point */ +void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data); +struct scatterlist *iwl_fw_dbg_alloc_sgtable(ssize_t size); + extern const struct iwl_fw_dump_desc iwl_dump_desc_assert; int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index ddee7c2deb36..f06978d5b5ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024, 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -275,16 +275,19 @@ static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf, goto out; } + /* ignore this flag, we cannot use the response */ + hcmd.flags &= ~CMD_WANT_SKB; + /* reject flags other than async, they cannot be used this way */ + if (hcmd.flags & ~CMD_ASYNC) { + ret = -EINVAL; + goto out; + } + if (fwrt->ops && fwrt->ops->send_hcmd) ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd); else ret = -EPERM; - if (ret < 0) - goto out; - - if (hcmd.flags & CMD_WANT_SKB) - iwl_free_resp(&hcmd); out: kfree(data); return ret ?: count; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index 525a82030daa..07f1240df866 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2014, 2018-2025 Intel Corporation + * Copyright (C) 2014, 2018-2026 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -265,18 +265,6 @@ struct iwl_fw_ini_error_dump_data { __u8 data[]; } __packed; -/** - * struct iwl_fw_ini_dump_entry - dump entry descriptor - * @list: list of dump entries - * @size: size of the data - * @data: entry data - */ -struct iwl_fw_ini_dump_entry { - struct list_head list; - u32 size; - u8 data[]; -} __packed; - /** * struct iwl_fw_ini_dump_file_hdr - header of dump file * @barker: must be %IWL_FW_INI_ERROR_DUMP_BARKER diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index f7a6f21267e9..a26ed82a8106 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2008-2014, 2018-2024 Intel Corporation + * Copyright (C) 2008-2014, 2018-2024, 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -111,6 +111,8 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_FW_NUM_STATIONS = IWL_UCODE_TLV_CONST_BASE + 0, IWL_UCODE_TLV_FW_NUM_LINKS = IWL_UCODE_TLV_CONST_BASE + 1, IWL_UCODE_TLV_FW_NUM_BEACONS = IWL_UCODE_TLV_CONST_BASE + 2, + IWL_UCODE_TLV_FW_NUM_MCAST_KEY_ENTRIES = IWL_UCODE_TLV_CONST_BASE + 3, + IWL_UCODE_TLV_FW_NAN_MAX_CHAN_SWITCH_TIME = IWL_UCODE_TLV_CONST_BASE + 4, IWL_UCODE_TLV_TYPE_DEBUG_INFO = IWL_UCODE_TLV_DEBUG_BASE + 0, IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_TLV_DEBUG_BASE + 1, @@ -1063,10 +1065,15 @@ struct iwl_fw_dump_exclude { __le32 addr, size; }; -struct iwl_fw_fseq_bin_version { +struct iwl_fw_fseq_bin_version_v1 { __le32 major, minor; }; /* FW_TLV_FSEQ_BIN_VERSION_S */ +struct iwl_fw_fseq_bin_version { + /* rf_id is currently unused and always zero */ + __le32 mac_id, rf_id, major, minor; +}; /* FW_TLV_FSEQ_BIN_VERSION_S */ + static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv, size_t fixed_size, size_t var_size) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index 94113d1db8e1..95b45a253641 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2024, 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -53,6 +53,8 @@ struct iwl_ucode_capabilities { u32 num_stations; u32 num_links; u32 num_beacons; + u32 num_mcast_key_entries; + u16 nan_max_chan_switch_time; DECLARE_BITMAP(_api, NUM_IWL_UCODE_TLV_API); DECLARE_BITMAP(_capa, NUM_IWL_UCODE_TLV_CAPA); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 55128caac7ed..8d9ff36e30f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2023, 2025 Intel Corporation + * Copyright (C) 2023, 2025-2026 Intel Corporation */ #include #include "iwl-drv.h" @@ -112,6 +112,11 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = { DMI_MATCH(DMI_SYS_VENDOR, "WIKO"), }, }, + { .ident = "XIAOMI", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"), + }, + }, {} }; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index 746f2acffb8f..ccdd6856572b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2021-2022, 2025 Intel Corporation + * Copyright (C) 2021-2022, 2025-2026 Intel Corporation */ #include @@ -8,27 +8,6 @@ #include "iwl-drv.h" #include "iwl-config.h" -#define IWL_DECLARE_RATE_INFO(r) \ - [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP - -/* - * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP - * */ -static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1), - IWL_DECLARE_RATE_INFO(2), - IWL_DECLARE_RATE_INFO(5), - IWL_DECLARE_RATE_INFO(11), - IWL_DECLARE_RATE_INFO(6), - IWL_DECLARE_RATE_INFO(9), - IWL_DECLARE_RATE_INFO(12), - IWL_DECLARE_RATE_INFO(18), - IWL_DECLARE_RATE_INFO(24), - IWL_DECLARE_RATE_INFO(36), - IWL_DECLARE_RATE_INFO(48), - IWL_DECLARE_RATE_INFO(54), -}; - /* mbps, mcs */ static const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = { { "1", "BPSK DSSS"}, @@ -61,12 +40,6 @@ static const char * const pretty_bw[] = { "320Mhz", }; -u8 iwl_fw_rate_idx_to_plcp(int idx) -{ - return fw_rate_idx_to_plcp[idx]; -} -IWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp); - const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx) { return &rate_mcs[idx]; @@ -124,6 +97,9 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) case RATE_MCS_MOD_TYPE_EHT: type = "EHT"; break; + case RATE_MCS_MOD_TYPE_UHR: + type = "UHR"; + break; default: type = "Unknown"; /* shouldn't happen */ } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 5f40cd15e27f..6a3539ad7331 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ @@ -372,7 +372,7 @@ struct iwl_mac_cfg { }; /* - * These sizes were picked according to 8 MSDUs inside 64/256/612 A-MSDUs + * These sizes were picked according to 8 MSDUs inside 64/256/512 A-MSDUs * in an A-MPDU, with additional overhead to account for processing time. * They will be doubled for MACs starting from So/Ty that don't support * putting multiple frames into a single buffer. @@ -416,6 +416,7 @@ struct iwl_mac_cfg { * @nvm_type: see &enum iwl_nvm_type * @uhb_supported: ultra high band channels supported * @eht_supported: EHT supported + * @uhr_supported: UHR supported * @num_rbds: number of receive buffer descriptors to use * (only used for multi-queue capable devices) * @@ -449,7 +450,8 @@ struct iwl_rf_cfg { lp_xtal_workaround:1, vht_mu_mimo_supported:1, uhb_supported:1, - eht_supported:1; + eht_supported:1, + uhr_supported:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; @@ -667,6 +669,7 @@ extern const char iwl_ax411_killer_1690s_name[]; extern const char iwl_ax411_killer_1690i_name[]; extern const char iwl_ax210_name[]; extern const char iwl_ax211_name[]; +extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_killer_be1750s_name[]; extern const char iwl_killer_be1750i_name[]; @@ -674,6 +677,7 @@ extern const char iwl_killer_be1750w_name[]; extern const char iwl_killer_be1750x_name[]; extern const char iwl_killer_be1790s_name[]; extern const char iwl_killer_be1790i_name[]; +extern const char iwl_killer_be1730x_name[]; extern const char iwl_be201_name[]; extern const char iwl_be200_name[]; extern const char iwl_be202_name[]; @@ -681,6 +685,7 @@ extern const char iwl_be401_name[]; extern const char iwl_be213_name[]; extern const char iwl_killer_be1775s_name[]; extern const char iwl_killer_be1775i_name[]; +extern const char iwl_killer_be1735x_name[]; extern const char iwl_be211_name[]; extern const char iwl_killer_bn1850w2_name[]; extern const char iwl_killer_bn1850i_name[]; @@ -736,6 +741,7 @@ extern const struct iwl_rf_cfg iwl_rf_hr; extern const struct iwl_rf_cfg iwl_rf_hr_80mhz; extern const struct iwl_rf_cfg iwl_rf_gf; +#define iwl_rf_ot iwl_rf_hr_80mhz #endif /* CONFIG_IWLMVM */ #if IS_ENABLED(CONFIG_IWLMLD) @@ -744,7 +750,8 @@ extern const struct iwl_rf_cfg iwl_rf_fm_160mhz; #define iwl_rf_wh iwl_rf_fm #define iwl_rf_wh_160mhz iwl_rf_fm_160mhz extern const struct iwl_rf_cfg iwl_rf_wh_non_eht; -#define iwl_rf_pe iwl_rf_fm +extern const struct iwl_rf_cfg iwl_rf_pe; +#define iwl_rf_pe_no_uhr iwl_rf_fm #endif /* CONFIG_IWLMLD */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index f3fa37fee2e4..d2fa80a3dd04 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -366,6 +366,7 @@ enum { #define CSR_HW_RF_ID_TYPE_GF4 (0x0010E000) #define CSR_HW_RF_ID_TYPE_FM (0x00112000) #define CSR_HW_RF_ID_TYPE_WP (0x00113000) +#define CSR_HW_RF_ID_TYPE_PE (0x00114000) /* HW_RF CHIP STEP */ #define CSR_HW_RF_STEP(_val) (((_val) >> 8) & 0xF) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index d5ded4d3a30b..842586d4fc5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1294,8 +1294,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(*fseq_ver)) goto invalid_tlv_len; - IWL_DEBUG_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n", - fseq_ver->version); + IWL_DEBUG_INFO(drv, + "TLV_FW_FSEQ_VERSION: %.32s (sha1: %.20s)\n", + fseq_ver->version, fseq_ver->sha1); } break; case IWL_UCODE_TLV_FW_NUM_STATIONS: @@ -1330,6 +1331,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, capa->num_beacons = le32_to_cpup((const __le32 *)tlv_data); break; + case IWL_UCODE_TLV_FW_NUM_MCAST_KEY_ENTRIES: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->num_mcast_key_entries = + le32_to_cpup((const __le32 *)tlv_data); + break; + case IWL_UCODE_TLV_FW_NAN_MAX_CHAN_SWITCH_TIME: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->nan_max_chan_switch_time = + le32_to_cpup((const __le32 *)tlv_data); + break; case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { const struct iwl_umac_debug_addrs *dbg_ptrs = (const void *)tlv_data; @@ -1640,6 +1653,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX; fw->ucode_capa.num_beacons = 1; + fw->ucode_capa.num_mcast_key_entries = 2; /* dump all fw memory areas by default */ fw->dbg.dump_mask = 0xffffffff; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index b1944584c693..bb746112ddad 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation + * Copyright (C) 2003-2014, 2018-2022, 2024-2026 Intel Corporation * Copyright (C) 2015-2016 Intel Deutschland GmbH */ #include @@ -12,7 +12,6 @@ #include "iwl-debug.h" #include "iwl-prph.h" #include "iwl-fh.h" -#include "pcie/gen1_2/internal.h" void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) { @@ -168,6 +167,22 @@ int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, return -ETIMEDOUT; } +int iwl_poll_umac_prph_bits_no_grab(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int t = 0; + + do { + if ((iwl_read_umac_prph_no_grab(trans, addr) & mask) == + (bits & mask)) + return 0; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} + void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { if (iwl_trans_grab_nic_access(trans)) { @@ -396,12 +411,6 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf) return 0; } -int iwl_trans_activate_nic(struct iwl_trans *trans) -{ - return iwl_pcie_gen1_2_activate_nic(trans); -} -IWL_EXPORT_SYMBOL(iwl_trans_activate_nic); - void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr, u32 sw_err_bit) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 5bcec239ffc4..6dce2e5267a6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2021, 2025 Intel Corporation + * Copyright (C) 2018-2021, 2025-2026 Intel Corporation */ #ifndef __iwl_io_h__ #define __iwl_io_h__ @@ -51,14 +51,14 @@ static inline void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout); +int iwl_poll_umac_prph_bits_no_grab(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout); void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, u32 bits, u32 mask); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); void iwl_force_nmi(struct iwl_trans *trans); -int iwl_trans_activate_nic(struct iwl_trans *trans); - /* Error handling */ int iwl_dump_fh(struct iwl_trans *trans, char **buf); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 8f3f651451bb..d47b4ae2f486 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023, 2025-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -18,7 +18,6 @@ #include "iwl-prph.h" #include "iwl-io.h" #include "iwl-csr.h" -#include "fw/acpi.h" #include "fw/api/nvm-reg.h" #include "fw/api/commands.h" #include "fw/api/cmdhdr.h" @@ -206,28 +205,30 @@ enum iwl_reg_capa_flags_v2 { }; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_2 */ /** - * enum iwl_reg_capa_flags_v4 - global flags applied for the whole regulatory + * enum iwl_reg_capa_flags_v5 - global flags applied for the whole regulatory * domain. - * @REG_CAPA_V4_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed + * @REG_CAPA_V5_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed * for this regulatory domain (valid only in 5Ghz). - * @REG_CAPA_V4_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed + * @REG_CAPA_V5_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed * for this regulatory domain (valid only in 5Ghz). - * @REG_CAPA_V4_MCS_12_ALLOWED: 11ac with MCS 12 is allowed. - * @REG_CAPA_V4_MCS_13_ALLOWED: 11ac with MCS 13 is allowed. - * @REG_CAPA_V4_11BE_DISABLED: 11be is forbidden for this regulatory domain. - * @REG_CAPA_V4_11AX_DISABLED: 11ax is forbidden for this regulatory domain. - * @REG_CAPA_V4_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed + * @REG_CAPA_V5_MCS_12_ALLOWED: 11ac with MCS 12 is allowed. + * @REG_CAPA_V5_MCS_13_ALLOWED: 11ac with MCS 13 is allowed. + * @REG_CAPA_V5_11BE_DISABLED: 11be is forbidden for this regulatory domain. + * @REG_CAPA_V5_11AX_DISABLED: 11ax is forbidden for this regulatory domain. + * @REG_CAPA_V5_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed * for this regulatory domain (valid only in 5GHz). + * @REG_CAPA_V5_11BN_DISABLED: UHR is not allowed for this regulatory domain */ -enum iwl_reg_capa_flags_v4 { - REG_CAPA_V4_160MHZ_ALLOWED = BIT(3), - REG_CAPA_V4_80MHZ_ALLOWED = BIT(4), - REG_CAPA_V4_MCS_12_ALLOWED = BIT(5), - REG_CAPA_V4_MCS_13_ALLOWED = BIT(6), - REG_CAPA_V4_11BE_DISABLED = BIT(8), - REG_CAPA_V4_11AX_DISABLED = BIT(13), - REG_CAPA_V4_320MHZ_ALLOWED = BIT(16), -}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4 */ +enum iwl_reg_capa_flags_v5 { + REG_CAPA_V5_160MHZ_ALLOWED = BIT(3), + REG_CAPA_V5_80MHZ_ALLOWED = BIT(4), + REG_CAPA_V5_MCS_12_ALLOWED = BIT(5), + REG_CAPA_V5_MCS_13_ALLOWED = BIT(6), + REG_CAPA_V5_11BE_DISABLED = BIT(8), + REG_CAPA_V5_11AX_DISABLED = BIT(13), + REG_CAPA_V5_320MHZ_ALLOWED = BIT(16), + REG_CAPA_V5_11BN_DISABLED = BIT(17), +}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4, 5 */ /* * API v2 for reg_capa_flags is relevant from version 6 and onwards of the @@ -546,7 +547,7 @@ static const u8 iwl_vendor_caps[] = { 0x00 }; -static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { +static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = { { .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT), @@ -690,6 +691,17 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { */ .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } }, + .uhr_cap = { + .has_uhr = true, + /* Note: asymmetry is fixed later */ + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP | + IEEE80211_UHR_MAC_CAP0_DPS_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DUO_SUPP, + }, + }, }, { .types_mask = BIT(NL80211_IFTYPE_AP) | @@ -788,6 +800,12 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { */ .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } }, + .uhr_cap = { + .has_uhr = true, + /* Note: asymmetry is fixed later */ + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), + }, }, }; @@ -855,6 +873,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, fips_enabled) iftype_data->eht_cap.has_eht = false; + if (!data->sku_cap_11bn_enable || !iftype_data->eht_cap.has_eht) + iftype_data->uhr_cap.has_uhr = false; + /* Advertise an A-MPDU exponent extension based on * operating band */ @@ -1023,9 +1044,9 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, struct ieee80211_sband_iftype_data *iftype_data; int i; - BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa)); - BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa)); - BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa)); + BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_iftype_cap)); + BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_iftype_cap)); + BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_iftype_cap)); switch (sband->band) { case NL80211_BAND_2GHZ: @@ -1042,10 +1063,10 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, return; } - memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa)); + memcpy(iftype_data, iwl_iftype_cap, sizeof(iwl_iftype_cap)); _ieee80211_set_sband_iftype_data(sband, iftype_data, - ARRAY_SIZE(iwl_he_eht_capa)); + ARRAY_SIZE(iwl_iftype_cap)); for (i = 0; i < sband->n_iftype_data; i++) iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i], @@ -1054,6 +1075,46 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains); } +static void +iwl_init_nan_phy_capa(const struct iwl_fw *fw, struct iwl_nvm_data *data) +{ + const struct ieee80211_sta_he_cap *he_cap; + + if (!fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT)) + return; + + data->nan_phy_capa.ht = data->bands[NL80211_BAND_2GHZ].ht_cap; + data->nan_phy_capa.vht = data->bands[NL80211_BAND_5GHZ].vht_cap; + + he_cap = ieee80211_get_he_iftype_cap(&data->bands[NL80211_BAND_2GHZ], + NL80211_IFTYPE_STATION); + if (he_cap) { + data->nan_phy_capa.he = *he_cap; + data->nan_phy_capa.he.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; + } + + /* + * FIXME: we copied HE capabilities from the 2.4 GHz band, + * but there are bits that are band-dependent: + * + * IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 - 2.4 GHz - set + * IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3 - 5 GHz - not set + * IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G - set + * IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G - set + * IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G - set + * + * We copied from STA iftype - so we have the following bits set: + * IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS + * IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS + * IEEE80211_HE_PHY_CAP7_MAX_NC_1 + * IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO + * + * Need to check which one should actually be set for NAN. + */ +} + void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data, u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw) { @@ -1082,6 +1143,8 @@ void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data, if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, fw); + + iwl_init_nan_phy_capa(fw, data); } IWL_EXPORT_SYMBOL(iwl_reinit_cab); @@ -1611,6 +1674,9 @@ u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, if (reg_capa.disable_11be) flags |= NL80211_RRF_NO_EHT; + if (reg_capa.disable_11bn) + flags |= NL80211_RRF_NO_UHR; + return flags; } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_nvm_get_regdom_bw_flags); @@ -1621,11 +1687,13 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver) if (resp_ver >= REG_CAPA_V4_RESP_VER) { reg_capa.allow_40mhz = true; - reg_capa.allow_80mhz = flags & REG_CAPA_V4_80MHZ_ALLOWED; - reg_capa.allow_160mhz = flags & REG_CAPA_V4_160MHZ_ALLOWED; - reg_capa.allow_320mhz = flags & REG_CAPA_V4_320MHZ_ALLOWED; - reg_capa.disable_11ax = flags & REG_CAPA_V4_11AX_DISABLED; - reg_capa.disable_11be = flags & REG_CAPA_V4_11BE_DISABLED; + reg_capa.allow_80mhz = flags & REG_CAPA_V5_80MHZ_ALLOWED; + reg_capa.allow_160mhz = flags & REG_CAPA_V5_160MHZ_ALLOWED; + reg_capa.allow_320mhz = flags & REG_CAPA_V5_320MHZ_ALLOWED; + reg_capa.disable_11ax = flags & REG_CAPA_V5_11AX_DISABLED; + reg_capa.disable_11be = flags & REG_CAPA_V5_11BE_DISABLED; + /* can check: was reserved and irrelevant for pre-UHR devices */ + reg_capa.disable_11bn = flags & REG_CAPA_V5_11BN_DISABLED; } else if (resp_ver >= REG_CAPA_V2_RESP_VER) { reg_capa.allow_40mhz = flags & REG_CAPA_V2_40MHZ_ALLOWED; reg_capa.allow_80mhz = flags & REG_CAPA_V2_80MHZ_ALLOWED; @@ -1683,8 +1751,16 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans, IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", num_of_ch); - /* build a regdomain rule for every valid channel */ - regd = kzalloc_flex(*regd, reg_rules, num_of_ch); + /* build a regdomain rule for every valid channel. + * Certain firmware versions might report no valid channels + * if booted in RF-kill, i.e. not all calibrations etc. are + * running. We'll get out of this situation later when the + * rfkill is removed and we update the regdomain again, but + * since cfg80211 doesn't accept an empty regdomain, we need + * to allocate space for at least one rule to add a dummy + * (unusable) rule in this case so we can init. + */ + regd = kzalloc_flex(*regd, reg_rules, num_of_ch ?: 1); if (!regd) return ERR_PTR(-ENOMEM); @@ -1758,14 +1834,7 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans, reg_query_regdb_wmm(regd->alpha2, center_freq, rule); } - /* - * Certain firmware versions might report no valid channels - * if booted in RF-kill, i.e. not all calibrations etc. are - * running. We'll get out of this situation later when the - * rfkill is removed and we update the regdomain again, but - * since cfg80211 doesn't accept an empty regdomain, add a - * dummy (unusable) rule here in this case so we can init. - */ + /* If no valid rules were found, add a dummy rule */ if (!valid_rules) { valid_rules = 1; rule = ®d->reg_rules[valid_rules - 1]; @@ -2079,6 +2148,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); if (trans->cfg->eht_supported) nvm->sku_cap_11be_enable = true; + nvm->sku_cap_11bn_enable = trans->cfg->uhr_supported; /* Initialize PHY sku data */ nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); @@ -2106,6 +2176,8 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, iwl_init_sbands(trans, nvm, channel_profile, tx_ant, rx_ant, sbands_flags, v4, fw); + iwl_init_nan_phy_capa(fw, nvm); + iwl_free_resp(&hcmd); return nvm; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 12f28bb0e859..e676d7c2d6cc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -35,6 +35,7 @@ enum iwl_nvm_sbands_flags { * for this regulatory domain (valid only in 6 Ghz). * @disable_11ax: 11ax is forbidden for this regulatory domain. * @disable_11be: 11be is forbidden for this regulatory domain. + * @disable_11bn: UHR/11bn is not allowed for this regulatory domain */ struct iwl_reg_capa { bool allow_40mhz; @@ -43,6 +44,7 @@ struct iwl_reg_capa { bool allow_320mhz; bool disable_11ax; bool disable_11be; + bool disable_11bn; }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h index ac0a29a1c31f..52d35b73ed74 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018, 2020-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018, 2020-2023, 2026 Intel Corporation * Copyright (C) 2015 Intel Mobile Communications GmbH */ #ifndef __iwl_eeprom_parse_h__ @@ -32,6 +32,7 @@ struct iwl_nvm_data { bool sku_cap_ipan_enable; bool sku_cap_mimo_disabled; bool sku_cap_11be_enable; + bool sku_cap_11bn_enable; u16 radio_cfg_type; u8 radio_cfg_step; @@ -55,6 +56,12 @@ struct iwl_nvm_data { struct ieee80211_sband_iftype_data uhb[2]; } iftd; + struct { + struct ieee80211_sta_ht_cap ht; + struct ieee80211_sta_vht_cap vht; + struct ieee80211_sta_he_cap he; + } nan_phy_capa; + struct ieee80211_channel channels[]; }; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index a7214ddcfaf5..50acfe0d4f79 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-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -385,7 +385,8 @@ enum { #define CNVI_SCU_REG_FOR_ECO_1_WIAMT_KNOWN BIT(4) #define CNVI_SCU_REG_FOR_ECO_1_WIAMT_PRESENT BIT(5) -#define CNVI_PMU_STEP_FLOW 0xA2D588 +#define CNVI_PMU_STEP_FLOW_BZ 0xA2D588 +#define CNVI_PMU_STEP_FLOW_SC 0xA2D688 #define CNVI_PMU_STEP_FLOW_FORCE_URM BIT(2) #define PREG_AUX_BUS_WPROT_0 0xA04CC0 @@ -411,6 +412,11 @@ enum { #define HPM_SECONDARY_DEVICE_STATE 0xa03404 #define WFPM_MAC_OTP_CFG7_ADDR 0xa03338 #define WFPM_MAC_OTP_CFG7_DATA 0xa0333c +#define WFPM_RSRCS_4PHS_REQ_STTS 0xa033f8 +#define WFPM_RSRCS_4PHS_ACK_STTS 0xa033fc + +#define RSRC_REQ_CNVR_TOP BIT(6) +#define RSRC_ACK_CNVR_TOP BIT(6) /* For UMAG_GEN_HW_STATUS reg check */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 16b2c313e72b..73aae1125042 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -2,16 +2,14 @@ /* * Copyright (C) 2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2019-2021, 2023-2025 Intel Corporation + * Copyright (C) 2019-2021, 2023-2026 Intel Corporation */ #include #include #include -#include "fw/api/tx.h" #include "iwl-trans.h" #include "iwl-drv.h" -#include "iwl-fh.h" #include #include "fw/api/commands.h" #include "pcie/gen1_2/internal.h" @@ -461,6 +459,12 @@ int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, } IWL_EXPORT_SYMBOL(iwl_trans_read_mem); +int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords) +{ + return iwl_trans_pcie_read_mem_no_grab(trans, addr, buf, dwords); +} + int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, const void *buf, int dwords) { @@ -554,6 +558,11 @@ bool iwl_trans_grab_nic_access(struct iwl_trans *trans) } IWL_EXPORT_SYMBOL(iwl_trans_grab_nic_access); +void iwl_trans_resched_with_nic_access(struct iwl_trans *trans) +{ + iwl_trans_pcie_resched_with_nic_access(trans); +} + void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans) { @@ -822,3 +831,10 @@ bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans) return iwl_pcie_gen1_2_is_ltr_enabled(trans); } IWL_EXPORT_SYMBOL(iwl_trans_is_ltr_enabled); + +int iwl_trans_activate_nic(struct iwl_trans *trans) +{ + return iwl_pcie_gen1_2_activate_nic(trans); +} +IWL_EXPORT_SYMBOL(iwl_trans_activate_nic); + diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 61e4f4776dcb..c581cc8f5bcb 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-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -161,6 +161,10 @@ struct iwl_device_tx_cmd { #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) +/* Maximum payload size for a non-NOCOPY host command (excluding the header) */ +#define IWL_MAX_CMD_PAYLOAD_SIZE \ + (TFD_MAX_PAYLOAD_SIZE - sizeof(struct iwl_cmd_header_wide)) + /* * number of transfer buffers (fragments) per transmit frame descriptor; * this is just the driver's idea, the hardware supports 20 @@ -700,106 +704,6 @@ struct iwl_cmd_meta { u32 tbs; }; -/* - * The FH will write back to the first TB only, so we need to copy some data - * into the buffer regardless of whether it should be mapped or not. - * This indicates how big the first TB must be to include the scratch buffer - * and the assigned PN. - * Since PN location is 8 bytes at offset 12, it's 20 now. - * If we make it bigger then allocations will be bigger and copy slower, so - * that's probably not useful. - */ -#define IWL_FIRST_TB_SIZE 20 -#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64) - -struct iwl_pcie_txq_entry { - void *cmd; - struct sk_buff *skb; - /* buffer to free after command completes */ - const void *free_buf; - struct iwl_cmd_meta meta; -}; - -struct iwl_pcie_first_tb_buf { - u8 buf[IWL_FIRST_TB_SIZE_ALIGN]; -}; - -/** - * struct iwl_txq - Tx Queue for DMA - * @tfds: transmit frame descriptors (DMA memory) - * @first_tb_bufs: start of command headers, including scratch buffers, for - * the writeback -- this is DMA memory and an array holding one buffer - * for each command on the queue - * @first_tb_dma: DMA address for the first_tb_bufs start - * @entries: transmit entries (driver state) - * @lock: queue lock - * @reclaim_lock: reclaim lock - * @stuck_timer: timer that fires if queue gets stuck - * @trans: pointer back to transport (for timer) - * @need_update: indicates need to update read/write index - * @ampdu: true if this queue is an ampdu queue for an specific RA/TID - * @wd_timeout: queue watchdog timeout (jiffies) - per queue - * @frozen: tx stuck queue timer is frozen - * @frozen_expiry_remainder: remember how long until the timer fires - * @block: queue is blocked - * @bc_tbl: byte count table of the queue (relevant only for gen2 transport) - * @write_ptr: 1-st empty entry (index) host_w - * @read_ptr: last used entry (index) host_r - * @dma_addr: physical addr for BD's - * @n_window: safe queue window - * @id: queue id - * @low_mark: low watermark, resume queue if free space more than this - * @high_mark: high watermark, stop queue if free space less than this - * @overflow_q: overflow queue for handling frames that didn't fit on HW queue - * @overflow_tx: need to transmit from overflow - * - * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame - * descriptors) and required locking structures. - * - * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware - * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless - * there might be HW changes in the future). For the normal TX - * queues, n_window, which is the size of the software queue data - * is also 256; however, for the command queue, n_window is only - * 32 since we don't need so many commands pending. Since the HW - * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. - * This means that we end up with the following: - * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | - * SW entries: | 0 | ... | 31 | - * where N is a number between 0 and 7. This means that the SW - * data is a window overlayed over the HW queue. - */ -struct iwl_txq { - void *tfds; - struct iwl_pcie_first_tb_buf *first_tb_bufs; - dma_addr_t first_tb_dma; - struct iwl_pcie_txq_entry *entries; - /* lock for syncing changes on the queue */ - spinlock_t lock; - /* lock to prevent concurrent reclaim */ - spinlock_t reclaim_lock; - unsigned long frozen_expiry_remainder; - struct timer_list stuck_timer; - struct iwl_trans *trans; - bool need_update; - bool frozen; - bool ampdu; - int block; - unsigned long wd_timeout; - struct sk_buff_head overflow_q; - struct iwl_dma_ptr bc_tbl; - - int write_ptr; - int read_ptr; - dma_addr_t dma_addr; - int n_window; - u32 id; - int low_mark; - int high_mark; - - bool overflow_tx; -}; - /** * struct iwl_trans_info - transport info for outside use * @name: the device name @@ -1019,6 +923,14 @@ void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, void *buf, int dwords); +/* + * Note the special calling convention - it's allowed to drop the + * internal transport lock and re-enable BHs temporarily, but will + * not release NIC access. + */ +int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords); + int iwl_trans_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); @@ -1034,6 +946,14 @@ void iwl_trans_debugfs_cleanup(struct iwl_trans *trans); (bufsize) / sizeof(u32)); \ }) +static inline int +iwl_trans_read_mem_bytes_no_grab(struct iwl_trans *trans, + u32 addr, void *buf, u32 bufsize) +{ + return iwl_trans_read_mem_no_grab(trans, addr, buf, + bufsize / sizeof(u32)); +} + int iwl_trans_write_imr_mem(struct iwl_trans *trans, u32 dst_addr, u64 src_addr, u32 byte_cnt); @@ -1065,6 +985,17 @@ void iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, bool iwl_trans_grab_nic_access(struct iwl_trans *trans); +/** + * iwl_trans_resched_with_nic_access - reschedule while holding NIC access + * @trans: the transport pointer + * + * This can be called while holding NIC access, during periods where + * the lock itself isn't interesting, the NIC should remain active, + * but a lot of processing is happening and the CPU may need to be + * released. In practice, this is only during FW dump. + */ +void iwl_trans_resched_with_nic_access(struct iwl_trans *trans); + void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans); @@ -1153,6 +1084,8 @@ static inline bool iwl_trans_dbg_ini_valid(struct iwl_trans *trans) void iwl_trans_interrupts(struct iwl_trans *trans, bool enable); +int iwl_trans_activate_nic(struct iwl_trans *trans); + static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans) { clear_bit(STATUS_IN_SW_RESET, &trans->status); @@ -1236,9 +1169,6 @@ enum iwl_reset_mode { void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode); void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans); -int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, - struct iwl_host_cmd *cmd); - /* Internal helper */ static inline void iwl_trans_set_info(struct iwl_trans *trans, struct iwl_trans_info *info) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index 3bf36f8f6874..e3627ad0321c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -64,6 +64,9 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld, } /* pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto out_unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) @@ -166,6 +169,9 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue, goto out_unlock; /* pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto out_unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) @@ -347,6 +353,9 @@ static void iwl_mld_rx_agg_session_expired(struct timer_list *t) } /* timer expired, pick any STA ID to find the pointer */ + if (WARN_ON_ONCE(!ba_data->sta_mask)) + goto unlock; + sta_id = ffs(ba_data->sta_mask) - 1; link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c index 6598d9333333..37f4de8b0281 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024, 2026 Intel Corporation */ #include @@ -238,6 +238,58 @@ int iwl_mld_store_ap_early_key(struct iwl_mld *mld, return -ENOSPC; } +static void iwl_mld_stop_beacon(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct iwl_mac_beacon_cmd cmd = {}; + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, BEACON_TEMPLATE_CMD, 14); + + if (WARN_ON(!mld_link)) + return; + + if (cmd_ver < 15) + return; + + /* leave byte_cnt 0 */ + cmd.link_id = cpu_to_le32(mld_link->fw_id); + + iwl_mld_send_cmd_pdu(mld, BEACON_TEMPLATE_CMD, &cmd); +} + +void +iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + u64 changes) +{ + u32 link_changes = 0; + + if (changes & BSS_CHANGED_ERP_SLOT) + link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; + + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT)) + link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS; + + if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH)) + link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; + + if (changes & BSS_CHANGED_HE_BSS_COLOR) + link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; + + if (link_changes) + iwl_mld_change_link_in_fw(mld, link, link_changes); + + if (changes & BSS_CHANGED_BEACON) { + WARN_ON(!link->enable_beacon); + iwl_mld_update_beacon_template(mld, vif, link); + } + + /* Enabling beacons was already covered above */ + if ((changes & BSS_CHANGED_BEACON_ENABLED) && !link->enable_beacon) + iwl_mld_stop_beacon(mld, vif, link); +} + static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link) @@ -272,10 +324,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx; int ret; - ret = iwl_mld_update_beacon_template(mld, vif, link); - if (ret) - return ret; - /* the link should be already activated when assigning chan context, * and LINK_CONTEXT_MODIFY_EHT_PARAMS is deprecated */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.h b/drivers/net/wireless/intel/iwlwifi/mld/ap.h index 4a6f52b9552d..f10e9c9a38ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024, 2026 Intel Corporation */ #ifndef __iwl_ap_h__ #define __iwl_ap_h__ @@ -14,6 +14,12 @@ int iwl_mld_update_beacon_template(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); +void +iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + u64 changes); + int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index 3a595a1c2e00..458a668ba916 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mld.h" @@ -43,6 +43,12 @@ struct iwl_mld_resume_key_iter_data { struct iwl_mld_wowlan_status *wowlan_status; }; +struct iwl_mld_rsc_resume_iter_data { + struct iwl_mld *mld; + const struct iwl_wowlan_all_rsc_tsc_v5 *notif; + int queue; +}; + struct iwl_mld_suspend_key_iter_data { struct iwl_wowlan_rsc_tsc_params_cmd *rsc; bool have_rsc; @@ -282,7 +288,8 @@ static void iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld, struct iwl_mld_wowlan_status *wowlan_status, const struct iwl_wowlan_gtk_status *gtk_data, - const struct iwl_wowlan_all_rsc_tsc_v5 *sc) + const struct iwl_wowlan_all_rsc_tsc_v5 *sc, + int rsc_notif_ver) { int status_idx = 0; @@ -305,14 +312,18 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld, wowlan_status->gtk[status_idx].id = wowlan_status->gtk[status_idx].flags & IWL_WOWLAN_GTK_IDX_MASK; - /* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc - * The gtk ids can be any two numbers between 0 and 3, - * the id_map maps between the key id and the index in sc->mcast - */ - rsc_idx = - sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id]; - iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx], - sc, rsc_idx); + /* If RSC_NOTIF is not supported */ + if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN) { + /* The rsc for both gtk keys are stored in + * gtk[0]->sc->mcast_rsc. The gtk ids can be any two + * numbers between 0 and 3, the id_map maps between the + * key id and the index in sc->mcast + */ + rsc_idx = + sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id]; + iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx], + sc, rsc_idx); + } if (key_status == IWL_WOWLAN_STATUS_NEW_KEY) { memcpy(wowlan_status->gtk[status_idx].key, @@ -598,6 +609,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION, IWL_FW_CMD_VER_UNKNOWN); + int rsc_notif_ver = iwl_fw_lookup_notif_ver(mld->fw, + DATA_PATH_GROUP, + RSC_NOTIF, + IWL_FW_CMD_VER_UNKNOWN); if (wowlan_info_ver == 5) { /* v5 format - validate before conversion */ @@ -642,8 +657,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, return true; iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk, - ¬if->gtk[0].sc); - iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc); + ¬if->gtk[0].sc, rsc_notif_ver); + if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN) + iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, + ¬if->gtk[0].sc); /* only one igtk is passed by FW */ iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]); iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk); @@ -902,8 +919,14 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, struct iwl_mld_resume_key_iter_data *data = _data; struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status; u8 status_idx; + int rsc_notif_ver = iwl_fw_lookup_notif_ver(data->mld->fw, + DATA_PATH_GROUP, + RSC_NOTIF, + IWL_FW_CMD_VER_UNKNOWN); - if (key->keyidx >= 0 && key->keyidx <= 3) { + /* If RSC_NOTIF is not supported */ + if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN && + key->keyidx >= 0 && key->keyidx <= 3) { /* PTK */ if (sta) { iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status, @@ -932,6 +955,105 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, } } +static void +iwl_mld_rsc_update_key_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld_rsc_resume_iter_data *data = _data; + struct ieee80211_key_seq seq; + + if (key->keyidx > 3) + return; + + if (sta) { + /* PTK */ + BUILD_BUG_ON(ARRAY_SIZE(data->notif->ucast_rsc) != + IWL_MAX_TID_COUNT); + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { + /* TKIP: just update key sequences */ + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + iwl_mld_le64_to_tkip_seq(data->notif->ucast_rsc[tid], + &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } + } else { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_ptk_pn *mld_ptk_pn = + rcu_dereference_wiphy(data->mld->wiphy, + mld_sta->ptk_pn[key->keyidx]); + + if (WARN_ON(!mld_ptk_pn)) + return; + + if (WARN_ON(data->queue >= + data->mld->trans->info.num_rxqs)) + return; + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + iwl_mld_le64_to_aes_seq(data->notif->ucast_rsc[tid], + &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + memcpy(mld_ptk_pn->q[data->queue].pn[tid], + seq.ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } + } + + IWL_DEBUG_WOWLAN(data->mld, + "Updated PTK RSC for key %d on queue %d\n", + key->keyidx, data->queue); + } else { + /* GTK */ + int rsc_idx = data->notif->mcast_key_id_map[key->keyidx]; + + if (rsc_idx == IWL_MCAST_KEY_MAP_INVALID) + return; + + if (IWL_FW_CHECK(data->mld, + rsc_idx >= ARRAY_SIZE(data->notif->mcast_rsc), + "Invalid mcast key mapping: %d for key %d\n", + rsc_idx, key->keyidx)) + return; + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + __le64 rsc = + data->notif->mcast_rsc[rsc_idx][tid]; + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + iwl_mld_le64_to_tkip_seq(rsc, &seq); + else + iwl_mld_le64_to_aes_seq(rsc, &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } + + IWL_DEBUG_WOWLAN(data->mld, + "Updated GTK %d RSC (rsc_idx %d) on queue %d\n", + key->keyidx, rsc_idx, data->queue); + } +} + +void +iwl_mld_process_rsc_notification(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_wowlan_all_rsc_tsc_v5 *notif, + int queue) +{ + struct iwl_mld_rsc_resume_iter_data iter_data = { + .mld = mld, + .notif = notif, + .queue = queue, + }; + + /* Iterate through all active keys and update RSC */ + ieee80211_iter_keys_rcu(mld->hw, vif, + iwl_mld_rsc_update_key_iter, + &iter_data); +} + static void iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, struct iwl_mld *mld, @@ -951,19 +1073,25 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, iwl_mld_update_mcast_rx_seq(key_config, key_data); - /* The FW holds only one igtk so we keep track of the valid one */ + /* The FW holds only one IGTK so we keep track of the valid one */ if (key_config->keyidx == 4 || key_config->keyidx == 5) { - struct iwl_mld_link *mld_link = - iwl_mld_link_from_mac80211(link_conf); + struct iwl_mld_link_sta *mld_ap_link_sta; + + mld_ap_link_sta = iwl_mld_get_ap_link_sta(vif, + link_conf->link_id); + if (WARN_ON(!mld_ap_link_sta)) + return; /* If we had more than one rekey, mac80211 will tell us to * remove the old and add the new so we will update the IGTK in * drv_set_key */ - if (mld_link->igtk && mld_link->igtk != key_config) { + if (mld_ap_link_sta->rx_igtk && + mld_ap_link_sta->rx_igtk != key_config) { /* mark the old IGTK as not in FW */ - mld_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID; - mld_link->igtk = key_config; + mld_ap_link_sta->rx_igtk->hw_key_idx = + STA_KEY_IDX_INVALID; + mld_ap_link_sta->rx_igtk = key_config; } } @@ -1938,8 +2066,11 @@ int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan) if (!bss_vif->cfg.assoc) { int ret; - /* If we're not associated, this must be netdetect */ - if (WARN_ON(!wowlan->nd_config)) + /* + * If not associated we can only do netdetect, if + * that's not enabled then just suspend normally. + */ + if (!wowlan->nd_config) return 1; ret = iwl_mld_netdetect_config(mld, bss_vif, wowlan); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.h b/drivers/net/wireless/intel/iwlwifi/mld/d3.h index 618d6fb3c796..c2e8ba877042 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024, 2026 Intel Corporation */ #ifndef __iwl_mld_d3_h__ #define __iwl_mld_d3_h__ @@ -42,6 +42,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld); void iwl_mld_set_rekey_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *data); +void iwl_mld_process_rsc_notification(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_wowlan_all_rsc_tsc_v5 *notif, + int queue); #if IS_ENABLED(CONFIG_IPV6) void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c index b05b58eb1281..351a4f177e92 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mld.h" @@ -979,15 +979,39 @@ void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200); VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200); } -#define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + +#define LINK_DEBUGFS_WIPHY_WRITE_FILE_OPS(name, bufsz) \ WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf) +/* + * Note: no locking is provided, so the function must have its own, + * but it cannot acquire the wiphy mutex. + */ +#define LINK_DEBUGFS_READ_FILE_OPS(name, bufsz) \ + _MLD_DEBUGFS_READ_FILE_OPS(link_##name, bufsz, struct ieee80211_bss_conf) + #define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ debugfs_create_file(alias, mode, parent, link_conf, \ &iwl_dbgfs_link_##name##_ops) #define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \ LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) +static ssize_t iwl_dbgfs_link_fw_id_read(struct ieee80211_bss_conf *link_conf, + size_t buflen, void *buf) +{ + struct iwl_mld_link *mld_link; + + guard(rcu)(); + + mld_link = iwl_mld_link_from_mac80211(link_conf); + if (!mld_link) + return -EINVAL; + + return scnprintf(buf, buflen, "%d\n", mld_link->fw_id); +} + +LINK_DEBUGFS_READ_FILE_OPS(fw_id, 64); + void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, @@ -1008,6 +1032,8 @@ void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, /* Release the reference from debugfs_lookup */ dput(mld_link_dir); } + + LINK_DEBUGFS_ADD_FILE(fw_id, mld_link_dir, 0400); } static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, @@ -1035,7 +1061,6 @@ static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, /* input is in FW format (v2 or v3) so convert to v3 */ rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3); - rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3)); ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : @@ -1050,20 +1075,22 @@ static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, return ret ? : count; } -static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, - size_t count, void *data) +static ssize_t +iwl_dbgfs_link_sta_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) { return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false); } -static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf, - size_t count, void *data) +static ssize_t +iwl_dbgfs_link_sta_fixed_rate_v3_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) { return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true); } -static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, - size_t count, void *data) +static ssize_t iwl_dbgfs_link_sta_tlc_dhc_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) { struct ieee80211_link_sta *link_sta = data; struct iwl_mld_link_sta *mld_link_sta; @@ -1090,18 +1117,42 @@ static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, return ret ? : count; } +static ssize_t +iwl_dbgfs_link_sta_fw_id_read(struct ieee80211_link_sta *link_sta, + size_t buflen, void *buf) +{ + struct iwl_mld_link_sta *mld_link_sta; + + guard(rcu)(); + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (!mld_link_sta) + return -EINVAL; + + return scnprintf(buf, buflen, "%u\n", mld_link_sta->fw_id); +} + #define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ debugfs_create_file(alias, mode, parent, link_sta, \ - &iwl_dbgfs_##name##_ops) + &iwl_dbgfs_link_sta_##name##_ops) #define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \ LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) #define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \ - WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta) + WIPHY_DEBUGFS_WRITE_FILE_OPS(link_sta_##name, bufsz, link_sta) + +/* + * Note: no locking is provided, so the function must have its own, + * but it cannot acquire the wiphy mutex. + */ +#define LINK_STA_DEBUGFS_READ_OPS(name, bufsz) \ + _MLD_DEBUGFS_READ_FILE_OPS(link_sta_##name, bufsz, \ + struct ieee80211_link_sta) LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64); +LINK_STA_DEBUGFS_READ_OPS(fw_id, 64); void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1111,4 +1162,5 @@ void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200); LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); + LINK_STA_DEBUGFS_ADD_FILE(fw_id, dir, 0400); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c index 3464b3268712..81df3fdfcbf5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c @@ -71,24 +71,34 @@ iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld, switch (peer->chandef.width) { case NL80211_CHAN_WIDTH_20_NOHT: - target->format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; - target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY, + IWL_LOCATION_FMT_BW_FORMAT); + target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_20: - target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT; - target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FMT_BW_FORMAT); + target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_40: - target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT; - target->format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; + target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FMT_BW_FORMAT); + target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_80: - target->format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; - target->format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; + target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT, + IWL_LOCATION_FMT_BW_FORMAT); + target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_160: - target->format_bw = IWL_LOCATION_FRAME_FORMAT_HE; - target->format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS; + target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE, + IWL_LOCATION_FMT_BW_FORMAT); + target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; default: IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n", diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 52b2ae496811..2b837c6fa5fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include "iface.h" +#include "nan.h" #include "hcmd.h" #include "key.h" #include "mlo.h" @@ -55,6 +56,24 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL); + if (vif->type == NL80211_IFTYPE_NAN) { + mld_vif->nan.mac_added = false; + /* Clean up NAN links */ + for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++) + iwl_mld_cleanup_nan_link(&mld_vif->nan.links[i]); + + if (mld_vif->nan.bcast_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &mld_vif->nan.bcast_sta); + if (mld_vif->nan.mgmt_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &mld_vif->nan.mgmt_sta); + + mld_vif->nan.tx_igtk = NULL; + } + + if (vif->type == NL80211_IFTYPE_NAN_DATA && + mld_vif->nan.mcast_data_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &mld_vif->nan.mcast_data_sta); + CLEANUP_STRUCT(mld_vif); } @@ -94,6 +113,8 @@ static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif) return FW_MAC_TYPE_P2P_DEVICE; case NL80211_IFTYPE_ADHOC: return FW_MAC_TYPE_IBSS; + case NL80211_IFTYPE_NAN: + return FW_MAC_TYPE_NAN; default: WARN_ON_ONCE(1); } @@ -257,7 +278,7 @@ static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, IEEE80211_EML_CAP_TRANSITION_TIMEOUT); cmd->client.esr_transition_timeout = - min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU, + min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU, esr_transition_timeout); cmd->client.medium_sync_delay = cpu_to_le16(vif->cfg.eml_med_sync_delay); @@ -362,6 +383,42 @@ static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld, MAC_CFG_FILTER_ACCEPT_GRP); } +static int iwl_mld_fill_mac_cmd_nan(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_vif *ndi_being_added, + struct iwl_mac_config_cmd *cmd) +{ + struct ieee80211_vif *iter; + u32 idx = 0; + + cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); + + /* + * A NAN_DATA vif might be in the process of being added - it won't + * be found by the iteration below since it's not yet active/in-driver. + * In hw restart, the iteration below will find the ndi_being_added. + */ + if (ndi_being_added && !mld->fw_status.in_hw_restart) { + memcpy(cmd->nan.ndi_addrs[idx].addr, ndi_being_added->addr, ETH_ALEN); + idx++; + } + + for_each_active_interface(iter, mld->hw) { + if (iter->type != NL80211_IFTYPE_NAN_DATA) + continue; + + if (WARN_ON_ONCE(idx >= ARRAY_SIZE(cmd->nan.ndi_addrs))) + return -EINVAL; + + memcpy(cmd->nan.ndi_addrs[idx].addr, iter->addr, ETH_ALEN); + idx++; + } + + cmd->nan.ndi_addrs_count = cpu_to_le32(idx); + + return 0; +} + static int iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif) { @@ -374,16 +431,23 @@ iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif) return iwl_mld_send_mac_cmd(mld, &cmd); } -int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, - u32 action) +static int +__iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, + u32 action, struct ieee80211_vif *ndi_being_added) { struct iwl_mac_config_cmd cmd = {}; + int ret; lockdep_assert_wiphy(mld->wiphy); - /* NAN interface type is not known to FW */ - if (vif->type == NL80211_IFTYPE_NAN) - return 0; + /* NAN_DATA interface type is not known to FW */ + if (WARN_ON(vif->type == NL80211_IFTYPE_NAN_DATA)) + return -EINVAL; + + /* ndi_being_added is only relevant for NAN and when adding a NAN_DATA interface */ + if (WARN_ON(ndi_being_added && + (vif->type != NL80211_IFTYPE_NAN || action != FW_CTXT_ACTION_MODIFY))) + return -EINVAL; if (action == FW_CTXT_ACTION_REMOVE) return iwl_mld_rm_mac_from_fw(mld, vif); @@ -411,6 +475,11 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, case NL80211_IFTYPE_ADHOC: iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd); break; + case NL80211_IFTYPE_NAN: + ret = iwl_mld_fill_mac_cmd_nan(mld, vif, ndi_being_added, &cmd); + if (ret) + return ret; + break; default: WARN(1, "not supported yet\n"); return -EOPNOTSUPP; @@ -419,6 +488,12 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, return iwl_mld_send_mac_cmd(mld, &cmd); } +int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, + u32 action) +{ + return __iwl_mld_mac_fw_action(mld, vif, action, NULL); +} + static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy, struct wiphy_work *wk) { @@ -430,7 +505,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy, iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif)); } -static IWL_MLD_ALLOC_FN(vif, vif) +IWL_MLD_ALLOC_FN_STATIC(vif, vif) /* Constructor function for struct iwl_mld_vif */ static void @@ -456,9 +531,40 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk, iwl_mld_mlo_scan_start_wk); } + + if (vif->type == NL80211_IFTYPE_NAN) { + for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++) { + memset(&mld_vif->nan.links[i], 0, sizeof(mld_vif->nan.links[i])); + mld_vif->nan.links[i].fw_id = FW_CTXT_ID_INVALID; + } + + iwl_mld_init_internal_sta(&mld_vif->nan.bcast_sta); + iwl_mld_init_internal_sta(&mld_vif->nan.mgmt_sta); + } else if (vif->type == NL80211_IFTYPE_NAN_DATA) { + iwl_mld_init_internal_sta(&mld_vif->nan.mcast_data_sta); + } + iwl_mld_init_internal_sta(&mld_vif->aux_sta); } +static int iwl_mld_update_nan_mac(struct iwl_mld *mld, + struct ieee80211_vif *ndi_being_added) +{ + struct ieee80211_vif *vif = mld->nan_device_vif; + struct iwl_mld_vif *mld_vif; + + if (WARN_ON_ONCE(!vif)) + return -ENODEV; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (!iwl_mld_vif_fw_id_valid(mld_vif)) + return 0; + + return __iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY, + ndi_being_added); +} + int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); @@ -468,10 +574,14 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) iwl_mld_init_vif(mld, vif); - /* NAN interface type is not known to FW */ + /* NAN MACs are added to FW only when a schedule is set */ if (vif->type == NL80211_IFTYPE_NAN) return 0; + /* NAN_DATA interface type is not known to FW, but we need to update NAN MAC */ + if (vif->type == NL80211_IFTYPE_NAN_DATA) + return iwl_mld_update_nan_mac(mld, vif); + ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); if (ret) return ret; @@ -483,23 +593,52 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) return ret; } +int iwl_mld_add_nan_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(vif->type != NL80211_IFTYPE_NAN)) + return -EINVAL; + + ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); + if (ret) + return ret; + + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD); + if (ret) { + RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); + return ret; + } + + mld_vif->nan.mac_added = true; + + return 0; +} + void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); lockdep_assert_wiphy(mld->wiphy); - /* NAN interface type is not known to FW */ - if (vif->type == NL80211_IFTYPE_NAN) + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + iwl_mld_update_nan_mac(mld, NULL); + return; + } + + if (!iwl_mld_vif_fw_id_valid(mld_vif)) return; iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE); - if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif))) - return; - RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); + if (vif->type == NL80211_IFTYPE_NAN) + mld_vif->nan.mac_added = false; + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF, mld_vif->fw_id); } @@ -616,24 +755,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, kfree_rcu(old_data, rcu_head); } -void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt) -{ - struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; - struct ieee80211_vif *vif; - - if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif), - "mac id is invalid: %d\n", notif->mac_id)) - return; - - vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]); - - if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) - return; - - IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid); -} - void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { @@ -748,6 +869,6 @@ struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld) fw_id = __ffs(fw_id_bitmap); - return wiphy_dereference(mld->wiphy, - mld->fw_id_to_vif[fw_id]); + return rcu_dereference_wiphy(mld->wiphy, + mld->fw_id_to_vif[fw_id]); } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index 8dfc79fed253..bc6f45ff76f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -8,6 +8,7 @@ #include #include "link.h" +#include "nan.h" #include "session-protect.h" #include "d3.h" #include "fw/api/time-event.h" @@ -137,7 +138,6 @@ struct iwl_mld_emlsr { * @beacon_inject_active: indicates an active debugfs beacon ie injection * @low_latency_causes: bit flags, indicating the causes for low-latency, * see @iwl_mld_low_latency_cause. - * @ps_disabled: indicates that PS is disabled for this interface * @last_link_activation_time: last time a link was activated, for * deferring MLO scans (to make them more reliable) * @mld: pointer to the mld structure. @@ -152,6 +152,16 @@ struct iwl_mld_emlsr { * p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use. * @aux_sta: station used for remain on channel. Used in P2P device. * @mlo_scan_start_wk: worker to start a deferred MLO scan + * @nan: NAN parameters + * @nan.links: NAN links for FW (indexed by FW link ID) + * @nan.mac_added: track whether or not the MAC was added to FW + * @nan.bcast_sta: internal station used for NAN synchronization and discovery + * activities. No queue is associated with it. + * @nan.mgmt_sta: internal station used for NAN management frames, e.g., SDFs + * and NAFs. + * @nan.mcast_data_sta: internal station used for multicast NAN Data frames. + * @nan.tx_igtk: TX IGTK key for NAN, tracked separately since NAN does not + * use the vif links. */ struct iwl_mld_vif { /* Add here fields that need clean up on restart */ @@ -167,7 +177,6 @@ struct iwl_mld_vif { bool beacon_inject_active; #endif u8 low_latency_causes; - bool ps_disabled; time64_t last_link_activation_time; ); /* And here fields that survive a fw restart */ @@ -175,6 +184,16 @@ struct iwl_mld_vif { struct iwl_mld_link deflink; struct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct { + /* use only with wiphy protection */ + struct iwl_mld_nan_link links[IWL_FW_MAX_LINKS]; + bool mac_added; + struct iwl_mld_int_sta bcast_sta; + struct iwl_mld_int_sta mgmt_sta; + struct iwl_mld_int_sta mcast_data_sta; + struct ieee80211_key_conf *tx_igtk; + } nan; + struct iwl_mld_emlsr emlsr; #ifdef CONFIG_PM_SLEEP @@ -206,6 +225,20 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif) /* Call only for interfaces that were added to the driver! */ static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif) { + struct ieee80211_vif *vif = iwl_mld_vif_to_mac80211(mld_vif); + + switch (vif->type) { + case NL80211_IFTYPE_NAN_DATA: + return false; + case NL80211_IFTYPE_NAN: + if (!mld_vif->nan.mac_added) + return false; + break; + default: + break; + } + + /* Should be added to FW */ if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif))) return false; @@ -221,6 +254,12 @@ static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif) link_id++) \ if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id))) +#define for_each_mld_nan_valid_link(mld_vif, nan_link) \ + for (nan_link = &(mld_vif)->nan.links[0]; \ + nan_link < &(mld_vif)->nan.links[ARRAY_SIZE((mld_vif)->nan.links)]; \ + nan_link++) \ + if (nan_link->fw_id != FW_CTXT_ID_INVALID) + /* Retrieve pointer to mld link from mac80211 structures */ static inline struct iwl_mld_link * iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf) @@ -235,6 +274,7 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif); int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, u32 action); int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); +int iwl_mld_add_nan_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); void iwl_mld_set_vif_associated(struct iwl_mld *mld, struct ieee80211_vif *vif); @@ -245,9 +285,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); -void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, - struct iwl_rx_packet *pkt); - void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif); @@ -258,4 +295,19 @@ static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif) struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld); +static inline struct iwl_mld_link_sta * +iwl_mld_get_ap_link_sta(struct ieee80211_vif *vif, int link_id) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_sta *ap_sta = mld_vif->ap_sta; + struct iwl_mld_sta *mld_ap_sta; + + if (!ap_sta) + return NULL; + + mld_ap_sta = iwl_mld_sta_from_mac80211(ap_sta); + + return iwl_mld_link_sta_dereference_check(mld_ap_sta, link_id); +} + #endif /* __iwl_mld_iface_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c index 04192c5f07ff..bf80b4770b5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024, 2026 Intel Corporation */ #include "key.h" #include "iface.h" @@ -12,7 +12,6 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE; bool igtk = key->keyidx == 4 || key->keyidx == 5; u32 flags = 0; @@ -38,9 +37,6 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld, break; } - if (!sta && vif->type == NL80211_IFTYPE_STATION) - sta = mld_vif->ap_sta; - /* If we are installing an iGTK (in AP or STA mode), we need to tell * the firmware this key will en/decrypt MGMT frames. * Same goes if we are installing a pairwise key for an MFP station. @@ -53,6 +49,12 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld, if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU; + /* When a GTK is configured for a station, it can only be + * used for Rx and never for Tx. Thus, set the NO TX flag. + */ + if (!pairwise && sta) + flags |= IWL_SEC_KEY_FLAG_NO_TX; + return flags; } @@ -67,6 +69,40 @@ static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld, lockdep_assert_wiphy(mld->wiphy); + if (vif->type == NL80211_IFTYPE_NAN_DATA && !sta) { + /* Older firmware versions do not support transmission of + * multicast data frames. + */ + if (!iwl_mld_nan_use_nan_stations(mld)) + return 0; + + if (WARN_ON(mld_vif->nan.mcast_data_sta.sta_id == + IWL_INVALID_STA)) + return 0; + + return BIT(mld_vif->nan.mcast_data_sta.sta_id); + } + + if (vif->type == NL80211_IFTYPE_NAN && !sta) { + /* Older firmware versions do not support installation of + * IGTK/BIGTK keys. + */ + if (!iwl_mld_nan_use_nan_stations(mld)) + return 0; + + if (WARN_ON(mld_vif->nan.bcast_sta.sta_id == IWL_INVALID_STA || + mld_vif->nan.mgmt_sta.sta_id == IWL_INVALID_STA)) + return 0; + + if (key->keyidx >= 4 && key->keyidx <= 5) + return BIT(mld_vif->nan.mgmt_sta.sta_id); + + if (key->keyidx >= 6 && key->keyidx <= 7) + return BIT(mld_vif->nan.bcast_sta.sta_id); + + return 0; + } + /* AP group keys are per link and should be on the mcast/bcast STA */ if (vif->type == NL80211_IFTYPE_AP && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { @@ -92,11 +128,7 @@ static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld, return BIT(link->mcast_sta.sta_id); } - /* for client mode use the AP STA also for group keys */ - if (!sta && vif->type == NL80211_IFTYPE_STATION) - sta = mld_vif->ap_sta; - - /* STA should be non-NULL now */ + /* STA should be non-NULL */ if (WARN_ON(!sta)) return 0; @@ -178,34 +210,69 @@ static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask, iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd); } +static struct ieee80211_key_conf ** +iwl_mld_get_igtk_ptr(struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + /* key's link ID is set to -1 for non-MLO */ + int link_id = key->link_id < 0 ? 0 : key->link_id; + struct iwl_mld_link_sta *mld_ap_link_sta; + struct iwl_mld_link *mld_link; + struct iwl_mld_sta *mld_sta; + + if (key->keyidx != 4 && key->keyidx != 5) + return NULL; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (WARN_ON(!sta)) + return NULL; + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_ap_link_sta = iwl_mld_link_sta_dereference_check(mld_sta, + link_id); + if (WARN_ON(!mld_ap_link_sta)) + return NULL; + + return &mld_ap_link_sta->rx_igtk; + case NL80211_IFTYPE_NAN: + if (sta) { + mld_sta = iwl_mld_sta_from_mac80211(sta); + + return &mld_sta->deflink.rx_igtk; + } + + return &mld_vif->nan.tx_igtk; + case NL80211_IFTYPE_AP: + mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); + if (WARN_ON(!mld_link)) + return NULL; + + return &mld_link->tx_igtk; + default: + WARN_ONCE(1, "invalid iftype %d for IGTK\n", vif->type); + return NULL; + } +} + void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_key_conf **igtk_ptr; lockdep_assert_wiphy(mld->wiphy); if (!sta_mask) return; - if (key->keyidx == 4 || key->keyidx == 5) { - struct iwl_mld_link *mld_link; - unsigned int link_id = 0; - - /* set to -1 for non-MLO right now */ - if (key->link_id >= 0) - link_id = key->link_id; - - mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); - if (WARN_ON(!mld_link)) - return; - - if (mld_link->igtk == key) - mld_link->igtk = NULL; - + igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key); + if (igtk_ptr && *igtk_ptr == key) { + *igtk_ptr = NULL; mld->num_igtks--; } @@ -222,46 +289,43 @@ int iwl_mld_add_key(struct iwl_mld *mld, { u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_link *mld_link = NULL; - bool igtk = key->keyidx == 4 || key->keyidx == 5; + struct ieee80211_key_conf **igtk_ptr; int ret; lockdep_assert_wiphy(mld->wiphy); - if (!sta_mask) - return -EINVAL; + if (!sta_mask) { + /* for NAN (GTK) indicate SW-only, it's not used at all */ + if (vif->type == NL80211_IFTYPE_NAN_DATA && !sta && + !iwl_mld_nan_use_nan_stations(mld)) + return 1; - if (igtk) { - if (mld->num_igtks == IWL_MAX_NUM_IGTKS) + /* otherwise that's not valid */ + IWL_WARN(mld, "empty STA mask for key %d\n", key->keyidx); + return -EINVAL; + } + + igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key); + if (igtk_ptr) { + if (mld->num_igtks == mld->fw->ucode_capa.num_mcast_key_entries) return -EOPNOTSUPP; - u8 link_id = 0; - - /* set to -1 for non-MLO right now */ - if (key->link_id >= 0) - link_id = key->link_id; - - mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); - - if (WARN_ON(!mld_link)) - return -EINVAL; - - if (mld_link->igtk) { + if (*igtk_ptr) { IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n", - mld_link->igtk->keyidx); - iwl_mld_remove_key(mld, vif, sta, mld_link->igtk); + (*igtk_ptr)->keyidx); + iwl_mld_remove_key(mld, vif, sta, *igtk_ptr); } - - WARN_ON(mld_link->igtk); } ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key); - if (ret) + if (ret) { + IWL_WARN(mld, "failed to add key to FW (%d)\n", ret); return ret; + } - if (mld_link) { - mld_link->igtk = key; + if (igtk_ptr) { + WARN_ON(*igtk_ptr); + *igtk_ptr = key; mld->num_igtks++; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index be2cdf43c72e..96e06940b34c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -3,6 +3,8 @@ * Copyright (C) 2024-2026 Intel Corporation */ +#include + #include "constants.h" #include "link.h" #include "iface.h" @@ -16,14 +18,34 @@ #include "fw/api/context.h" #include "fw/dbg.h" -static int iwl_mld_send_link_cmd(struct iwl_mld *mld, - struct iwl_link_config_cmd *cmd, - enum iwl_ctxt_action action) +/** + * struct iwl_mld_link_chan_load_threshold - channel load thresholds + * @high_lim: level up transition thresholds, in percentage + * @low_lim: level down transition thresholds, in percentage + */ +struct iwl_mld_link_chan_load_threshold { + u8 high_lim; + u8 low_lim; +}; + +static const struct iwl_mld_link_chan_load_threshold +link_chan_load_thresh_tbl[] = { + [LINK_CHAN_LOAD_LVL1] = { .high_lim = 45, .low_lim = 40 }, + [LINK_CHAN_LOAD_LVL2] = { .high_lim = 70, .low_lim = 65 }, + [LINK_CHAN_LOAD_LVL3] = { .high_lim = 85, .low_lim = 80 }, +}; + +int iwl_mld_send_link_cmd(struct iwl_mld *mld, + struct iwl_link_config_cmd *cmd, + enum iwl_ctxt_action action) { int ret; lockdep_assert_wiphy(mld->wiphy); + if (WARN_ON_ONCE(cmd->link_id == cpu_to_le32(FW_CTXT_ID_INVALID))) + return -EINVAL; + cmd->action = cpu_to_le32(action); ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), @@ -315,10 +337,28 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link, link_sta_dereference_check(mld_vif->ap_sta, link->link_id); - if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he && + if (WARN_ON(!link_sta)) + return -EINVAL; + + if (link_sta->he_cap.has_he && link_sta->he_cap.he_cap_elem.mac_cap_info[5] & IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX) cmd.ul_mu_data_disable = 1; + + if (link_sta->uhr_cap.has_uhr && + link_sta->uhr_cap.mac.mac_cap[0] & + IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP) + flags |= LINK_FLG_DPS; + + if (link_sta->uhr_cap.has_uhr && + link_sta->uhr_cap.mac.mac_cap[1] & + IEEE80211_UHR_MAC_CAP1_DUO_SUPP) + flags |= LINK_FLG_DUO; + + if (link_sta->uhr_cap.has_uhr && + mld_vif->ap_sta->ext_mld_capa_ops & + IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM) + flags |= LINK_FLG_MLPM; } cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext; @@ -354,6 +394,21 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link, if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS)) changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + if (link->uhr_support && link->npca.enabled) { + flags |= LINK_FLG_NPCA; + if (link->npca.moplen) + cmd.npca_params.flags |= IWL_NPCA_FLAG_MAC_HDR_BASED; + cmd.npca_params.dis_subch_bmap = + cpu_to_le16(link->chanreq.oper.npca_punctured); + cmd.npca_params.initial_qsrc = link->npca.init_qsrc; + cmd.npca_params.min_dur_threshold = link->npca.min_dur_thresh; + /* spec/mac80211 have these in units of 4 usec */ + cmd.npca_params.switch_delay = + 4 * link->npca.switch_delay; + cmd.npca_params.switch_back_delay = + 4 * link->npca.switch_back_delay; + } + send_cmd: cmd.modify_mask = cpu_to_le32(changes); cmd.flags = cpu_to_le32(flags); @@ -437,7 +492,8 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); } -static IWL_MLD_ALLOC_FN(link, bss_conf) +IWL_MLD_ALLOC_FN(link, bss_conf) +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_link_fw_id); /* Constructor function for struct iwl_mld_link */ static int @@ -608,15 +664,20 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED * on current link and the link's bss_param_ch_count has changed on * the other link's beacon. + * + * When both links lose beacons, keep the primary (symmetric failure). + * When only the current link is sick, keep the other link. */ - if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS && - scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) || - missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH || - (bss_param_ch_cnt_link_id != link_id && - missed_bcon >= - IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) { + if (missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS && + scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) { iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON, iwl_mld_get_primary_link(vif)); + } else if (missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH || + (bss_param_ch_cnt_link_id != link_id && + missed_bcon >= + IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON, + iwl_mld_get_other_link(vif, link_id)); } } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif); @@ -651,35 +712,131 @@ struct iwl_mld_rssi_to_grade { u16 grade; }; -#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \ +#define RSSI_TO_GRADE_LINE_WITH_LB(_lb, _hb_uhb, _grade) \ { \ .rssi = {_lb, _hb_uhb}, \ .grade = _grade \ } -/* - * This array must be sorted by increasing RSSI for proper functionality. - * The grades are actually estimated throughput, represented as fixed-point - * with a scale factor of 1/10. - */ -static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = { - RSSI_TO_GRADE_LINE(-85, -89, 172), - RSSI_TO_GRADE_LINE(-83, -86, 344), - RSSI_TO_GRADE_LINE(-82, -85, 516), - RSSI_TO_GRADE_LINE(-80, -83, 688), - RSSI_TO_GRADE_LINE(-77, -79, 1032), - RSSI_TO_GRADE_LINE(-73, -76, 1376), - RSSI_TO_GRADE_LINE(-70, -74, 1548), - RSSI_TO_GRADE_LINE(-69, -72, 1720), - RSSI_TO_GRADE_LINE(-65, -68, 2064), - RSSI_TO_GRADE_LINE(-61, -66, 2294), - RSSI_TO_GRADE_LINE(-58, -61, 2580), - RSSI_TO_GRADE_LINE(-55, -58, 2868), - RSSI_TO_GRADE_LINE(-46, -55, 3098), - RSSI_TO_GRADE_LINE(-43, -54, 3442) +#define RSSI_TO_GRADE_LINE(_rssi, _grade) \ + RSSI_TO_GRADE_LINE_WITH_LB(_rssi, _rssi, _grade) + +/* Tables must be sorted by increasing RSSI */ + +/* 20 MHz Operational BW Grading Table */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_20mhz[] = { + RSSI_TO_GRADE_LINE_WITH_LB(-94, -95, 9), + RSSI_TO_GRADE_LINE_WITH_LB(-92, -93, 17), + RSSI_TO_GRADE_LINE_WITH_LB(-90, -90, 34), + RSSI_TO_GRADE_LINE_WITH_LB(-87, -87, 52), + RSSI_TO_GRADE_LINE_WITH_LB(-83, -84, 69), + RSSI_TO_GRADE_LINE_WITH_LB(-79, -80, 103), + RSSI_TO_GRADE_LINE_WITH_LB(-75, -75, 137), + RSSI_TO_GRADE_LINE_WITH_LB(-72, -73, 155), + RSSI_TO_GRADE_LINE_WITH_LB(-70, -71, 172), + RSSI_TO_GRADE_LINE_WITH_LB(-67, -68, 206), + RSSI_TO_GRADE_LINE_WITH_LB(-64, -65, 230), + RSSI_TO_GRADE_LINE_WITH_LB(-59, -60, 258), + RSSI_TO_GRADE_LINE_WITH_LB(-57, -58, 287), + RSSI_TO_GRADE_LINE_WITH_LB(-52, -53, 310), + RSSI_TO_GRADE_LINE_WITH_LB(-50, -50, 345), }; -#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade) +/* 40 MHz Operational BW Grading Table */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_40mhz[] = { + RSSI_TO_GRADE_LINE_WITH_LB(-95, -95, 9), + RSSI_TO_GRADE_LINE_WITH_LB(-93, -93, 17), + RSSI_TO_GRADE_LINE_WITH_LB(-91, -92, 18), + RSSI_TO_GRADE_LINE_WITH_LB(-89, -90, 34), + RSSI_TO_GRADE_LINE_WITH_LB(-87, -87, 68), + RSSI_TO_GRADE_LINE_WITH_LB(-84, -84, 104), + RSSI_TO_GRADE_LINE_WITH_LB(-80, -81, 138), + RSSI_TO_GRADE_LINE_WITH_LB(-77, -77, 206), + RSSI_TO_GRADE_LINE_WITH_LB(-72, -72, 274), + RSSI_TO_GRADE_LINE_WITH_LB(-69, -70, 310), + RSSI_TO_GRADE_LINE_WITH_LB(-67, -68, 344), + RSSI_TO_GRADE_LINE_WITH_LB(-64, -65, 412), + RSSI_TO_GRADE_LINE_WITH_LB(-61, -62, 460), + RSSI_TO_GRADE_LINE_WITH_LB(-56, -57, 516), + RSSI_TO_GRADE_LINE_WITH_LB(-54, -55, 574), + RSSI_TO_GRADE_LINE_WITH_LB(-49, -50, 620), + RSSI_TO_GRADE_LINE_WITH_LB(-46, -47, 690), +}; + +/* 80 MHz Operational BW Grading Table */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_80mhz[] = { + RSSI_TO_GRADE_LINE(-95, 9), + RSSI_TO_GRADE_LINE(-93, 17), + RSSI_TO_GRADE_LINE(-92, 18), + RSSI_TO_GRADE_LINE(-90, 34), + RSSI_TO_GRADE_LINE(-89, 36), + RSSI_TO_GRADE_LINE(-87, 68), + RSSI_TO_GRADE_LINE(-83, 136), + RSSI_TO_GRADE_LINE(-80, 208), + RSSI_TO_GRADE_LINE(-77, 276), + RSSI_TO_GRADE_LINE(-74, 412), + RSSI_TO_GRADE_LINE(-69, 548), + RSSI_TO_GRADE_LINE(-67, 620), + RSSI_TO_GRADE_LINE(-66, 688), + RSSI_TO_GRADE_LINE(-61, 824), + RSSI_TO_GRADE_LINE(-59, 920), + RSSI_TO_GRADE_LINE(-54, 1032), + RSSI_TO_GRADE_LINE(-52, 1148), + RSSI_TO_GRADE_LINE(-47, 1240), + RSSI_TO_GRADE_LINE(-44, 1380), +}; + +/* 160 MHz Operational BW Grading Table */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_160mhz[] = { + RSSI_TO_GRADE_LINE(-95, 9), + RSSI_TO_GRADE_LINE(-93, 17), + RSSI_TO_GRADE_LINE(-92, 18), + RSSI_TO_GRADE_LINE(-90, 34), + RSSI_TO_GRADE_LINE(-89, 36), + RSSI_TO_GRADE_LINE(-87, 68), + RSSI_TO_GRADE_LINE(-86, 72), + RSSI_TO_GRADE_LINE(-84, 136), + RSSI_TO_GRADE_LINE(-81, 272), + RSSI_TO_GRADE_LINE(-78, 416), + RSSI_TO_GRADE_LINE(-75, 552), + RSSI_TO_GRADE_LINE(-71, 824), + RSSI_TO_GRADE_LINE(-67, 1096), + RSSI_TO_GRADE_LINE(-65, 1240), + RSSI_TO_GRADE_LINE(-63, 1376), + RSSI_TO_GRADE_LINE(-59, 1648), + RSSI_TO_GRADE_LINE(-57, 1840), + RSSI_TO_GRADE_LINE(-52, 2064), + RSSI_TO_GRADE_LINE(-50, 2296), + RSSI_TO_GRADE_LINE(-46, 2480), + RSSI_TO_GRADE_LINE(-42, 2760), +}; + +/* 320 MHz Operational BW Grading Table */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_320mhz[] = { + RSSI_TO_GRADE_LINE(-95, 9), + RSSI_TO_GRADE_LINE(-93, 17), + RSSI_TO_GRADE_LINE(-92, 18), + RSSI_TO_GRADE_LINE(-90, 34), + RSSI_TO_GRADE_LINE(-89, 36), + RSSI_TO_GRADE_LINE(-87, 68), + RSSI_TO_GRADE_LINE(-86, 72), + RSSI_TO_GRADE_LINE(-84, 136), + RSSI_TO_GRADE_LINE(-83, 144), + RSSI_TO_GRADE_LINE(-81, 272), + RSSI_TO_GRADE_LINE(-78, 544), + RSSI_TO_GRADE_LINE(-75, 832), + RSSI_TO_GRADE_LINE(-72, 1104), + RSSI_TO_GRADE_LINE(-69, 1648), + RSSI_TO_GRADE_LINE(-64, 2192), + RSSI_TO_GRADE_LINE(-62, 2480), + RSSI_TO_GRADE_LINE(-61, 2752), + RSSI_TO_GRADE_LINE(-57, 3296), + RSSI_TO_GRADE_LINE(-55, 3680), + RSSI_TO_GRADE_LINE(-50, 4128), + RSSI_TO_GRADE_LINE(-47, 4592), + RSSI_TO_GRADE_LINE(-43, 4960), + RSSI_TO_GRADE_LINE(-40, 5520), +}; #define DEFAULT_CHAN_LOAD_2GHZ 30 #define DEFAULT_CHAN_LOAD_5GHZ 15 @@ -689,31 +846,27 @@ static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = { #define SCALE_FACTOR 256 #define MAX_CHAN_LOAD 256 -static unsigned int -iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf) +static void +iwl_mld_apply_puncturing_penalty(const struct ieee80211_bss_conf *link_conf, + unsigned int *grade, int bw_mhz) { - enum nl80211_chan_width chan_width = - link_conf->chanreq.oper.width; - int mhz = nl80211_chan_width_to_mhz(chan_width); - unsigned int n_subchannels; + unsigned int n_punctured, n_subchannels; - if (WARN_ONCE(mhz < 20 || mhz > 320, - "Invalid channel width : (%d)\n", mhz)) - return 1; + /* Puncturing only applicable for BW >= 80 MHz */ + if (bw_mhz < 80) + return; - /* total number of subchannels */ - n_subchannels = mhz / 20; + n_punctured = hweight16(link_conf->chanreq.oper.punctured); + if (n_punctured == 0) + return; - /* No puncturing if less than 80 MHz */ - if (mhz >= 80) - n_subchannels -= hweight16(link_conf->chanreq.oper.punctured); + n_subchannels = bw_mhz / 20; - return n_subchannels; + *grade = *grade * (n_subchannels - n_punctured) / n_subchannels; } -static int -iwl_mld_get_chan_load_from_element(struct iwl_mld *mld, - struct ieee80211_bss_conf *link_conf) +int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) { const struct cfg80211_bss_ies *ies; const struct element *bss_load_elem = NULL; @@ -787,6 +940,51 @@ int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, return chan_load; } +/* Returns whether internal MLO Scan needs to be triggered */ +bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + u32 new_chan_load) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + enum iwl_mld_link_chan_load_level new_lvl; + bool scan_trig = false; + + if (WARN_ON(!mld_link)) + return false; + + /* For each Level, + * First check if high limit threshold crosses + * If not then, check if low limit threshold crosses + * Set new level based on low limit thresh only if old level + * is not lower than level threshold + */ + for (new_lvl = LINK_CHAN_LOAD_LVL_MAX; + new_lvl > LINK_CHAN_LOAD_LVL_NONE; new_lvl--) { + if (new_chan_load >= + link_chan_load_thresh_tbl[new_lvl].high_lim) + break; + if (new_chan_load >= + link_chan_load_thresh_tbl[new_lvl].low_lim && + mld_link->chan_load_lvl >= new_lvl) + break; + } + + /* Trigger scan only for Level Up Transition */ + if (new_lvl > mld_link->chan_load_lvl) + scan_trig = true; + + IWL_DEBUG_EHT(mld, + "Link %d: chan_load=%d%%, old_lvl=%d, new_lvl=%d, scan_trig=%d\n", + link_conf->link_id, new_chan_load, + mld_link->chan_load_lvl, new_lvl, scan_trig); + + /* Update computed new level */ + mld_link->chan_load_lvl = new_lvl; + + return scan_trig; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_chan_load_requires_scan); + static unsigned int iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf) { @@ -828,16 +1026,223 @@ iwl_mld_get_avail_chan_load(struct iwl_mld *mld, return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf); } +VISIBLE_IF_IWLWIFI_KUNIT s8 +iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + const struct ieee80211_he_6ghz_oper *he_6ghz_oper; + const struct cfg80211_bss_ies *beacon_ies; + const struct element *elem; + + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_get_dup_beacon_rssi_adjust, + mld, link_conf); + + /* Duplicated beacon feature is only specific to 6 GHz */ + if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ, + "Unexpected band %d\n", + link_conf->chanreq.oper.chan->band)) + return 0; + + lockdep_assert_wiphy(mld->wiphy); + + beacon_ies = wiphy_dereference(mld->wiphy, link_conf->bss->beacon_ies); + if (!beacon_ies) + return 0; + + elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, + beacon_ies->data, beacon_ies->len); + if (!elem || + elem->datalen < sizeof(struct ieee80211_he_operation) + 1 || + elem->datalen < ieee80211_he_oper_size(&elem->data[1])) + return 0; + + he_6ghz_oper = ieee80211_he_6ghz_oper((const void *)&elem->data[1]); + if (!he_6ghz_oper) + return 0; + + if (!(he_6ghz_oper->control & IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON)) + return 0; + + /* Apply adjustment based on operational bandwidth */ + switch (link_conf->chanreq.oper.width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + return 0; + case NL80211_CHAN_WIDTH_40: + return 3; + case NL80211_CHAN_WIDTH_80: + return 6; + case NL80211_CHAN_WIDTH_160: + return 9; + case NL80211_CHAN_WIDTH_320: + return 12; + default: + WARN_ONCE(1, "Unexpected channel width: %d\n", + link_conf->chanreq.oper.width); + return 0; + } +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_dup_beacon_rssi_adjust); + +static s8 iwl_mld_get_primary_psd(const struct ieee80211_parsed_tpe_psd *psd, + const struct cfg80211_chan_def *chandef, + int bw_mhz) +{ + int start_freq, primary_idx; + + if (!psd->valid) + return S8_MAX; + + start_freq = chandef->center_freq1 - (bw_mhz / 2); + primary_idx = (chandef->chan->center_freq - start_freq - 10) / 20; + + if (primary_idx < 0 || primary_idx >= psd->count) + return S8_MAX; + + /* TPE element stores PSD limit as value * 2 */ + return psd->power[primary_idx] / 2; +} + +VISIBLE_IF_IWLWIFI_KUNIT s8 +iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) +{ + const struct ieee80211_parsed_tpe *tpe = &link_conf->tpe; + s8 psd_20mhz, psd_oper, psd_local, psd_reg, psd_boost; + s8 min_20mhz, min_oper, adjustment, ap_power_limit; + s8 psd_avg_local = S8_MAX, psd_avg_reg = S8_MAX; + s8 eirp_20mhz, eirp_oper, eirp_local, eirp_reg; + int bw_mhz, num_subchans; + u8 bw_index; + + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_get_psd_eirp_rssi_adjust, + link_conf); + + /* PSD/EIRP adjustment is only specific to 6 GHz */ + if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ, + "PSD/EIRP adjustment called for non-6 GHz band %d\n", + link_conf->chanreq.oper.chan->band)) + return 0; + + bw_mhz = nl80211_chan_width_to_mhz(link_conf->chanreq.oper.width); + + switch (bw_mhz) { + case 20: + bw_index = 0; + break; + case 40: + bw_index = 1; + break; + case 80: + bw_index = 2; + break; + case 160: + bw_index = 3; + break; + case 320: + bw_index = 4; + break; + default: + WARN_ONCE(1, "Unexpected bandwidth: %d MHz\n", bw_mhz); + return 0; + } + + if (link_conf->power_type == IEEE80211_REG_VLP_AP) + ap_power_limit = 14; + else + ap_power_limit = 23; + + /* Primary 20 MHz PSD */ + psd_local = iwl_mld_get_primary_psd(&tpe->psd_local[0], + &link_conf->chanreq.oper, + bw_mhz); + psd_reg = iwl_mld_get_primary_psd(&tpe->psd_reg_client[0], + &link_conf->chanreq.oper, + bw_mhz); + psd_20mhz = min(psd_local, psd_reg); + + /* TPE element stores EIRP limit as value * 2 */ + eirp_local = (tpe->max_local[0].valid && tpe->max_local[0].count > 0) ? + tpe->max_local[0].power[0] / 2 : S8_MAX; + eirp_reg = (tpe->max_reg_client[0].valid && + tpe->max_reg_client[0].count > 0) ? + tpe->max_reg_client[0].power[0] / 2 : S8_MAX; + eirp_20mhz = min(eirp_local, eirp_reg); + + num_subchans = bw_mhz / 20; + + if (tpe->psd_local[0].valid) { + int sum_local = 0, valid_local = 0; + int count_local = min(num_subchans, tpe->psd_local[0].count); + + for (int i = 0; i < count_local; i++) { + if (tpe->psd_local[0].power[i] != S8_MIN) { + sum_local += tpe->psd_local[0].power[i]; + valid_local++; + } + } + /* TPE element stores PSD limit as value * 2 */ + if (valid_local > 0) + psd_avg_local = sum_local / valid_local / 2; + } + + if (tpe->psd_reg_client[0].valid) { + int sum_reg = 0, valid_reg = 0; + int count_reg = min(num_subchans, tpe->psd_reg_client[0].count); + + for (int i = 0; i < count_reg; i++) { + if (tpe->psd_reg_client[0].power[i] != S8_MIN) { + sum_reg += + tpe->psd_reg_client[0].power[i]; + valid_reg++; + } + } + /* TPE element stores PSD limit as value * 2 */ + if (valid_reg > 0) + psd_avg_reg = sum_reg / valid_reg / 2; + } + + psd_oper = min(psd_avg_local, psd_avg_reg); + + /* TPE element stores EIRP limit as value * 2 */ + eirp_local = (tpe->max_local[0].valid && + tpe->max_local[0].count > bw_index) ? + tpe->max_local[0].power[bw_index] / 2 : S8_MAX; + eirp_reg = (tpe->max_reg_client[0].valid && + tpe->max_reg_client[0].count > bw_index) ? + tpe->max_reg_client[0].power[bw_index] / 2 : S8_MAX; + eirp_oper = min(eirp_local, eirp_reg); + + min_20mhz = min(ap_power_limit, min(eirp_20mhz, psd_20mhz)); + + /* PSD boost: 10*log10(BW/20) approximated as 3*ilog2(BW/20) */ + psd_boost = 3 * ilog2(bw_mhz / 20); + + /* Use int for psd_oper + psd_boost to prevent s8 overflow */ + min_oper = min(ap_power_limit, + min(eirp_oper, + (s8)min_t(int, psd_oper + psd_boost, S8_MAX))); + + adjustment = max(min_oper - min_20mhz, 0); + + return adjustment; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_psd_eirp_rssi_adjust); + /* This function calculates the grade of a link. Returns 0 in error case */ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf) { + const struct iwl_mld_rssi_to_grade *grade_table; enum nl80211_band band; - int rssi_idx; + int rssi_idx, table_size, bw_mhz; s32 link_rssi; - unsigned int grade = MAX_GRADE; + unsigned int grade; - if (WARN_ON_ONCE(!link_conf)) + if (WARN_ON_ONCE(!link_conf || !link_conf->bss)) + return 0; + + bw_mhz = nl80211_chan_width_to_mhz(link_conf->chanreq.oper.width); + if (bw_mhz < 0) return 0; band = link_conf->chanreq.oper.chan->band; @@ -852,25 +1257,64 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, * For 6 GHz the RSSI of the beacons is lower than * the RSSI of the data. */ - if (band == NL80211_BAND_6GHZ && link_rssi) - link_rssi += 4; + if (band == NL80211_BAND_6GHZ && link_rssi) { + s8 rssi_adj_6g = + iwl_mld_get_dup_beacon_rssi_adjust(mld, link_conf); + + if (!rssi_adj_6g) + rssi_adj_6g = + iwl_mld_get_psd_eirp_rssi_adjust(link_conf); + + if (!rssi_adj_6g) + rssi_adj_6g = 4; + + link_rssi += rssi_adj_6g; + } + + /* Select grading table based on operational bandwidth */ + switch (bw_mhz) { + case 20: + grade_table = rssi_to_grade_20mhz; + table_size = ARRAY_SIZE(rssi_to_grade_20mhz); + break; + case 40: + grade_table = rssi_to_grade_40mhz; + table_size = ARRAY_SIZE(rssi_to_grade_40mhz); + break; + case 80: + grade_table = rssi_to_grade_80mhz; + table_size = ARRAY_SIZE(rssi_to_grade_80mhz); + break; + case 160: + grade_table = rssi_to_grade_160mhz; + table_size = ARRAY_SIZE(rssi_to_grade_160mhz); + break; + case 320: + grade_table = rssi_to_grade_320mhz; + table_size = ARRAY_SIZE(rssi_to_grade_320mhz); + break; + default: + WARN_ONCE(1, "Invalid bandwidth: %d MHz\n", bw_mhz); + return 0; + } + + /* Initialize grade to maximum value from selected table */ + grade = grade_table[table_size - 1].grade; rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1; - /* No valid RSSI - take the lowest grade */ + /* No valid RSSI - take the lowest grade from selected table */ if (!link_rssi) - link_rssi = rssi_to_grade_map[0].rssi[rssi_idx]; + link_rssi = grade_table[0].rssi[rssi_idx]; IWL_DEBUG_EHT(mld, - "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n", - link_conf->link_id, band, - link_conf->chanreq.oper.width, + "Calculating grade of link %d: band = %d, BW = %d, punct subchannels = 0x%x RSSI = %d\n", + link_conf->link_id, band, bw_mhz, link_conf->chanreq.oper.punctured, link_rssi); - /* Get grade based on RSSI */ - for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) { - const struct iwl_mld_rssi_to_grade *line = - &rssi_to_grade_map[i]; + /* Get grade based on RSSI from the bandwidth-specific table */ + for (int i = 0; i < table_size; i++) { + const struct iwl_mld_rssi_to_grade *line = &grade_table[i]; if (link_rssi > line->rssi[rssi_idx]) continue; @@ -879,8 +1323,10 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, } /* Apply the channel load and puncturing factors */ - grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR; - grade = grade * iwl_mld_get_n_subchannels(link_conf); + grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / + SCALE_FACTOR; + + iwl_mld_apply_puncturing_penalty(link_conf, &grade, bw_mhz); IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index ca691259fc5e..7b56819d45fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_link_h__ #define __iwl_mld_link_h__ @@ -10,6 +10,14 @@ #include "mld.h" #include "sta.h" +enum iwl_mld_link_chan_load_level { + LINK_CHAN_LOAD_LVL_NONE, + LINK_CHAN_LOAD_LVL1, + LINK_CHAN_LOAD_LVL2, + LINK_CHAN_LOAD_LVL3, + LINK_CHAN_LOAD_LVL_MAX = LINK_CHAN_LOAD_LVL3 +}; + /** * struct iwl_probe_resp_data - data for NoA/CSA updates * @rcu_head: used for freeing the data on update @@ -28,14 +36,17 @@ struct iwl_probe_resp_data { * @rcu_head: RCU head for freeing this data. * @fw_id: the fw id of the link. * @active: if the link is active or not. + * @avg_signal: The current average signal of beacons [dBm] retrieved from + * firmware per-link periodic stats (STATISTICS_OPER_NOTIF). * @queue_params: QoS data from mac80211. This is updated with a call to * drv_conf_tx per each AC, and then notified once with BSS_CHANGED_QOS. * So we store it here and then send one link cmd for all the ACs. * @chan_ctx: pointer to the channel context assigned to the link. If a link * has an assigned channel context it means that it is active. * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked. - * @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two. - * This tracks the one IGTK that currently exists in FW. + * @tx_igtk: FW can only have one IGTK per MAC at a time, whereas mac80211 can + * have two. This tracks the one IGTK that currently exists in FW, for TX + * purposes. The RX IGTKs are tracked per station. * @bigtks: BIGTKs of the AP. Only valid for STA mode. * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS. * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS. @@ -49,6 +60,8 @@ struct iwl_probe_resp_data { * @silent_deactivation: next deactivation needs to be silent. * @probe_resp_data: data from FW notification to store NOA related data to be * inserted into probe response. + * @chan_load_lvl: current channel load level for a link, computed based on + * channel load by others on a link. */ struct iwl_mld_link { struct rcu_head rcu_head; @@ -57,11 +70,13 @@ struct iwl_mld_link { struct_group(zeroed_on_hw_restart, u8 fw_id; bool active; + s8 avg_signal; struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; struct ieee80211_chanctx_conf __rcu *chan_ctx; bool he_ru_2mhz_block; - struct ieee80211_key_conf *igtk; + struct ieee80211_key_conf *tx_igtk; struct ieee80211_key_conf __rcu *bigtks[2]; + enum iwl_mld_link_chan_load_level chan_load_lvl; ); /* And here fields that survive a fw restart */ struct iwl_mld_int_sta bcast_sta; @@ -99,6 +114,13 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) /* Convert a percentage from [0,100] to [0,255] */ #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100) +int iwl_mld_allocate_link_fw_id(struct iwl_mld *mld, u8 *fw_id, + struct ieee80211_bss_conf *mac80211_ptr); + +int iwl_mld_send_link_cmd(struct iwl_mld *mld, + struct iwl_link_config_cmd *cmd, + enum iwl_ctxt_action action); + int iwl_mld_add_link(struct iwl_mld *mld, struct ieee80211_bss_conf *bss_conf); void iwl_mld_remove_link(struct iwl_mld *mld, @@ -120,13 +142,26 @@ int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf); +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +s8 iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); +s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf); +#endif + unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf); +int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); + int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf, bool expect_active_link); +bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + u32 new_chan_load); + void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 3c8daddc0bcb..17286b3341c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -62,17 +62,16 @@ static const struct ieee80211_iface_limit iwl_mld_limits_ap[] = { static const struct ieee80211_iface_limit iwl_mld_limits_nan[] = { { - .max = 2, + .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_NAN), }, - /* Removed when two channels are permitted */ { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP), + .max = 2, + .types = BIT(NL80211_IFTYPE_NAN_DATA), }, }; @@ -90,19 +89,13 @@ iwl_mld_iface_combinations[] = { .limits = iwl_mld_limits_ap, .n_limits = ARRAY_SIZE(iwl_mld_limits_ap), }, - /* NAN combinations follow, these exclude P2P */ + /* NAN combination follow, this excludes P2P and AP */ { - .num_different_channels = 2, - .max_interfaces = 3, - .limits = iwl_mld_limits_nan, - .n_limits = ARRAY_SIZE(iwl_mld_limits_nan) - 1, - }, - { - .num_different_channels = 1, + .num_different_channels = 3, .max_interfaces = 4, .limits = iwl_mld_limits_nan, .n_limits = ARRAY_SIZE(iwl_mld_limits_nan), - } + }, }; static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = { @@ -114,10 +107,10 @@ static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = { }; #define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \ - IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \ - __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ + IEEE80211_EML_CAP_EML_PADDING_DELAY_32US << \ + __bf_shf(IEEE80211_EML_CAP_EML_PADDING_DELAY) | \ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ - __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) + __bf_shf(IEEE80211_EML_CAP_EML_TRANSITION_DELAY)) #define IWL_MLD_CAPA_OPS (FIELD_PREP_CONST( \ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ @@ -272,6 +265,45 @@ static void iwl_mac_hw_set_flags(struct iwl_mld *mld) ieee80211_hw_set(hw, TDLS_WIDER_BW); } +static void iwl_mld_hw_set_nan(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN_DATA); + + hw->wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ); + if (mld->nvm_data->bands[NL80211_BAND_5GHZ].n_channels) + hw->wiphy->nan_supported_bands |= + BIT(NL80211_BAND_5GHZ); + + hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC | + WIPHY_NAN_FLAGS_USERSPACE_DE; + + hw->wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_VHT | + NAN_OP_MODE_PHY_MODE_HE | + NAN_OP_MODE_160MHZ; + + hw->wiphy->nan_capa.n_antennas = + (hweight32(hw->wiphy->available_antennas_tx) & + NAN_DEV_CAPA_NUM_TX_ANT_MASK) | + ((hweight32(hw->wiphy->available_antennas_rx) << + NAN_DEV_CAPA_NUM_RX_ANT_POS) & + NAN_DEV_CAPA_NUM_RX_ANT_MASK); + + /* Maximal channel switch time - use FW TLV value if available */ + if (mld->fw->ucode_capa.nan_max_chan_switch_time) + hw->wiphy->nan_capa.max_channel_switch_time = + mld->fw->ucode_capa.nan_max_chan_switch_time; + else + hw->wiphy->nan_capa.max_channel_switch_time = + 4 * USEC_PER_MSEC; + + hw->wiphy->nan_capa.phy.ht = mld->nvm_data->nan_phy_capa.ht; + hw->wiphy->nan_capa.phy.vht = mld->nvm_data->nan_phy_capa.vht; + hw->wiphy->nan_capa.phy.he = mld->nvm_data->nan_phy_capa.he; +} + static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) { struct ieee80211_hw *hw = mld->hw; @@ -334,37 +366,16 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) wiphy->hw_timestamp_max_peers = 1; + wiphy->iface_combinations = iwl_mld_iface_combinations; + if (iwl_mld_nan_supported(mld)) { - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN); - hw->wiphy->iface_combinations = iwl_mld_iface_combinations; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwl_mld_iface_combinations); - - hw->wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ); - if (mld->nvm_data->bands[NL80211_BAND_5GHZ].n_channels) - hw->wiphy->nan_supported_bands |= - BIT(NL80211_BAND_5GHZ); - - hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC | - WIPHY_NAN_FLAGS_USERSPACE_DE; - - hw->wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_MASK | - NAN_OP_MODE_80P80MHZ | - NAN_OP_MODE_160MHZ; - - /* Support 2 antenna's for Tx and Rx */ - hw->wiphy->nan_capa.n_antennas = 0x22; - - /* Maximal channel switch time is 4 msec */ - hw->wiphy->nan_capa.max_channel_switch_time = 4; - hw->wiphy->nan_capa.dev_capabilities = - NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED | - NAN_DEV_CAPA_NDPE_SUPPORTED; - } else { - wiphy->iface_combinations = iwl_mld_iface_combinations; - /* Do not include NAN combinations */ wiphy->n_iface_combinations = - ARRAY_SIZE(iwl_mld_iface_combinations) - 2; + ARRAY_SIZE(iwl_mld_iface_combinations); + iwl_mld_hw_set_nan(mld); + } else { + /* Do not include NAN combination */ + wiphy->n_iface_combinations = + ARRAY_SIZE(iwl_mld_iface_combinations) - 1; } wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); @@ -414,6 +425,8 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) mld->ext_capab[0].eml_capabilities = IWL_MLD_EMLSR_CAPA; mld->ext_capab[0].mld_capa_and_ops = IWL_MLD_CAPA_OPS; + mld->ext_capab[0].ext_mld_capa_and_ops = + IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM; } @@ -449,22 +462,6 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld) static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld) { - int ratecheck; - - /* check for rates version 3 */ - ratecheck = - (iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11) + - (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, - TLC_MNG_UPDATE_NOTIF, 0) >= 4) + - (iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, - REPLY_RX_MPDU_CMD, 0) >= 6) + - (iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9); - - if (ratecheck != 0 && ratecheck != 4) { - IWL_ERR(mld, "Firmware has inconsistent rates\n"); - return -EINVAL; - } - /* 11ax is expected to be enabled for all supported devices */ if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable)) return -EINVAL; @@ -673,6 +670,28 @@ int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw, if (ret) return ret; + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + if (WARN_ON(!mld->nan_device_vif)) { + ret = -EINVAL; + goto err; + } + + if (iwl_mld_nan_use_nan_stations(mld)) { + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_int_sta *sta = + &mld_vif->nan.mcast_data_sta; + + ret = iwl_mld_add_nan_mcast_data_sta(mld, + vif->addr, + sta); + if (ret) + goto err; + } + + return 0; + } + /* * Add the default link, but not if this is an MLD vif as that implies * the HW is restarting and it will be configured by change_vif_links. @@ -739,10 +758,17 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw, if (vif->type == NL80211_IFTYPE_P2P_DEVICE) mld->p2p_device_vif = NULL; - if (vif->type == NL80211_IFTYPE_NAN) + if (vif->type == NL80211_IFTYPE_NAN) { mld->nan_device_vif = NULL; - else + } else if (vif->type != NL80211_IFTYPE_NAN_DATA) { iwl_mld_remove_link(mld, &vif->bss_conf); + } else if (iwl_mld_nan_use_nan_stations(mld)) { + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_int_sta *sta = &mld_vif->nan.mcast_data_sta; + + if (sta->sta_id != IWL_INVALID_STA) + iwl_mld_remove_nan_mcast_data_sta(mld, sta); + } #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink); @@ -988,6 +1014,30 @@ void iwl_mld_remove_chanctx(struct ieee80211_hw *hw, mld->used_phy_ids &= ~BIT(phy->fw_id); } +static void +iwl_mld_update_link_npca_puncturing(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_vif *vif; + + for_each_active_interface(vif, hw) { + struct ieee80211_bss_conf *link; + int link_id; + + for_each_vif_active_link(vif, link, link_id) { + if (rcu_access_pointer(link->chanctx_conf) != ctx) + continue; + + if (!link->npca.enabled) + continue; + + iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_UHR_PARAMS); + } + } +} + static void iwl_mld_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) @@ -1003,9 +1053,19 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw, IEEE80211_CHANCTX_CHANGE_CHANNEL))) return; + /* NPCA puncturing is in link API for FW */ + if (changed & IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT) { + iwl_mld_update_link_npca_puncturing(hw, ctx); + changed &= ~IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT; + } + /* Check if a FW update is required */ - if (changed & IEEE80211_CHANCTX_CHANGE_AP) + if (!changed) + return; + + if (changed & IEEE80211_CHANCTX_CHANGE_AP || + changed & IEEE80211_CHANCTX_CHANGE_NPCA) goto update; if (chandef->chan == phy->chandef.chan && @@ -1238,33 +1298,6 @@ int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, return 0; } -static void -iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link, - u64 changes) -{ - u32 link_changes = 0; - - if (changes & BSS_CHANGED_ERP_SLOT) - link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; - - if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT)) - link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS; - - if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH)) - link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; - - if (changes & BSS_CHANGED_HE_BSS_COLOR) - link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; - - if (link_changes) - iwl_mld_change_link_in_fw(mld, link, link_changes); - - if (changes & BSS_CHANGED_BEACON) - iwl_mld_update_beacon_template(mld, vif, link); -} - static u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -1296,6 +1329,9 @@ u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld, link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; } + if (changes & BSS_CHANGED_NPCA) + link_changes |= LINK_CONTEXT_MODIFY_UHR_PARAMS; + return link_changes; } @@ -1374,6 +1410,10 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_MU_GROUPS) iwl_mld_update_mu_groups(mld, link_conf); break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + /* NAN has no links */ + break; default: /* shouldn't happen */ WARN_ON_ONCE(1); @@ -1389,28 +1429,6 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, iwl_mld_set_tx_power(mld, link_conf, link_conf->txpower); } -static void -iwl_mld_smps_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) -{ - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - bool workaround_required = - iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) < 2; - - if (!workaround_required) - return; - - /* Send the device-level power commands since the - * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to - * determine SMPS mode. - */ - if (mld_vif->ps_disabled == !enable) - return; - - mld_vif->ps_disabled = !enable; - - iwl_mld_update_device_power(mld, false); -} - static void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1421,6 +1439,11 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, lockdep_assert_wiphy(mld->wiphy); + if (vif->type == NL80211_IFTYPE_NAN) { + iwl_mld_nan_vif_cfg_changed(mld, vif, changes); + return; + } + if (vif->type != NL80211_IFTYPE_STATION) return; @@ -1443,10 +1466,8 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, } } - if (changes & BSS_CHANGED_PS) { - iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); + if (changes & BSS_CHANGED_PS) iwl_mld_update_mac_power(mld, vif, false); - } /* TODO: task=MLO BSS_CHANGED_MLD_VALID_LINKS/CHANGED_MLD_TTLM */ } @@ -1616,7 +1637,7 @@ iwl_mld_mac80211_conf_tx(struct ieee80211_hw *hw, lockdep_assert_wiphy(mld->wiphy); - if (vif->type == NL80211_IFTYPE_NAN) + if (vif->type == NL80211_IFTYPE_NAN || vif->type == NL80211_IFTYPE_NAN_DATA) return 0; link = iwl_mld_link_dereference_check(mld_vif, link_id); @@ -1832,7 +1853,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, new_state == IEEE80211_STA_AUTHORIZED) { ret = 0; - if (!sta->tdls) { + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { mld_vif->authorized = true; /* Ensure any block due to a non-BSS link is synced */ @@ -1853,7 +1874,6 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, FW_CTXT_ACTION_MODIFY); if (ret) return ret; - iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); } /* MFP is set by default before the station is authorized. @@ -1898,7 +1918,6 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, &mld_vif->mlo_scan_start_wk); iwl_mld_reset_cca_40mhz_workaround(mld, vif); - iwl_mld_smps_workaround(mld, vif, true); } /* once we move into assoc state, need to update the FW to @@ -2243,7 +2262,6 @@ static int iwl_mld_set_key_add(struct iwl_mld *mld, ret = iwl_mld_add_key(mld, vif, sta, key); if (ret) { - IWL_WARN(mld, "set key failed (%d)\n", ret); if (ptk_pn) { RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL); kfree(ptk_pn); @@ -2285,7 +2303,9 @@ static void iwl_mld_set_key_remove(struct iwl_mld *mld, } /* if this key was stored to be added later to the FW - free it here */ - if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) iwl_mld_free_ap_early_key(mld, key, mld_vif); /* We already removed it */ @@ -2303,9 +2323,20 @@ static int iwl_mld_mac80211_set_key(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); int ret; + /* + * FW always needs the AP STA for client mode. + * Note that during removal this could already + * be NULL (mac80211 removes keys after STAs) + * but then we'll already have removed the key + * and set hw_key_idx = STA_KEY_IDX_INVALID. + */ + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mld_vif->ap_sta; + switch (cmd) { case SET_KEY: ret = iwl_mld_set_key_add(mld, vif, sta, key); @@ -2566,15 +2597,108 @@ iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw, ret); } +static int iwl_mld_count_free_link_ids(struct iwl_mld *mld) +{ + int free_count = 0; + + for (int i = 0; i < mld->fw->ucode_capa.num_links; i++) { + if (!rcu_access_pointer(mld->fw_id_to_bss_conf[i])) + free_count++; + } + + return free_count; +} + +static bool +iwl_mld_chanctx_used_by_other_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + struct ieee80211_bss_conf *iter_link; + struct ieee80211_vif *iter_vif; + int link_id; + + for_each_active_interface(iter_vif, hw) { + if (vif == iter_vif) + continue; + + /* NAN doesn't have active links, so we don't count NAN users */ + for_each_vif_active_link(iter_vif, iter_link, link_id) { + if (rcu_access_pointer(iter_link->chanctx_conf) == + chanctx_conf) + return true; + } + } + + return false; +} + static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 desired_links) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); int n_links = hweight16(desired_links); + int n_add = hweight16(desired_links & ~vif->active_links); + unsigned long to_deactivate = vif->active_links & ~desired_links; + int free_link_ids; + int i; /* Check if HW supports the wanted number of links */ - return n_links <= iwl_mld_max_active_links(mld, vif); + if (n_links > iwl_mld_max_active_links(mld, vif)) + return false; + + /* + * During link switch, mac80211 first adds the new links, then removes + * the old ones. This means we temporarily need extra link objects + * during the transition. Check if we have enough free link IDs. + */ + + free_link_ids = iwl_mld_count_free_link_ids(mld); + + if (free_link_ids >= n_add) + return true; + + if (!mld->nan_device_vif) + return false; + + /* + * Not enough free link IDs. First try to evacuate NAN from the + * channel context of a link that is going to be deactivated. + */ + for_each_set_bit(i, &to_deactivate, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + struct ieee80211_chanctx_conf *chanctx_conf; + + link_conf = link_conf_dereference_protected(vif, i); + if (!link_conf) + continue; + + chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); + if (!chanctx_conf) + continue; + + if (iwl_mld_chanctx_used_by_other_vif(hw, vif, chanctx_conf)) + continue; + + if (ieee80211_nan_try_evacuate(hw, chanctx_conf)) { + free_link_ids = iwl_mld_count_free_link_ids(mld); + /* + * Evacuation of one channel should do the job. If not, + * something bad is happening. Don't try to evacuate more + */ + return free_link_ids >= n_add; + } + } + + /* Couldn't find/evacuate any channel going to go unused, try any */ + if (ieee80211_nan_try_evacuate(hw, NULL)) { + free_link_ids = iwl_mld_count_free_link_ids(mld); + if (free_link_ids >= n_add) + return true; + } + + return false; } static int @@ -2854,4 +2978,5 @@ const struct ieee80211_ops iwl_mld_hw_ops = { .start_nan = iwl_mld_start_nan, .stop_nan = iwl_mld_stop_nan, .nan_change_conf = iwl_mld_nan_change_config, + .nan_peer_sched_changed = iwl_mld_mac802111_nan_peer_sched_changed, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index 16bb1b4904f9..8502129abe49 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -129,7 +129,7 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, mld->mcc_src = resp->source_id; - /* FM is the earliest supported and later always do puncturing */ + /* FM follows BIOS/MCC policy, WH disallows puncturing only in US/CA. */ if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) { if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing, le16_to_cpu(resp->mcc))) @@ -137,6 +137,15 @@ iwl_mld_get_regdomain(struct iwl_mld *mld, else __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mld->hw->flags); + } else if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == + IWL_CFG_RF_TYPE_WH) { + u16 mcc = le16_to_cpu(resp->mcc); + + if (mcc == IWL_MCC_US || mcc == IWL_MCC_CANADA) + ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); + else + __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, + mld->hw->flags); } out: diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c index 9af79297c3b6..78c78cf891cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include @@ -162,7 +162,6 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = { HCMD_NAME(PHY_CONFIGURATION_CMD), HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), HCMD_NAME(POWER_TABLE_CMD), - HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), HCMD_NAME(BEACON_NOTIFICATION), HCMD_NAME(BEACON_TEMPLATE_CMD), HCMD_NAME(TX_ANT_CONFIGURATION_CMD), @@ -236,6 +235,10 @@ static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = { HCMD_NAME(STA_REMOVE_CMD), HCMD_NAME(ROC_CMD), HCMD_NAME(NAN_CFG_CMD), + HCMD_NAME(NAN_SCHEDULE_CMD), + HCMD_NAME(NAN_PEER_CMD), + HCMD_NAME(NAN_ULW_ATTR_NOTIF), + HCMD_NAME(NAN_SCHED_UPDATE_COMPLETED_NOTIF), HCMD_NAME(NAN_DW_END_NOTIF), HCMD_NAME(NAN_JOINED_CLUSTER_NOTIF), HCMD_NAME(MISSED_BEACONS_NOTIF), @@ -259,6 +262,7 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = { HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD), HCMD_NAME(SCD_QUEUE_CONFIG_CMD), HCMD_NAME(SEC_KEY_CMD), + HCMD_NAME(RSC_NOTIF), HCMD_NAME(ESR_MODE_NOTIF), HCMD_NAME(MONITOR_NOTIF), HCMD_NAME(TLC_MNG_UPDATE_NOTIF), @@ -411,9 +415,6 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir); - /* we'll verify later it matches between commands */ - mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11; - iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); iwl_mld_get_bios_tables(mld); @@ -675,6 +676,15 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode, if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT && mld->fw_status.running) mld->fw_status.in_hw_restart = true; + + /* FW is dead. We don't want to process its notifications. + * Right, we cancel them also in iwl_mld_stop_fw, but + * iwl_mld_async_handlers_wk might be executed before + * ieee80211_restart_work. + * In addition, in case of an error during recovery, + * iwl_mld_stop_fw might be too late. + */ + iwl_mld_cancel_async_notifications(mld); } static void iwl_mld_dump_error(struct iwl_op_mode *op_mode, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h index 606cb64f8ea4..922aa3dbff54 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_h__ #define __iwl_mld_h__ @@ -189,7 +189,6 @@ * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure). * @set_tx_ant: stores the last TX antenna bitmask set by user space (if any) * @set_rx_ant: stores the last RX antenna bitmask set by user space (if any) - * @fw_rates_ver_3: FW rates are in version 3 * @low_latency: low-latency manager. * @tzone: thermal zone device's data * @cooling_dev: cooling device's related data @@ -299,8 +298,6 @@ struct iwl_mld { u8 set_tx_ant; u8 set_rx_ant; - bool fw_rates_ver_3; - struct iwl_mld_low_latency low_latency; bool ibss_manager; @@ -555,15 +552,24 @@ iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \ return -ENOSPC; \ } +#define IWL_MLD_ALLOC_FN_STATIC(_type, _mac80211_type) \ +static IWL_MLD_ALLOC_FN(_type, _mac80211_type) + static inline struct ieee80211_bss_conf * iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id) { + struct ieee80211_bss_conf *link; + if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links, "Invalid fw_link_id: %d\n", fw_link_id)) return NULL; - return wiphy_dereference(mld->wiphy, + link = wiphy_dereference(mld->wiphy, mld->fw_id_to_bss_conf[fw_link_id]); + if (IS_ERR(link)) + return NULL; + + return link; } #define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c index 8227ccb31d60..a2f8a6957535 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mlo.h" #include "phy.h" @@ -1081,8 +1081,13 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac, container_of((const void *)phy, struct ieee80211_chanctx_conf, drv_priv); struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; - struct ieee80211_bss_conf *prim_link; + u32 new_chan_load = phy->avg_channel_load_not_by_us; + struct ieee80211_bss_conf *prim_link, *link_conf; unsigned int prim_link_id; + int link_id; + + if (!ieee80211_vif_is_mld(vif) || hweight16(vif->valid_links) <= 1) + return; prim_link_id = iwl_mld_get_primary_link(vif); prim_link = link_conf_dereference_protected(vif, prim_link_id); @@ -1090,6 +1095,32 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac, if (WARN_ON(!prim_link)) return; + /* Evaluate MLO Internal Scan for high chan load beyond thresholds */ + for_each_vif_active_link(vif, link_conf, link_id) { + if (rcu_access_pointer(link_conf->chanctx_conf) != chanctx) + continue; + + /* No QBSS IE - links will be selected based on default channel + * load values, so the same link will be selected again. + * No point in scan. + */ + if (iwl_mld_get_chan_load_from_element(mld, link_conf) < 0) + continue; + + if (iwl_mld_chan_load_requires_scan(mld, + link_conf, + new_chan_load)) { + /* When EMLSR is active, only trigger scan based on + * primary link + */ + if (iwl_mld_emlsr_active(vif) && link_conf != prim_link) + continue; + + iwl_mld_int_mlo_scan(mld, vif); + return; + } + } + if (chanctx != rcu_access_pointer(prim_link->chanctx_conf)) return; @@ -1107,7 +1138,6 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac, prim_link_id); } else { u32 old_chan_load = data->prev_chan_load_not_by_us; - u32 new_chan_load = phy->avg_channel_load_not_by_us; u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx); #define THRESHOLD_CROSSED(threshold) \ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c index 4d8e85f2bd7c..dbe7a54a38b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c @@ -5,8 +5,12 @@ #include "mld.h" #include "iface.h" +#include "link.h" #include "mlo.h" +#include "tlc.h" #include "fw/api/mac-cfg.h" +#include "fw/api/mac.h" +#include "fw/api/rs.h" #define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512 #define IWL_NAN_RSSI_CLOSE 55 @@ -14,8 +18,17 @@ bool iwl_mld_nan_supported(struct iwl_mld *mld) { - return fw_has_capa(&mld->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT); + const struct iwl_fw *fw = mld->fw; + + if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT) && + iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD), 0) >= 1 && + iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD), 0) >= 1 && + iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), 0) >= 3 && + iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 0) >= 4 && + iwl_fw_lookup_cmd_ver(fw, WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD), 0) >= 6 && + mld->nvm_data->nan_phy_capa.ht.ht_supported) + return true; + return false; } static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld, @@ -38,6 +51,98 @@ static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld, return iwl_mld_send_cmd(mld, &hcmd); } +bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld) +{ + /* + * If the FW supports version 1 of the NAN config command, it means that + * it needs to receive the station ID of the auxiliary station in the + * NAN configuration command. Otherwise, use the NAN dedicated station + * types. + */ + return iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, + NAN_CFG_CMD), 1) != 1; +} + +static const struct iwl_mld_int_sta * +iwl_mld_nan_get_mgmt_sta(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + const struct iwl_mld_int_sta *sta; + + if (iwl_mld_nan_use_nan_stations(mld)) + sta = &mld_vif->nan.mgmt_sta; + else + sta = &mld_vif->aux_sta; + + if (WARN_ON(sta->sta_id == IWL_INVALID_STA)) + return NULL; + + return sta; +} + +int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif); + + if (!sta) + return IWL_MLD_INVALID_QUEUE; + + return sta->queue_id; +} + +static void iwl_mld_nan_flush(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif); + + if (!sta) + return; + + if (WARN_ON(sta->queue_id == IWL_MLD_INVALID_QUEUE)) + return; + + IWL_DEBUG_INFO(mld, "NAN: flush queues for sta=%u\n", + sta->sta_id); + + iwl_mld_flush_link_sta_txqs(mld, sta->sta_id); +} + +static void iwl_mld_nan_remove_stations(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + iwl_mld_nan_flush(mld, vif); + + if (!iwl_mld_nan_use_nan_stations(mld)) { + iwl_mld_remove_aux_sta(mld, vif); + return; + } + + iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta); + iwl_mld_remove_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta); +} + +static int iwl_mld_nan_add_stations(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (!iwl_mld_nan_use_nan_stations(mld)) + return iwl_mld_add_aux_sta(mld, &mld_vif->aux_sta); + + ret = iwl_mld_add_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta); + if (ret) + return ret; + + ret = iwl_mld_add_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta); + if (ret) + iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta); + + return ret; +} + static int iwl_mld_nan_config(struct iwl_mld *mld, struct ieee80211_vif *vif, struct cfg80211_nan_conf *conf, @@ -114,7 +219,12 @@ static int iwl_mld_nan_config(struct iwl_mld *mld, conf->vendor_elems_len); } - cmd.sta_id = mld_vif->aux_sta.sta_id; + /* FW needs to know about the station ID only with version 1 of the + * NAN configuration command + */ + if (!iwl_mld_nan_use_nan_stations(mld)) + cmd.sta_id = mld_vif->aux_sta.sta_id; + return iwl_mld_nan_send_config_cmd(mld, &cmd, data, conf->extra_nan_attrs_len + conf->vendor_elems_len); @@ -124,8 +234,6 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_nan_conf *conf) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta; int ret; IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands); @@ -134,19 +242,20 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (ret) return ret; - ret = iwl_mld_add_aux_sta(mld, aux_sta); + ret = iwl_mld_nan_add_stations(mld, vif); if (ret) goto unblock_emlsr; ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD); if (ret) { IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret); - goto remove_aux; + goto remove_stas; } + return 0; -remove_aux: - iwl_mld_remove_aux_sta(mld, vif); +remove_stas: + iwl_mld_nan_remove_stations(mld, vif); unblock_emlsr: iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN); @@ -174,7 +283,6 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); struct iwl_nan_config_cmd cmd = { .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), }; @@ -191,8 +299,7 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw, /* assume that higher layer guarantees that no additional frames are * added before calling this callback */ - iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); - iwl_mld_remove_aux_sta(mld, vif); + iwl_mld_nan_remove_stations(mld, vif); /* cancel based on object type being NAN, as the NAN objects do * not have a unique identifier associated with them @@ -247,6 +354,39 @@ bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld, return true; } +bool iwl_mld_cancel_nan_ulw_attr_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 obj_id) +{ + return true; +} + +void iwl_mld_handle_nan_ulw_attr_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_nan_ulw_attr_notif *notif = (void *)pkt->data; + struct wireless_dev *wdev; + + IWL_DEBUG_INFO(mld, "NAN: ULW attr update: len=%u\n", notif->attr_len); + + if (IWL_FW_CHECK(mld, !mld->nan_device_vif, + "NAN: ULW attr update without NAN vif\n")) + return; + + if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif), + "NAN: ULW attr update without NAN started\n")) + return; + + if (IWL_FW_CHECK(mld, + notif->attr_len > IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN, + "NAN: ULW attr update invalid len %u\n", + notif->attr_len)) + return; + + wdev = ieee80211_vif_to_wdev(mld->nan_device_vif); + cfg80211_nan_ulw_update(wdev, notif->attr, notif->attr_len, GFP_KERNEL); +} + void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { @@ -257,7 +397,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, struct wireless_dev *wdev; struct ieee80211_channel *chan; - IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band); + IWL_DEBUG_INFO(mld, "NAN: DW end: band=%u\n", notif->band); if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n")) return; @@ -266,13 +406,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, "NAN: DW end without NAN started\n")) return; - if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA)) - return; - - IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n", - mld_vif->aux_sta.sta_id); - - iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); + iwl_mld_nan_flush(mld, mld->nan_device_vif); /* TODO: currently the notification specified the band on which the DW * ended. Need to change that to the actual channel on which the next DW @@ -293,6 +427,581 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, return; } + if (WARN_ON_ONCE(!chan)) + return; + wdev = ieee80211_vif_to_wdev(mld->nan_device_vif); cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL); } + +static void iwl_mld_nan_fill_rates(struct iwl_link_config_cmd *cmd) +{ + u32 ofdm = 0; + + /* All OFDM rates - NAN uses OFDM only, no CCK */ + ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(9) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(18) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(36) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(48) >> IWL_FIRST_OFDM_RATE; + ofdm |= IWL_RATE_BIT_MSK(54) >> IWL_FIRST_OFDM_RATE; + + cmd->ofdm_rates = cpu_to_le32(ofdm); + cmd->short_slot = cpu_to_le32(1); +} + +static void iwl_mld_nan_fill_qos(struct iwl_ac_qos *ac, __le32 *qos_flags) +{ + /* AC_BK: CWmin=15, CWmax=1023, AIFSN=7, TXOP=0 */ + ac[AC_BK].cw_min = cpu_to_le16(15); + ac[AC_BK].cw_max = cpu_to_le16(1023); + ac[AC_BK].aifsn = 7; + ac[AC_BK].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BK); + ac[AC_BK].edca_txop = 0; + + /* AC_BE: CWmin=15, CWmax=1023, AIFSN=3, TXOP=0 */ + ac[AC_BE].cw_min = cpu_to_le16(15); + ac[AC_BE].cw_max = cpu_to_le16(1023); + ac[AC_BE].aifsn = 3; + ac[AC_BE].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BE); + ac[AC_BE].edca_txop = 0; + + /* AC_VI: CWmin=7, CWmax=15, AIFSN=2, TXOP=3008us */ + ac[AC_VI].cw_min = cpu_to_le16(7); + ac[AC_VI].cw_max = cpu_to_le16(15); + ac[AC_VI].aifsn = 2; + ac[AC_VI].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VI); + ac[AC_VI].edca_txop = cpu_to_le16(3008); + + /* AC_VO: CWmin=3, CWmax=7, AIFSN=2, TXOP=1504us */ + ac[AC_VO].cw_min = cpu_to_le16(3); + ac[AC_VO].cw_max = cpu_to_le16(7); + ac[AC_VO].aifsn = 2; + ac[AC_VO].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VO); + ac[AC_VO].edca_txop = cpu_to_le16(1504); + + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); +} + +static void +iwl_mld_nan_link_prep_cmd(struct iwl_mld *mld, + struct iwl_mld_nan_link *nan_link, + struct iwl_link_config_cmd *cmd, + u32 modify_flags) +{ + struct ieee80211_vif *vif = mld->nan_device_vif; + struct iwl_mld_vif *mld_vif; + + if (WARN_ON_ONCE(!vif)) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + + memset(cmd, 0, sizeof(*cmd)); + + if (!nan_link->chanctx) { + cmd->phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); + } else { + struct iwl_mld_phy *mld_phy; + + mld_phy = iwl_mld_phy_from_mac80211(nan_link->chanctx); + cmd->phy_id = cpu_to_le32(mld_phy->fw_id); + } + + if (modify_flags & LINK_CONTEXT_MODIFY_RATES_INFO) + iwl_mld_nan_fill_rates(cmd); + + if (modify_flags & LINK_CONTEXT_MODIFY_QOS_PARAMS) + iwl_mld_nan_fill_qos(cmd->ac, &cmd->qos_flags); + + cmd->link_id = cpu_to_le32(nan_link->fw_id); + cmd->mac_id = cpu_to_le32(mld_vif->fw_id); + cmd->active = cpu_to_le32(nan_link->active); + + ether_addr_copy(cmd->local_link_addr, vif->addr); + + cmd->modify_mask = cpu_to_le32(modify_flags); +} + +static struct iwl_mld_nan_link * +iwl_mld_nan_link_add(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif, + struct ieee80211_chanctx_conf *chanctx) +{ + struct iwl_mld_nan_link *nan_link; + struct iwl_link_config_cmd cmd; + u8 fw_id; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_allocate_link_fw_id(mld, &fw_id, ERR_PTR(-ENODEV)); + /* + * We should always have enough links. The schedule contains up to 3, + * and the BSS vif cannot do EMLSR - so can only have 1. + */ + if (WARN_ON(ret < 0)) + return NULL; + + nan_link = &mld_vif->nan.links[fw_id]; + + if (WARN_ON_ONCE(nan_link->fw_id != FW_CTXT_ID_INVALID)) + goto err; + + nan_link->fw_id = fw_id; + nan_link->chanctx = chanctx; + + iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd, + LINK_CONTEXT_MODIFY_RATES_INFO | + LINK_CONTEXT_MODIFY_QOS_PARAMS); + + ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD); + if (ret) { + nan_link->fw_id = FW_CTXT_ID_INVALID; + nan_link->chanctx = NULL; + goto err; + } + + return nan_link; +err: + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL); + return NULL; +} + +static int iwl_mld_nan_link_set_active(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_nan_link *nan_link, + bool active) +{ + struct iwl_link_config_cmd cmd; + struct ieee80211_sta *sta; + int ret; + + if (nan_link->active == active) + return 0; + + if (active) { + for_each_station(sta, mld->hw) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) + iwl_mld_config_tlc(mld, mld_sta->vif, sta); + } + } + + nan_link->active = active; + + iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd, + LINK_CONTEXT_MODIFY_ACTIVE); + + ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY); + if (ret) { + nan_link->active = !nan_link->active; + return ret; + } + + if (!active) { + nan_link->chanctx = NULL; + /* TODO: when FW is ready, Update phy in TLC to invalid after */ + } + + return 0; +} + +static void iwl_mld_nan_link_remove(struct iwl_mld *mld, + struct iwl_mld_nan_link *nan_link, + u32 link_id) +{ + struct iwl_link_config_cmd cmd = { + .link_id = cpu_to_le32(link_id), + .phy_id = cpu_to_le32(FW_CTXT_ID_INVALID), + }; + + iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); + + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link_id], NULL); + nan_link->fw_id = FW_CTXT_ID_INVALID; + nan_link->active = false; + nan_link->chanctx = NULL; +} + +static bool iwl_mld_nan_have_links(struct iwl_mld_vif *mld_vif) +{ + struct iwl_mld_nan_link *nan_link; + + for_each_mld_nan_valid_link(mld_vif, nan_link) + return true; + + return false; +} + +static struct iwl_mld_nan_link * +iwl_mld_nan_find_link(struct iwl_mld_vif *mld_vif, + struct ieee80211_chanctx_conf *chanctx) +{ + struct iwl_mld_nan_link *nan_link; + + for_each_mld_nan_valid_link(mld_vif, nan_link) { + if (nan_link->chanctx == chanctx) + return nan_link; + } + + return NULL; +} + +static void iwl_mld_nan_set_mcast_data_links(struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (vif->type != NL80211_IFTYPE_NAN_DATA) + return; + + /* Note that all errors are handled internally so nothing to do + * with the return value (used only to silence compilation warnings) + */ + iwl_mld_update_nan_mcast_data_sta(mld_vif->mld, vif->addr, + &mld_vif->nan.mcast_data_sta); +} + +void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u64 changes) +{ + struct iwl_nan_schedule_cmd cmd = {}; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool previously_empty_schedule = !iwl_mld_nan_have_links(mld_vif); + struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched; + struct iwl_mld_nan_link *links[ARRAY_SIZE(sched_cfg->channels)] = {}; + struct ieee80211_nan_channel **slots = sched_cfg->schedule; + bool link_used[ARRAY_SIZE(mld_vif->nan.links)] = {}; + struct iwl_mld_nan_link *nan_link; + unsigned long remove_link_ids = 0; + bool added_links = false; + bool empty_schedule = true; + int ret, i; + u16 cmd_size; + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD); + u8 version = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0); + + if (!(changes & BSS_CHANGED_NAN_LOCAL_SCHED)) + return; + + switch (version) { + case 1: + if (sched_cfg->deferred) { + IWL_ERR(mld, + "NAN: deferred schedule not supported by FW\n"); + return; + } + + cmd_size = sizeof(struct iwl_nan_schedule_cmd_v1); + break; + case 2: + cmd_size = sizeof(struct iwl_nan_schedule_cmd); + + if (sched_cfg->deferred) + cmd.deferred = 1; + + if (sched_cfg->avail_blob_len && + !WARN_ON(sched_cfg->avail_blob_len > + sizeof(cmd.avail_attr.attr))) { + cmd.avail_attr.attr_len = sched_cfg->avail_blob_len; + memcpy(cmd.avail_attr.attr, sched_cfg->avail_blob, + sched_cfg->avail_blob_len); + } + break; + default: + IWL_ERR(mld, "NAN: unsupported NAN schedule cmd version %d\n", + version); + return; + } + + for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + if (!sched_cfg->channels[i].chanreq.oper.chan) + continue; + empty_schedule = false; + break; + } + + /* add the MAC if needed (before adding links) */ + if (!empty_schedule && previously_empty_schedule) { + WARN_ON(mld_vif->nan.mac_added); + ret = iwl_mld_add_nan_vif(mld, vif); + + if (ret) { + IWL_ERR(mld, "NAN: failed to add MAC (%d)\n", ret); + return; + } + } + + if (!mld_vif->nan.mac_added) { + /* nothing to do */ + return; + } + + /* this currently just uses the same index */ + BUILD_BUG_ON(ARRAY_SIZE(sched_cfg->channels) != + ARRAY_SIZE(cmd.channels)); + + /* + * mac80211 removes unused channels before adding new ones, so it may + * update an empty schedule with an availability attribute because it + * is going to add channels later. Since the firmware does not expect + * an availability attribute without channels, ignore it in that case. + */ + if (empty_schedule) + cmd.avail_attr.attr_len = 0; + + /* find links we can keep (same chanctx/PHY) */ + for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + struct ieee80211_chanctx_conf *chanctx; + struct iwl_mld_nan_link *link; + + chanctx = sched_cfg->channels[i].chanctx_conf; + /* ULW */ + if (!chanctx) + continue; + + link = iwl_mld_nan_find_link(mld_vif, chanctx); + links[i] = link; + if (link) + link_used[link->fw_id] = true; + } + + /* add/reassign links for new channels */ + for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + struct ieee80211_chanctx_conf *chanctx; + + /* already have an existing active link */ + if (links[i]) + continue; + + chanctx = sched_cfg->channels[i].chanctx_conf; + /* ULW or unused slot */ + if (!chanctx) + continue; + + /* + * if this fails we still update the schedule, but + * without a valid link we'll always ULW it + */ + links[i] = iwl_mld_nan_link_add(mld, mld_vif, chanctx); + + /* we have a link, activate it */ + if (links[i]) { + added_links = true; + link_used[links[i]->fw_id] = true; + iwl_mld_nan_link_set_active(mld, vif, links[i], true); + } + } + + /* fill the command */ + for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + cmd.channels[i].link_id = FW_CTXT_ID_INVALID; + + if (!sched_cfg->channels[i].chanreq.oper.chan) + continue; + + memcpy(cmd.channels[i].channel_entry, + sched_cfg->channels[i].channel_entry, 6); + cmd.channels[i].link_id = + links[i] ? links[i]->fw_id : FW_CTXT_ID_INVALID; + } + + for (i = 0; i < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; i++) { + int chan_idx; + + if (!slots[i]) + continue; + + chan_idx = slots[i] - sched_cfg->channels; + if (WARN_ON_ONCE(chan_idx < 0 || + chan_idx >= ARRAY_SIZE(cmd.channels))) + continue; + + cmd.channels[chan_idx].availability_map |= cpu_to_le32(BIT(i)); + } + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size); + if (ret) + IWL_ERR(mld, "NAN: failed to update schedule (%d)\n", ret); + + /* prepare stations for links we'll remove */ + for_each_mld_nan_valid_link(mld_vif, nan_link) { + if (!link_used[nan_link->fw_id]) { + iwl_mld_nan_link_set_active(mld, vif, nan_link, false); + remove_link_ids |= BIT(nan_link->fw_id); + /* mark unused for STA updates */ + nan_link->fw_id = FW_CTXT_ID_INVALID; + } + } + + if (added_links || remove_link_ids) { + struct ieee80211_sta *sta; + + for_each_station(sta, mld->hw) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI || + mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) + iwl_mld_add_modify_sta_cmd(mld, &sta->deflink); + } + + /* + * Iterate over all the NAN Data interfaces and update the links + * for the internal multicast data station. + * In recovery - the station will be added later in + * drv_add_interface + */ + if (iwl_mld_nan_use_nan_stations(mld) && !mld->fw_status.in_hw_restart) { + struct ieee80211_vif *iter; + + for_each_active_interface(iter, mld->hw) + iwl_mld_nan_set_mcast_data_links(iter); + } + } + + /* delete unused links */ + for_each_set_bit(i, &remove_link_ids, ARRAY_SIZE(mld_vif->nan.links)) + iwl_mld_nan_link_remove(mld, &mld_vif->nan.links[i], i); + + /* remove MAC if needed */ + if (!previously_empty_schedule && empty_schedule) { + /* must have been added */ + WARN_ON_ONCE(!mld_vif->nan.mac_added); + + /* mac80211 should reconfigure same state */ + if (!WARN_ON_ONCE(mld->fw_status.in_hw_restart && + !iwl_mld_error_before_recovery(mld))) + iwl_mld_rm_vif(mld, vif); + } +} + +bool iwl_mld_cancel_nan_sched_update_completed_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 obj_id) +{ + return true; +} + +void iwl_mld_handle_nan_sched_update_completed_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_nan_sched_update_completed_notif *notif = (void *)pkt->data; + struct ieee80211_vif *vif = mld->nan_device_vif; + + if (IWL_FW_CHECK(mld, !vif, + "NAN: schedule update completed without NAN vif\n")) + return; + + if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(vif), + "NAN: schedule update completed without NAN started\n")) + return; + + /* + * Deferred schedule update should not fail in firmware since all + * channels and links were added. + */ + IWL_FW_CHECK(mld, notif->status != IWL_NAN_SCHED_UPDATE_SUCCESS, + "NAN: deferred schedule update failed\n"); + + if (WARN_ON(!vif->cfg.nan_sched.deferred)) + return; + + ieee80211_nan_sched_update_done(vif); +} + +int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_nan_peer_sched *sched = sta->nan_sched; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_nan_link *nan_link; + struct iwl_nan_peer_cmd cmd = { + .nmi_sta_id = mld_sta->deflink.fw_id, + .sequence_id = sched->seq_id, + .committed_dw_info = cpu_to_le16(sched->committed_dw), + .max_channel_switch_time = cpu_to_le16(sched->max_chan_switch), + .initial_ulw_size = cpu_to_le32(sched->ulw_size), + .per_phy[0 ... NUM_PHY_CTX - 1] = { + /* unused by FW if availability_map == 0 */ + .map_id = CFG80211_NAN_INVALID_MAP_ID, + .link_id = FW_CTXT_ID_INVALID, + }, + /* .initial_ulw directly provided below by data[1]/len[1] */ + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD), + .data[0] = &cmd, + .len[0] = sizeof(cmd), + .data[1] = sched->init_ulw, + .len[1] = sched->ulw_size, + .dataflags[1] = IWL_HCMD_DFL_DUP, + }; + struct ieee80211_sta *iter; + + /* Update TLC in case peer channels were added/removed/updated */ + for_each_station(iter, mld->hw) { + struct iwl_mld_sta *tmp = iwl_mld_sta_from_mac80211(iter); + + if (tmp->sta_type == STATION_TYPE_NAN_PEER_NDI) + iwl_mld_config_tlc(mld, tmp->vif, iter); + } + + for (int i = 0; i < ARRAY_SIZE(sched->maps); i++) { + if (sched->maps[i].map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + BUILD_BUG_ON(ARRAY_SIZE(sched->maps[i].slots) != 32); + for (int slot = 0; + slot < ARRAY_SIZE(sched->maps[i].slots); + slot++) { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_nan_channel *chan; + struct iwl_mld_phy *phy; + + chan = sched->maps[i].slots[slot]; + if (!chan) + continue; + + ctx = chan->chanctx_conf; + if (!ctx) + continue; + + phy = iwl_mld_phy_from_mac80211(ctx); + + for_each_mld_nan_valid_link(mld_vif, nan_link) { + if (nan_link->chanctx == ctx) { + cmd.per_phy[phy->fw_id].link_id = + nan_link->fw_id; + break; + } + } + + if (WARN_ON(cmd.per_phy[phy->fw_id].link_id == + FW_CTXT_ID_INVALID)) + continue; + + /* + * each channel can only appear in one map, + * upper layers enforce that + */ + if (WARN_ON(cmd.per_phy[phy->fw_id].map_id != CFG80211_NAN_INVALID_MAP_ID && + cmd.per_phy[phy->fw_id].map_id != sched->maps[i].map_id)) + continue; + + cmd.per_phy[phy->fw_id].map_id = sched->maps[i].map_id; + memcpy(cmd.per_phy[phy->fw_id].channel_entry, + chan->channel_entry, + sizeof(cmd.per_phy[phy->fw_id].channel_entry)); + cmd.per_phy[phy->fw_id].availability_map |= + cpu_to_le32(BIT(slot)); + } + } + + return iwl_mld_send_cmd(mld, &hcmd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.h b/drivers/net/wireless/intel/iwlwifi/mld/nan.h index c04d77208971..5411bca52cde 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/nan.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.h @@ -1,12 +1,31 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2025-2026 Intel Corporation */ #ifndef __iwl_mld_nan_h__ #define __iwl_mld_nan_h__ #include #include +/** + * struct iwl_mld_nan_link - struct representing a NAN link + * @chanctx: the channel context + * @active: indicates the NAN link is currently active + * @fw_id: FW link ID + */ +struct iwl_mld_nan_link { + struct ieee80211_chanctx_conf *chanctx; + bool active; + u8 fw_id; +}; + +/* Cleanup function for struct iwl_mld_nan_link, will be called in restart */ +static inline void iwl_mld_cleanup_nan_link(struct iwl_mld_nan_link *nan_link) +{ + memset(nan_link, 0, sizeof(*nan_link)); + nan_link->fw_id = FW_CTXT_ID_INVALID; +} + bool iwl_mld_nan_supported(struct iwl_mld *mld); int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -19,13 +38,33 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_handle_nan_ulw_attr_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_handle_nan_sched_update_completed_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt, u32 obj_id); +bool iwl_mld_cancel_nan_ulw_attr_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 obj_id); bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt, u32 obj_id); +bool iwl_mld_cancel_nan_sched_update_completed_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 obj_id); +void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u64 changes); + +int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_sta *sta); + +int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif); + +bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld); #endif /* __iwl_mld_nan_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c index 9c88a8579a75..7574689e4088 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mld.h" @@ -307,11 +307,8 @@ CMD_VERSIONS(tx_resp_notif, CMD_VER_ENTRY(8, iwl_tx_resp) CMD_VER_ENTRY(9, iwl_tx_resp)) CMD_VERSIONS(compressed_ba_notif, - CMD_VER_ENTRY(5, iwl_compressed_ba_notif) - CMD_VER_ENTRY(6, iwl_compressed_ba_notif) CMD_VER_ENTRY(7, iwl_compressed_ba_notif)) CMD_VERSIONS(tlc_notif, - CMD_VER_ENTRY(3, iwl_tlc_update_notif) CMD_VER_ENTRY(4, iwl_tlc_update_notif)) CMD_VERSIONS(mu_mimo_grp_notif, CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif)) @@ -330,7 +327,8 @@ CMD_VERSIONS(probe_resp_data_notif, CMD_VERSIONS(datapath_monitor_notif, CMD_VER_ENTRY(1, iwl_datapath_monitor_notif)) CMD_VERSIONS(stats_oper_notif, - CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper)) + CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper_v3) + CMD_VER_ENTRY(4, iwl_system_statistics_notif_oper)) CMD_VERSIONS(stats_oper_part1_notif, CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper)) CMD_VERSIONS(bt_coex_notif, @@ -341,8 +339,6 @@ CMD_VERSIONS(emlsr_mode_notif, CMD_VER_ENTRY(2, iwl_esr_mode_notif)) CMD_VERSIONS(emlsr_trans_fail_notif, CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif)) -CMD_VERSIONS(uapsd_misbehaving_ap_notif, - CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif)) CMD_VERSIONS(time_msmt_notif, CMD_VER_ENTRY(1, iwl_time_msmt_notify)) CMD_VERSIONS(time_sync_confirm_notif, @@ -350,7 +346,10 @@ CMD_VERSIONS(time_sync_confirm_notif, CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(10, iwl_tof_range_rsp_ntfy)) CMD_VERSIONS(beacon_filter_notif, CMD_VER_ENTRY(2, iwl_beacon_filter_notif)) CMD_VERSIONS(nan_cluster_notif, CMD_VER_ENTRY(1, iwl_nan_cluster_notif)) +CMD_VERSIONS(nan_ulw_attr_notif, CMD_VER_ENTRY(1, iwl_nan_ulw_attr_notif)) CMD_VERSIONS(nan_dw_end_notif, CMD_VER_ENTRY(1, iwl_nan_dw_end_notif)) +CMD_VERSIONS(nan_sched_update_completed_notif, + CMD_VER_ENTRY(1, iwl_nan_sched_update_completed_notif)) DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id) DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id) @@ -365,8 +364,6 @@ DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid) DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid) DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif, mac_id) -DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif, - mac_id) DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id) DEFINE_SIMPLE_CANCELLATION(beacon_filter, iwl_beacon_filter_notif, link_id) @@ -457,8 +454,6 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { emlsr_mode_notif, RX_HANDLER_ASYNC) RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF, emlsr_trans_fail_notif, RX_HANDLER_ASYNC) - RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, - uapsd_misbehaving_ap_notif) RX_HANDLER_NO_OBJECT(LEGACY_GROUP, WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION, time_msmt_notif, RX_HANDLER_SYNC) @@ -471,8 +466,12 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = { ftm_resp_notif) RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_JOINED_CLUSTER_NOTIF, nan_cluster_notif) + RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_ULW_ATTR_NOTIF, + nan_ulw_attr_notif) RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_DW_END_NOTIF, nan_dw_end_notif) + RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_SCHED_UPDATE_COMPLETED_NOTIF, + nan_sched_update_completed_notif) }; EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers); @@ -607,6 +606,11 @@ void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi, else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_QUEUES_NOTIFICATION))) iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0); +#ifdef CONFIG_PM_SLEEP + else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, + RSC_NOTIF))) + iwl_mld_handle_rsc_notif(mld, pkt, 0); +#endif else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, PHY_AIR_SNIFFER_NOTIF)) iwl_mld_handle_phy_air_sniffer_notif(mld, napi, pkt); else @@ -630,6 +634,11 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue); else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))) iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue); +#ifdef CONFIG_PM_SLEEP + else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, + RSC_NOTIF))) + iwl_mld_handle_rsc_notif(mld, pkt, queue); +#endif } void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds) @@ -685,10 +694,6 @@ void iwl_mld_cancel_async_notifications(struct iwl_mld *mld) { struct iwl_async_handler_entry *entry, *tmp; - lockdep_assert_wiphy(mld->wiphy); - - wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); - spin_lock_bh(&mld->async_handlers_lock); list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c index 1d93fb9e4dbf..59bf088ead84 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -99,9 +99,9 @@ iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width) /* Maps the driver specific control channel position (relative to the center * freq) definitions to the fw values */ -u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) +static u8 _iwl_mld_get_fw_ctrl_pos(u32 control, u32 cf1) { - int offs = chandef->chan->center_freq - chandef->center_freq1; + int offs = control - cf1; int abs_offs = abs(offs); u8 ret; @@ -127,6 +127,12 @@ u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) return ret; } +u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) +{ + return _iwl_mld_get_fw_ctrl_pos(chandef->chan->center_freq, + chandef->center_freq1); +} + int iwl_mld_phy_fw_action(struct iwl_mld *mld, struct ieee80211_chanctx_conf *ctx, u32 action) { @@ -150,6 +156,18 @@ int iwl_mld_phy_fw_action(struct iwl_mld *mld, cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap); } + /* + * Set NPCA channel if NPCA is used; if not used, just set it to an + * arbitrary channel on the other side to help firmware. + */ + if (chandef->npca_chan) + cmd.secondary_ctrl_chnl_loc = + _iwl_mld_get_fw_ctrl_pos(chandef->npca_chan->center_freq, + chandef->center_freq1); + else + cmd.secondary_ctrl_chnl_loc = + cmd.ci.ctrl_pos ^ IWL_PHY_CTRL_POS_ABOVE; + ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd); if (ret) IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index 266fe16bb95d..2387f3da5185 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -11,36 +11,11 @@ #include "link.h" #include "constants.h" -static void iwl_mld_vif_ps_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - bool *ps_enable = (bool *)data; - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - *ps_enable &= !mld_vif->ps_disabled; -} - int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3) { struct iwl_device_power_cmd cmd = {}; - bool enable_ps = false; - if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) { - enable_ps = true; - - /* Disable power save if any STA interface has - * power save turned off - */ - ieee80211_iterate_active_interfaces_mtx(mld->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mld_vif_ps_iterator, - &enable_ps); - } - - if (enable_ps) + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); @@ -113,10 +88,10 @@ static bool iwl_mld_power_is_radar(struct iwl_mld *mld, return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR; } -static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld, - struct iwl_mld_link *link, - struct iwl_mac_power_cmd *cmd, - bool ps_poll) +static void iwl_mld_power_configure_uapsd_v2(struct iwl_mld *mld, + struct iwl_mld_link *link, + struct iwl_mac_power_cmd_v2 *cmd, + bool ps_poll) { bool tid_found = false; @@ -175,10 +150,54 @@ static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld, cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len; } +static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld, + struct iwl_mld_link *link, + struct iwl_mac_power_cmd *cmd, + bool ps_poll) +{ + bool tid_found = false; + + /* set advanced pm flag with no uapsd ACs to enable ps-poll */ + if (ps_poll) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + return; + } + + for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO; + ac <= IEEE80211_AC_BK; + ac++) { + if (!link->queue_params[ac].uapsd) + continue; + + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !link->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } +} + static void iwl_mld_power_config_skip_dtim(struct iwl_mld *mld, const struct ieee80211_bss_conf *link_conf, - struct iwl_mac_power_cmd *cmd) + u8 *skip_dtim_periods, __le16 *flags) { unsigned int dtimper_tu; unsigned int dtimper; @@ -196,15 +215,15 @@ iwl_mld_power_config_skip_dtim(struct iwl_mld *mld, /* configure skip over dtim up to 900 TU DTIM interval */ skip = max_t(int, 1, 900 / dtimper_tu); - cmd->skip_dtim_periods = skip; - cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + *skip_dtim_periods = skip; + *flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); } #define POWER_KEEP_ALIVE_PERIOD_SEC 25 -static void iwl_mld_power_build_cmd(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd, - bool d3) +static void iwl_mld_power_build_cmd_v2(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd_v2 *cmd, + bool d3) { int dtimper, bi; int keep_alive; @@ -252,9 +271,7 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld, return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); - - if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2) - cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); + cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); /* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */ if (link_conf->beacon_rate && @@ -265,7 +282,9 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld, } if (d3) { - iwl_mld_power_config_skip_dtim(mld, link_conf, cmd); + iwl_mld_power_config_skip_dtim(mld, link_conf, + &cmd->skip_dtim_periods, + &cmd->flags); cmd->rx_data_timeout = cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT); cmd->tx_data_timeout = @@ -286,6 +305,95 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld, * mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd * which will look at what mac80211 is saying. */ +#ifdef CONFIG_IWLWIFI_DEBUGFS + ps_poll = mld_vif->use_ps_poll; +#endif + iwl_mld_power_configure_uapsd_v2(mld, link, cmd, ps_poll); +} + +static void iwl_mld_power_build_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd, + bool d3) +{ + int dtimper, bi; + int keep_alive; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct iwl_mld_link *link = &mld_vif->deflink; + bool ps_poll = false; + __le32 fw_id = cpu_to_le32(mld_vif->fw_id); + + if (ieee80211_vif_is_mld(vif)) { + int link_id; + + if (WARN_ON(!vif->active_links)) + return; + + /* The firmware consumes one single configuration for the vif + * and can't differentiate between links, just pick the lowest + * link_id's configuration and use that. + */ + link_id = __ffs(vif->active_links); + link_conf = link_conf_dereference_check(vif, link_id); + link = iwl_mld_link_dereference_check(mld_vif, link_id); + + if (WARN_ON(!link_conf || !link)) + return; + } + dtimper = link_conf->dtim_period; + bi = link_conf->beacon_int; + + /* Regardless of power management state the driver must set + * keep alive period. FW will use it for sending keep alive NDPs + * immediately after association. Check that keep alive period + * is at least 3 * DTIM + */ + keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), + USEC_PER_SEC); + keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); + + cmd->id_and_color = fw_id; + cmd->keep_alive_seconds = cpu_to_le16(keep_alive); + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + + if (vif->cfg.ps && iwl_mld_tdls_sta_count(mld) == 0) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); + + /* firmware supports LPRX for beacons at rate 1 Mbps or + * 6 Mbps only + */ + if (link_conf->beacon_rate && + (link_conf->beacon_rate->bitrate == 10 || + link_conf->beacon_rate->bitrate == 60)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; + } + } + + if (d3) { + iwl_mld_power_config_skip_dtim(mld, link_conf, + &cmd->skip_dtim_periods, + &cmd->flags); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT); + } else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) { + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT); + } + #ifdef CONFIG_IWLWIFI_DEBUGFS ps_poll = mld_vif->use_ps_poll; #endif @@ -295,11 +403,23 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld, int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif, bool d3) { - struct iwl_mac_power_cmd cmd = {}; + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0); - iwl_mld_power_build_cmd(mld, vif, &cmd, d3); + if (cmd_ver >= 3) { + struct iwl_mac_power_cmd cmd = {}; - return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd); + iwl_mld_power_build_cmd(mld, vif, &cmd, d3); + return iwl_mld_send_cmd_with_flags_pdu(mld, + MAC_PM_POWER_TABLE, 0, + &cmd, sizeof(cmd)); + } else { + struct iwl_mac_power_cmd_v2 cmd = {}; + + iwl_mld_power_build_cmd_v2(mld, vif, &cmd, d3); + return iwl_mld_send_cmd_with_flags_pdu(mld, + MAC_PM_POWER_TABLE, 0, + &cmd, sizeof(cmd)); + } } static void diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c index f829156d42b3..1ac85d6ce1d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -322,10 +322,10 @@ void iwl_mld_ptp_remove(struct iwl_mld *mld) mld->ptp_data.ptp_clock_info.name, ptp_clock_index(mld->ptp_data.ptp_clock)); + cancel_delayed_work_sync(&mld->ptp_data.dwork); ptp_clock_unregister(mld->ptp_data.ptp_clock); mld->ptp_data.ptp_clock = NULL; mld->ptp_data.last_gp2 = 0; mld->ptp_data.wrap_counter = 0; - cancel_delayed_work_sync(&mld->ptp_data.dwork); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index a2e586c6ea67..269439d789f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -39,8 +39,7 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld, } phy_data->phy_info = le16_to_cpu(desc->phy_info); - phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags, - mld->fw_rates_ver_3); + phy_data->rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags); phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise); phy_data->energy_a = desc->v3.energy_a; phy_data->energy_b = desc->v3.energy_b; @@ -158,7 +157,7 @@ static bool iwl_mld_used_average_energy(struct iwl_mld *mld, int link_id, guard(rcu)(); link_conf = rcu_dereference(mld->fw_id_to_bss_conf[link_id]); - if (!link_conf) + if (IS_ERR_OR_NULL(link_conf)) return false; mld_link = iwl_mld_link_from_mac80211(link_conf); @@ -1206,6 +1205,13 @@ static void iwl_mld_decode_eht_non_tb(struct iwl_mld_rx_phy_data *phy_data, iwl_mld_eht_set_ru_alloc(rx_status, le32_get_bits(phy_data->ntfy->sigs.eht.b2, OFDM_RX_FRAME_EHT_STA_RU)); + + if (phy_data->with_data) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) | + LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id, + OFDM_RX_FRAME_EHT_USER_FIELD_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID); } static void iwl_mld_decode_eht_phy_data(struct iwl_mld_rx_phy_data *phy_data, @@ -1314,14 +1320,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb, if (likely(!phy_data->ntfy)) return; - if (phy_data->with_data) { - eht->user_info[0] |= - cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) | - LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id, - OFDM_RX_FRAME_EHT_USER_FIELD_ID, - IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID); - } - iwl_mld_decode_eht_usig(phy_data, skb); iwl_mld_decode_eht_phy_data(phy_data, rx_status, eht); } @@ -2262,6 +2260,30 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld, wake_up(&mld->rxq_sync.waitq); } +#ifdef CONFIG_PM_SLEEP +void iwl_mld_handle_rsc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, int queue) +{ + const struct iwl_wowlan_all_rsc_tsc_v5 *notif = (void *)pkt->data; + u32 len = iwl_rx_packet_payload_len(pkt); + struct ieee80211_vif *bss_vif; + + if (IWL_FW_CHECK(mld, len != sizeof(*notif), + "invalid notification size %u (%zu)\n", + len, sizeof(*notif))) + return; + + /* for the bss lookup and updating the keys' pn */ + guard(rcu)(); + + bss_vif = iwl_mld_get_bss_vif(mld); + if (WARN_ON(!bss_vif)) + return; + + iwl_mld_process_rsc_notification(mld, bss_vif, notif, queue); +} +#endif /* CONFIG_PM_SLEEP */ + static void iwl_mld_no_data_rx(struct iwl_mld *mld, struct napi_struct *napi, struct iwl_rx_phy_air_sniffer_ntfy *ntfy) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h index 09dddbd40f55..573b89c3c9c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_rx_h__ #define __iwl_mld_rx_h__ @@ -61,6 +61,11 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld, struct napi_struct *napi, struct iwl_rx_packet *pkt, int queue); +#ifdef CONFIG_PM_SLEEP +void iwl_mld_handle_rsc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, int queue); +#endif + void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, struct napi_struct *napi, struct sk_buff *skb, int queue, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index b3836423e53e..d80a1cfc2ed5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -237,6 +237,12 @@ iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld, vif->type != NL80211_IFTYPE_P2P_DEVICE) return IWL_SCAN_TYPE_FRAGMENTED; + /* While associated to MLD AP with active EMLSR, set all scan + * operations as fast-balance scans. + */ + if (iwl_mld_emlsr_active(vif)) + return IWL_SCAN_TYPE_FAST_BALANCE; + /* In case of DCM with P2P GO set all scan requests as * fast-balance scan */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 4c97d12ce2d0..e18d86f021dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -13,6 +13,7 @@ #include "key.h" #include "agg.h" #include "tlc.h" +#include "nan.h" #include "fw/api/sta.h" #include "fw/api/mac.h" #include "fw/api/rx.h" @@ -43,13 +44,13 @@ int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, static void iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta, - struct ieee80211_bss_conf *link, + bool is_6ghz, __le32 *tx_ampdu_max_size, __le32 *tx_ampdu_spacing) { u32 agg_size = 0, mpdu_dens = 0; - if (WARN_ON(!link_sta || !link)) + if (WARN_ON(!link_sta)) return; /* Note that we always use only legacy & highest supported PPDUs, so @@ -63,7 +64,7 @@ iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta, mpdu_dens = link_sta->ht_cap.ampdu_density; } - if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { + if (is_6ghz) { /* overwrite HT values on 6 GHz */ mpdu_dens = le16_get_bits(link_sta->he_6ghz_capa.capa, @@ -427,8 +428,14 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, len = sizeof(struct iwl_sta_cfg_cmd_v2); cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask))); + } else if (cmd->station_type == + cpu_to_le32(STATION_TYPE_NAN_MCAST_DATA)) { + if (WARN_ON(!hweight32(le32_to_cpu(cmd->link_mask)))) + return -EINVAL; } else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) && cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) && + cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_BCAST) && + cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_MGMT) && hweight32(le32_to_cpu(cmd->link_mask)) != 1)) { return -EINVAL; } @@ -439,29 +446,61 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, return ret; } -static int -iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, - struct ieee80211_link_sta *link_sta) +static u32 iwl_mld_get_nan_link_mask(struct iwl_mld *mld) +{ + struct iwl_mld_vif *nan_dev = + iwl_mld_vif_from_mac80211(mld->nan_device_vif); + struct iwl_mld_nan_link *nan_link; + u32 link_mask = 0; + + for_each_mld_nan_valid_link(nan_dev, nan_link) + link_mask |= BIT(nan_link->fw_id); + + return link_mask; +} + +int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) { struct ieee80211_sta *sta = link_sta->sta; struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); - struct ieee80211_bss_conf *link; - struct iwl_mld_link *mld_link; struct iwl_sta_cfg_cmd cmd = {}; int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + bool is_6ghz, uora_exists; + u32 link_mask; lockdep_assert_wiphy(mld->wiphy); - link = link_conf_dereference_protected(mld_sta->vif, - link_sta->link_id); - - mld_link = iwl_mld_link_from_mac80211(link); - - if (WARN_ON(!link || !mld_link) || fw_id < 0) + if (WARN_ON(fw_id < 0)) return -EINVAL; + if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI || + mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) { + if (WARN_ON(!mld->nan_device_vif)) + return -EINVAL; + + is_6ghz = false; + uora_exists = false; + + link_mask = iwl_mld_get_nan_link_mask(mld); + } else { + struct ieee80211_bss_conf *link; + struct iwl_mld_link *mld_link; + + link = link_conf_dereference_protected(mld_sta->vif, + link_sta->link_id); + mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!link || !mld_link)) + return -EINVAL; + + link_mask = BIT(mld_link->fw_id); + is_6ghz = link->chanreq.oper.chan->band == NL80211_BAND_6GHZ; + uora_exists = link->uora_exists; + } + cmd.sta_id = cpu_to_le32(fw_id); - cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id)); + cmd.link_mask = cpu_to_le32(link_mask); cmd.station_type = cpu_to_le32(mld_sta->sta_type); memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN); @@ -499,7 +538,13 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, break; } - iwl_mld_fill_ampdu_size_and_dens(link_sta, link, + /* In NAN, there is no association request so no initial SMPS info */ + if (mld_sta->vif->type == NL80211_IFTYPE_NAN_DATA) { + cmd.mimo = cpu_to_le32(1); + cmd.mimo_protection = cpu_to_le32(0); + } + + iwl_mld_fill_ampdu_size_and_dens(link_sta, is_6ghz, &cmd.tx_ampdu_max_size, &cmd.tx_ampdu_spacing); @@ -511,7 +556,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, if (link_sta->he_cap.has_he) { cmd.trig_rnd_alloc = - cpu_to_le32(link->uora_exists ? 1 : 0); + cpu_to_le32(uora_exists ? 1 : 0); /* PPE Thresholds */ iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext); @@ -525,10 +570,29 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, cmd.ack_enabled = cpu_to_le32(1); } + if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) { + struct ieee80211_sta *nmi_sta = + wiphy_dereference(mld->wiphy, sta->nmi); + int nmi_fw_id; + + /* copy the local NDI address */ + ether_addr_copy(cmd.ndi_local_addr, mld_sta->vif->addr); + + if (WARN_ON(!nmi_sta)) + return -EINVAL; + + nmi_fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, + &nmi_sta->deflink); + if (nmi_fw_id < 0) + return -EINVAL; + + cmd.nmi_sta_id = (u8) nmi_fw_id; + } + return iwl_mld_send_sta_cmd(mld, &cmd); } -static IWL_MLD_ALLOC_FN(link_sta, link_sta) +IWL_MLD_ALLOC_FN_STATIC(link_sta, link_sta) static int iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta) @@ -759,10 +823,23 @@ int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, { struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); struct ieee80211_link_sta *link_sta; + enum iwl_fw_sta_type type; int link_id; int ret; - ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER); + switch (vif->type) { + case NL80211_IFTYPE_NAN: + type = STATION_TYPE_NAN_PEER_NMI; + break; + case NL80211_IFTYPE_NAN_DATA: + type = STATION_TYPE_NAN_PEER_NDI; + break; + default: + type = STATION_TYPE_PEER; + break; + } + + ret = iwl_mld_init_sta(mld, sta, vif, type); if (ret) return ret; @@ -1001,10 +1078,9 @@ static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld, } static int -iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld, +iwl_mld_set_internal_sta_to_fw(struct iwl_mld *mld, const struct iwl_mld_int_sta *internal_sta, - u8 fw_link_id, - const u8 *addr) + u32 link_mask, const u8 *addr) { struct iwl_sta_cfg_cmd cmd = {}; @@ -1012,20 +1088,30 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld, return iwl_mld_send_aux_sta_cmd(mld, internal_sta); cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id); - cmd.link_mask = cpu_to_le32(BIT(fw_link_id)); + cmd.link_mask = cpu_to_le32(link_mask); cmd.station_type = cpu_to_le32(internal_sta->sta_type); /* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP. * On the other hand, FW will never check this flag during RX since * an AP/GO doesn't receive protected broadcast management frames. * So, we can set it unconditionally. + * + * For NAN stations associated with a NAN Device, the MFP bit must be + * set to 1, as otherwise the FW will assert when a key associated with + * these stations would be added. */ - if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT) + if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT || + internal_sta->sta_type == STATION_TYPE_NAN_BCAST || + internal_sta->sta_type == STATION_TYPE_NAN_MGMT) cmd.mfp = cpu_to_le32(1); if (addr) { - memcpy(cmd.peer_mld_address, addr, ETH_ALEN); - memcpy(cmd.peer_link_address, addr, ETH_ALEN); + if (internal_sta->sta_type == STATION_TYPE_NAN_MCAST_DATA) { + ether_addr_copy(cmd.ndi_local_addr, addr); + } else { + memcpy(cmd.peer_mld_address, addr, ETH_ALEN); + memcpy(cmd.peer_link_address, addr, ETH_ALEN); + } } return iwl_mld_send_sta_cmd(mld, &cmd); @@ -1034,7 +1120,8 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld, static int iwl_mld_add_internal_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta, enum iwl_fw_sta_type sta_type, - u8 fw_link_id, const u8 *addr, u8 tid) + u32 link_mask, const u8 *addr, + u8 tid, bool add_txq) { int ret, queue_id; @@ -1046,11 +1133,14 @@ static int iwl_mld_add_internal_sta(struct iwl_mld *mld, internal_sta->sta_type = sta_type; - ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id, + ret = iwl_mld_set_internal_sta_to_fw(mld, internal_sta, link_mask, addr); if (ret) goto err; + if (!add_txq) + return 0; + queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid); if (queue_id < 0) { iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id); @@ -1085,8 +1175,8 @@ int iwl_mld_add_bcast_sta(struct iwl_mld *mld, return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta, STATION_TYPE_BCAST_MGMT, - mld_link->fw_id, addr, - IWL_MGMT_TID); + BIT(mld_link->fw_id), addr, + IWL_MGMT_TID, true); } int iwl_mld_add_mcast_sta(struct iwl_mld *mld, @@ -1105,14 +1195,16 @@ int iwl_mld_add_mcast_sta(struct iwl_mld *mld, return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta, STATION_TYPE_MCAST, - mld_link->fw_id, mcast_addr, 0); + BIT(mld_link->fw_id), mcast_addr, + 0, true); } int iwl_mld_add_aux_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta) { return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX, - 0, NULL, IWL_MAX_TID_COUNT); + 0, NULL, IWL_MAX_TID_COUNT, + true); } int iwl_mld_add_mon_sta(struct iwl_mld *mld, @@ -1129,23 +1221,25 @@ int iwl_mld_add_mon_sta(struct iwl_mld *mld, return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta, STATION_TYPE_BCAST_MGMT, - mld_link->fw_id, NULL, - IWL_MAX_TID_COUNT); + BIT(mld_link->fw_id), NULL, + IWL_MAX_TID_COUNT, + true); } static void iwl_mld_remove_internal_sta(struct iwl_mld *mld, struct iwl_mld_int_sta *internal_sta, bool flush, u8 tid) { - if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA || - internal_sta->queue_id == IWL_MLD_INVALID_QUEUE)) + if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA)) return; - if (flush) + if (flush && !WARN_ON_ONCE(internal_sta->queue_id == + IWL_MLD_INVALID_QUEUE)) iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id); - iwl_mld_free_txq(mld, BIT(internal_sta->sta_id), - tid, internal_sta->queue_id); + if (internal_sta->queue_id != IWL_MLD_INVALID_QUEUE) + iwl_mld_free_txq(mld, BIT(internal_sta->sta_id), + tid, internal_sta->queue_id); iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id); @@ -1346,3 +1440,82 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld, return ret; } + +int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta) +{ + const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_BCAST, + 0, bcast_addr, 0, false); +} + +int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta) +{ + return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_MGMT, + 0, NULL, IWL_MAX_TID_COUNT, true); +} + +int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld, + const u8 *ndi_addr, + struct iwl_mld_int_sta *sta) +{ + u32 link_mask = iwl_mld_get_nan_link_mask(mld); + + /* In case that there are no NAN links, nothing to do */ + if (!link_mask) + return 0; + + return iwl_mld_add_internal_sta(mld, sta, + STATION_TYPE_NAN_MCAST_DATA, + link_mask, ndi_addr, + 0, true); +} + +int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld, + const u8 *ndi_addr, + struct iwl_mld_int_sta *sta) +{ + u32 link_mask; + + if (WARN_ON(!mld->nan_device_vif)) + return -EINVAL; + + /* If the sta doesn't exist, add it */ + if (sta->sta_id == IWL_INVALID_STA) + return iwl_mld_add_nan_mcast_data_sta(mld, ndi_addr, sta); + + /* The station was already added */ + if (WARN_ON(sta->sta_type != STATION_TYPE_NAN_MCAST_DATA)) + return -EINVAL; + + link_mask = iwl_mld_get_nan_link_mask(mld); + if (link_mask) + return iwl_mld_set_internal_sta_to_fw(mld, + sta, link_mask, + ndi_addr); + + /* If no links are associated with NAN, remove the station */ + iwl_mld_remove_nan_mcast_data_sta(mld, sta); + + return 0; +} + +void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta) +{ + iwl_mld_remove_internal_sta(mld, sta, false, 0); +} + +void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta) +{ + iwl_mld_remove_internal_sta(mld, sta, true, IWL_MAX_TID_COUNT); +} + +void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta) +{ + iwl_mld_remove_internal_sta(mld, sta, true, 0); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h index 36288c2fb38c..98d693235c66 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_sta_h__ @@ -28,6 +28,9 @@ struct iwl_mld_rxq_dup_data { * This represents the link-level sta - the driver level equivalent to the * ieee80211_link_sta * + * @rx_igtk: FW can only have one IGTK for RX at a time, whereas mac80211 will + * have two. This tracks the one IGTK that currently exists in FW, to + * remove it there when a new one is installed. * @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif * @signal_avg: the signal average coming from the firmware * @in_fw: whether the link STA is uploaded to the FW (false during restart) @@ -37,6 +40,7 @@ struct iwl_mld_rxq_dup_data { struct iwl_mld_link_sta { /* Add here fields that need clean up on restart */ struct_group(zeroed_on_hw_restart, + struct ieee80211_key_conf *rx_igtk; u32 last_rate_n_flags; bool in_fw; s8 signal_avg; @@ -195,6 +199,8 @@ void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta); int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta); u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta); +int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta); int iwl_mld_update_all_link_stations(struct iwl_mld *mld, struct ieee80211_sta *sta); void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta); @@ -270,4 +276,28 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 old_links, u16 new_links); + +int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta); + +int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta); + +int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld, + const u8 *ndi_addr, + struct iwl_mld_int_sta *sta); + +int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld, + const u8 *ndi_addr, + struct iwl_mld_int_sta *sta); + +void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta); + +void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta); + +void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *sta); + #endif /* __iwl_mld_sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c index 54eb0ead78ee..e7b283cbe199 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mld.h" @@ -40,13 +40,21 @@ iwl_mld_fill_stats_from_oper_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt, u8 fw_sta_id, struct station_info *sinfo) { - const struct iwl_system_statistics_notif_oper *notif = - (void *)&pkt->data; - const struct iwl_stats_ntfy_per_sta *per_sta = - ¬if->per_sta[fw_sta_id]; + const struct iwl_stats_ntfy_per_sta *per_sta; struct ieee80211_link_sta *link_sta; struct iwl_mld_link_sta *mld_link_sta; + if (iwl_fw_lookup_notif_ver(mld->fw, STATISTICS_GROUP, + STATISTICS_OPER_NOTIF, 3) >= 4) { + const struct iwl_system_statistics_notif_oper *notif = + (void *)&pkt->data; + per_sta = ¬if->per_sta[fw_sta_id]; + } else { + const struct iwl_system_statistics_notif_oper_v3 *notif = + (void *)&pkt->data; + per_sta = ¬if->per_sta[fw_sta_id]; + } + /* 0 isn't a valid value, but FW might send 0. * In that case, set the latest non-zero value we stored */ @@ -303,6 +311,40 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, } } +static void iwl_mld_sta_stats_fill_beacon_signal_avg(struct ieee80211_vif *vif, + struct station_info *sinfo) +{ + struct ieee80211_bss_conf *link_conf; + struct iwl_mld_link *link; + u8 link_id; + + if (iwl_mld_emlsr_active(vif)) + return; + + /* TODO: support statistics for NAN */ + if (vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA) + return; + + link_id = iwl_mld_get_primary_link(vif); + link_conf = link_conf_dereference_protected(vif, link_id); + + if (WARN_ONCE(!link_conf, + "link_conf is NULL for link_id=%u\n", link_id)) + return; + + link = iwl_mld_link_from_mac80211(link_conf); + if (WARN_ONCE(!link, + "iwl_mld_link is NULL for link_id=%u\n", link_id)) + return; + + if (!link->avg_signal) + return; + + sinfo->rx_beacon_signal_avg = link->avg_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG); +} + void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -321,9 +363,9 @@ void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo); - /* TODO: NL80211_STA_INFO_BEACON_RX */ + iwl_mld_sta_stats_fill_beacon_signal_avg(vif, sinfo); - /* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */ + /* TODO: NL80211_STA_INFO_BEACON_RX */ } #define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */ @@ -435,6 +477,8 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld, fw_id++) { const struct iwl_stats_ntfy_per_link *link_stats; struct ieee80211_bss_conf *bss_conf; + struct iwl_mld_link *link; + u32 avg_raw; int sig; bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id); @@ -448,6 +492,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld, sig = -le32_to_cpu(link_stats->beacon_filter_average_energy); iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf); + link = iwl_mld_link_from_mac80211(bss_conf); + if (WARN_ON_ONCE(!link)) + continue; + + avg_raw = le32_to_cpu(link_stats->beacon_average_energy); + link->avg_signal = clamp_t(int, -(int)avg_raw, S8_MIN, 0); + /* TODO: parse more fields here (task=statistics)*/ } @@ -507,6 +558,10 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, (old_load >> 1); } + IWL_DEBUG_EHT(phy->mld, + "PHY %d: load_by_us=%u%% load_not_by_us=%u%%\n", + phy->fw_id, phy->channel_load_by_us, new_load); + iwl_mld_emlsr_check_chan_load(hw, phy, old_load); } @@ -523,17 +578,42 @@ iwl_mld_process_per_phy_stats(struct iwl_mld *mld, void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) { - const struct iwl_system_statistics_notif_oper *stats = + struct iwl_system_statistics_notif_oper *_notif __free(kfree) = NULL; + const struct iwl_system_statistics_notif_oper *notif = (void *)&pkt->data; - u32 curr_ts_usec = le32_to_cpu(stats->time_stamp); - BUILD_BUG_ON(ARRAY_SIZE(stats->per_sta) != IWL_STATION_COUNT_MAX); - BUILD_BUG_ON(ARRAY_SIZE(stats->per_link) < + BUILD_BUG_ON(ARRAY_SIZE(notif->per_sta) != IWL_STATION_COUNT_MAX); + BUILD_BUG_ON(ARRAY_SIZE(notif->per_link) < ARRAY_SIZE(mld->fw_id_to_bss_conf)); - iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec); - iwl_mld_process_per_sta_stats(mld, stats->per_sta); - iwl_mld_process_per_phy_stats(mld, stats->per_phy); + if (iwl_fw_lookup_notif_ver(mld->fw, STATISTICS_GROUP, + STATISTICS_OPER_NOTIF, 3) == 3) { + const struct iwl_system_statistics_notif_oper_v3 *stats = + (void *)&pkt->data; + _notif = kzalloc_obj(*_notif); + + if (!_notif) + return; + + _notif->time_stamp = stats->time_stamp; + for (int i = 0; i < ARRAY_SIZE(_notif->per_link); i++) + _notif->per_link[i] = stats->per_link[i]; + + BUILD_BUG_ON(sizeof(_notif->per_phy[0]) < + sizeof(stats->per_phy[0])); + for (int i = 0; i < ARRAY_SIZE(_notif->per_phy); i++) + memcpy(&_notif->per_phy[i], &stats->per_phy[i], + sizeof(stats->per_phy[i])); + for (int i = 0; i < ARRAY_SIZE(_notif->per_sta); i++) + _notif->per_sta[i] = stats->per_sta[i]; + + notif = _notif; + } + + iwl_mld_process_per_link_stats(mld, notif->per_link, + le32_to_cpu(notif->time_stamp)); + iwl_mld_process_per_sta_stats(mld, notif->per_sta); + iwl_mld_process_per_phy_stats(mld, notif->per_phy); } void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile index 36317feb923b..efa61638b8ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o +iwlmld-tests-y += chan_load_thresh.o ccflags-y += -I$(src)/../ obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c new file mode 100644 index 000000000000..87e29e09949b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2026 Intel Corporation + */ +#include +#include "mld.h" +#include "link.h" +#include "iface.h" +#include "utils.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +struct test_chan_load_case { + const char *desc; + u32 load; + enum iwl_mld_link_chan_load_level old_lvl; + enum iwl_mld_link_chan_load_level expected_lvl; + bool expected_scan_trig; +}; + +static const struct test_chan_load_case test_chan_load_thresh_cases[] = { + /* Level-up transitions */ + { + .desc = "Transition NONE->NONE", + .load = 20, + .old_lvl = LINK_CHAN_LOAD_LVL_NONE, + .expected_lvl = LINK_CHAN_LOAD_LVL_NONE, + .expected_scan_trig = false, + }, + { + .desc = "Transition NONE->LVL1", + .load = 50, + .old_lvl = LINK_CHAN_LOAD_LVL_NONE, + .expected_lvl = LINK_CHAN_LOAD_LVL1, + .expected_scan_trig = true, + }, + { + .desc = "Transition LVL1->LVL2", + .load = 75, + .old_lvl = LINK_CHAN_LOAD_LVL1, + .expected_lvl = LINK_CHAN_LOAD_LVL2, + .expected_scan_trig = true, + }, + { + .desc = "Transition LVL2->LVL3", + .load = 90, + .old_lvl = LINK_CHAN_LOAD_LVL2, + .expected_lvl = LINK_CHAN_LOAD_LVL3, + .expected_scan_trig = true, + }, + + /* Level-down transitions */ + { + .desc = "Transition LVL1->NONE", + .load = 30, + .old_lvl = LINK_CHAN_LOAD_LVL1, + .expected_lvl = LINK_CHAN_LOAD_LVL_NONE, + .expected_scan_trig = false, + }, + { + .desc = "Transition LVL2->LVL1", + .load = 60, + .old_lvl = LINK_CHAN_LOAD_LVL2, + .expected_lvl = LINK_CHAN_LOAD_LVL1, + .expected_scan_trig = false, + }, + { + .desc = "Transition LVL3->LVL2", + .load = 70, + .old_lvl = LINK_CHAN_LOAD_LVL3, + .expected_lvl = LINK_CHAN_LOAD_LVL2, + .expected_scan_trig = false, + }, + + /* No change */ + { + .desc = "Transition LVL2->LVL2", + .load = 72, + .old_lvl = LINK_CHAN_LOAD_LVL2, + .expected_lvl = LINK_CHAN_LOAD_LVL2, + .expected_scan_trig = false, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_chan_load_thresh_cases, + test_chan_load_thresh_cases, desc); + +static void test_chan_load_thresholds(struct kunit *test) +{ + const struct test_chan_load_case *tc = test->param_value; + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + struct ieee80211_bss_conf *link_conf; + struct iwl_mld_link *mld_link; + struct iwl_mld_kunit_link assoc_link = { + .id = 0, + .chandef = &chandef_6ghz_160mhz, + }; + bool scan_trig; + u32 chan_load; + + /* Setup associated non-MLO station */ + vif = iwlmld_kunit_setup_non_mlo_assoc(&assoc_link); + mld_vif = iwl_mld_vif_from_mac80211(vif); + + link_conf = &vif->bss_conf; + mld_link = &mld_vif->deflink; + + chan_load = tc->load; + mld_link->chan_load_lvl = tc->old_lvl; + + /* Execute function under test */ + wiphy_lock(mld->wiphy); + scan_trig = iwl_mld_chan_load_requires_scan(mld, link_conf, chan_load); + wiphy_unlock(mld->wiphy); + + /* Check return value */ + KUNIT_EXPECT_EQ(test, tc->expected_scan_trig, scan_trig); + + /* Check updated channel-load level */ + KUNIT_EXPECT_EQ(test, tc->expected_lvl, mld_link->chan_load_lvl); +} + +static struct kunit_case chan_load_thresh_test_cases[] = { + KUNIT_CASE_PARAM(test_chan_load_thresholds, + test_chan_load_thresh_cases_gen_params), + {} +}; + +static struct kunit_suite chan_load_thresh_test_suite = { + .name = "iwl_mld_chan_load_threshold_tests", + .init = iwlmld_kunit_test_init, + .test_cases = chan_load_thresh_test_cases, +}; + +kunit_test_suite(chan_load_thresh_test_suite); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index 766c24db3613..73c2b7b3c9de 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -2,7 +2,7 @@ /* * KUnit tests for link selection functions * - * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2025-2026 Intel Corporation */ #include @@ -13,68 +13,277 @@ #include "phy.h" #include "mlo.h" +struct link_grading_input { + u8 link_id; + const struct cfg80211_chan_def *chandef; + bool active; + s32 signal; + bool has_chan_util_elem; + u8 chan_util; + u8 chan_load_by_us; + s8 dup_beacon_adj; + s8 psd_eirp_adj; + u16 punctured; +}; + static const struct link_grading_test_case { const char *desc; - struct { - struct { - u8 link_id; - const struct cfg80211_chan_def *chandef; - bool active; - s32 signal; - bool has_chan_util_elem; - u8 chan_util; /* 0-255 , used only if has_chan_util_elem is true */ - u8 chan_load_by_us; /* 0-100, used only if active is true */; - } link; - } input; + struct link_grading_input link; unsigned int expected_grade; } link_grading_cases[] = { + /* Per-bandwidth grading table tests */ { - .desc = "channel util of 128 (50%)", - .input.link = { + .desc = "20 MHz grading table: -75 dBm", + .link = { .link_id = 0, .chandef = &chandef_2ghz_20mhz, .active = false, + .signal = -75, + }, + /* 137 * 0.7 (default 2.4GHz channel load 30%) */ + .expected_grade = 96, + }, + { + .desc = "20 MHz with channel util 128 (50%): -70 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_2ghz_20mhz, + .active = false, + .signal = -70, .has_chan_util_elem = true, .chan_util = 128, }, .expected_grade = 86, }, { - .desc = "channel util of 180 (70%)", - .input.link = { + .desc = "20 MHz with channel util 180 (70%): -70 dBm", + .link = { .link_id = 0, .chandef = &chandef_2ghz_20mhz, .active = false, + .signal = -70, .has_chan_util_elem = true, .chan_util = 180, }, .expected_grade = 51, }, { - .desc = "channel util of 180 (70%), channel load by us of 10%", - .input.link = { + .desc = "20 MHz active link with chan load by us 10%: -70 dBm", + .link = { .link_id = 0, .chandef = &chandef_2ghz_20mhz, + .active = true, + .signal = -70, .has_chan_util_elem = true, .chan_util = 180, - .active = true, .chan_load_by_us = 10, }, .expected_grade = 67, }, - { - .desc = "no channel util element", - .input.link = { + { + .desc = "40 MHz grading table: -80 dBm", + .link = { .link_id = 0, - .chandef = &chandef_2ghz_20mhz, - .active = true, + .chandef = &chandef_5ghz_40mhz, + .active = false, + .signal = -80, }, - .expected_grade = 120, + /* 206 * 0.85 (default 5GHz channel load 15%) */ + .expected_grade = 175, + }, + { + .desc = "80 MHz grading table: -70 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_80mhz, + .active = false, + .signal = -70, + }, + /* 548 * 0.85 (default 5GHz channel load 15%) */ + .expected_grade = 466, + }, + { + .desc = "160 MHz grading table: -65 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_160mhz, + .active = false, + .signal = -65, + }, + /* 1240 * 0.85 (default 5GHz channel load 15%) */ + .expected_grade = 1055, + }, + { + .desc = "320 MHz grading table: -60 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_320mhz, + .active = false, + .signal = -60, + }, + /* 3680 at -56 dBm (-60 + 4 dBm 6 GHz) */ + .expected_grade = 3680, + }, + /* 6 GHz RSSI adjustment integration tests */ + { + .desc = "6 GHz 160 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_160mhz, + .active = false, + .signal = -69, + }, + /* -69 + 4 dBm = -65, grade 1240 */ + .expected_grade = 1240, + }, + { + .desc = "6 GHz 80 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_80mhz, + .active = false, + .signal = -74, + }, + /* -74 + 4 dBm = -70, grade 548 */ + .expected_grade = 548, + }, + { + .desc = "6 GHz 40 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_40mhz, + .active = false, + .signal = -84, + }, + /* -84 + 4 dBm = -80, grade 206 */ + .expected_grade = 206, + }, + { + .desc = "6 GHz 20 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_20mhz, + .active = false, + .signal = -79, + }, + .expected_grade = 137, + }, + /* Duplicated beacon RSSI adjustment tests */ + { + .desc = "6 GHz 40 MHz dup beacon: -81 dBm + 3 dBm = -78 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_40mhz, + .active = false, + .signal = -81, + .dup_beacon_adj = 3, + }, + .expected_grade = 206, + }, + { + .desc = "6 GHz 80 MHz dup beacon: -73 dBm + 6 dBm = -67 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_80mhz, + .active = false, + .signal = -73, + .dup_beacon_adj = 6, + }, + .expected_grade = 620, + }, + { + .desc = "6 GHz 160 MHz dup beacon: -74 dBm + 9 dBm = -65 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_160mhz, + .active = false, + .signal = -74, + .dup_beacon_adj = 9, + }, + .expected_grade = 1240, + }, + { + .desc = "6 GHz 320 MHz dup beacon: -72 dBm + 12 dBm = -60 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_320mhz, + .active = false, + .signal = -72, + .dup_beacon_adj = 12, + }, + .expected_grade = 3296, + }, + /* PSD/EIRP RSSI adjustment tests */ + { + .desc = "6 GHz 80 MHz PSD/EIRP: -77 dBm + 3 dBm = -74 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_80mhz, + .active = false, + .signal = -77, + .psd_eirp_adj = 3, + }, + /* -77 + 3 dBm = -74, grade 412; fallback +4: -73 -> 548 */ + .expected_grade = 412, + }, + { + .desc = "6 GHz 160 MHz PSD/EIRP: -70 dBm + 3 dBm = -67 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_160mhz, + .active = false, + .signal = -70, + .psd_eirp_adj = 3, + }, + /* -70 + 3 dBm = -67, grade 1096; fallback +4: -66 -> 1240 */ + .expected_grade = 1096, + }, + /* Puncturing penalty tests */ + { + .desc = "80 MHz with 20 MHz punctured: 3 active subchannels", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_80mhz, + .active = false, + .signal = -70, + .punctured = 0x2, + }, + /* 548 * 0.85 (5GHz load) * 3/4 (puncturing) */ + .expected_grade = 349, + }, + { + .desc = "160 MHz with 40 MHz punctured: 6 active subchannels", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_160mhz, + .active = false, + .signal = -65, + .punctured = 0xC, + }, + /* 1240 * 0.85 (5GHz load) * 6/8 (puncturing) */ + .expected_grade = 791, }, }; KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc); +static s8 fake_dup_beacon_rssi_adjust(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + const struct link_grading_test_case *params = + kunit_get_current_test()->param_value; + + return params->link.dup_beacon_adj; +} + +static s8 fake_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) +{ + const struct link_grading_test_case *params = + kunit_get_current_test()->param_value; + + return params->link.psd_eirp_adj; +} + static void setup_link(struct ieee80211_bss_conf *link) { struct kunit *test = kunit_get_current_test(); @@ -84,14 +293,14 @@ static void setup_link(struct ieee80211_bss_conf *link) KUNIT_ALLOC_AND_ASSERT(test, link->bss); - link->bss->signal = DBM_TO_MBM(test_param->input.link.signal); + link->bss->signal = DBM_TO_MBM(test_param->link.signal); - link->chanreq.oper = *test_param->input.link.chandef; + link->chanreq.oper = *test_param->link.chandef; - if (test_param->input.link.has_chan_util_elem) { + if (test_param->link.has_chan_util_elem) { struct cfg80211_bss_ies *ies; struct ieee80211_bss_load_elem bss_load = { - .channel_util = test_param->input.link.chan_util, + .channel_util = test_param->link.chan_util, }; struct element *elem = iwlmld_kunit_gen_element(WLAN_EID_QBSS_LOAD, @@ -106,7 +315,10 @@ static void setup_link(struct ieee80211_bss_conf *link) rcu_assign_pointer(link->bss->ies, ies); } - if (test_param->input.link.active) { + if (test_param->link.punctured) + link->chanreq.oper.punctured = test_param->link.punctured; + + if (test_param->link.active) { struct ieee80211_chanctx_conf *chan_ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); struct iwl_mld_phy *phy; @@ -115,7 +327,7 @@ static void setup_link(struct ieee80211_bss_conf *link) phy = iwl_mld_phy_from_mac80211(chan_ctx); - phy->channel_load_by_us = test_param->input.link.chan_load_by_us; + phy->channel_load_by_us = test_param->link.chan_load_by_us; } } @@ -127,12 +339,11 @@ static void test_link_grading(struct kunit *test) struct ieee80211_vif *vif; struct ieee80211_bss_conf *link; unsigned int actual_grade; - /* Extract test case parameters */ - u8 link_id = test_param->input.link.link_id; - bool active = test_param->input.link.active; + u8 link_id = test_param->link.link_id; + bool active = test_param->link.active; u16 valid_links; struct iwl_mld_kunit_link assoc_link = { - .chandef = test_param->input.link.chandef, + .chandef = test_param->link.chandef, }; /* If the link is not active, use a different link as the assoc link */ @@ -146,6 +357,11 @@ static void test_link_grading(struct kunit *test) vif = iwlmld_kunit_setup_mlo_assoc(valid_links, &assoc_link); + kunit_activate_static_stub(test, iwl_mld_get_dup_beacon_rssi_adjust, + fake_dup_beacon_rssi_adjust); + kunit_activate_static_stub(test, iwl_mld_get_psd_eirp_rssi_adjust, + fake_psd_eirp_rssi_adjust); + wiphy_lock(mld->wiphy); link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); KUNIT_ASSERT_NOT_NULL(test, link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 69a0d67858bf..a4e5f2be499f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * KUnit tests for channel helper functions + * KUnit tests for link helper functions * - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -96,8 +96,478 @@ static void test_missed_beacon(struct kunit *test) /* TODO: add test cases for esr and check */ } +struct dup_beacon_test_case { + const char *desc; + enum nl80211_chan_width bandwidth; + bool has_he_oper; + bool dup_beacon_bit; + s8 expected_adj; +}; + +static const struct dup_beacon_test_case dup_beacon_cases[] = { + { + .desc = "20 MHz - no duplication", + .bandwidth = NL80211_CHAN_WIDTH_20, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 0, + }, + { + .desc = "40 MHz with duplication - 3 dB", + .bandwidth = NL80211_CHAN_WIDTH_40, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 3, + }, + { + .desc = "80 MHz with duplication - 6 dB", + .bandwidth = NL80211_CHAN_WIDTH_80, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 6, + }, + { + .desc = "160 MHz with duplication - 9 dB", + .bandwidth = NL80211_CHAN_WIDTH_160, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 9, + }, + { + .desc = "320 MHz with duplication - 12 dB", + .bandwidth = NL80211_CHAN_WIDTH_320, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 12, + }, + { + .desc = "80 MHz without dup bit - no adjustment", + .bandwidth = NL80211_CHAN_WIDTH_80, + .has_he_oper = true, + .dup_beacon_bit = false, + .expected_adj = 0, + }, + { + .desc = "80 MHz without HE oper - no adjustment", + .bandwidth = NL80211_CHAN_WIDTH_80, + .has_he_oper = false, + .dup_beacon_bit = true, + .expected_adj = 0, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_dup_beacon_rssi_adjust, dup_beacon_cases, desc); + +struct psd_eirp_test_case { + const char *desc; + const struct cfg80211_chan_def *chandef; + enum ieee80211_ap_reg_power power_type; + struct { + s8 psd_20; + s8 psd_oper; + s8 eirp_20; + s8 eirp_oper; + } local, reg; + s8 expected_adj; + struct { + bool no_psd_data; + bool no_eirp_data; + bool no_reg_psd_data; + bool has_partial_psd; + u8 psd_partial_count; + bool non_uniform_psd; + bool has_unusable_channels; + } flags; +}; + +static const struct psd_eirp_test_case psd_eirp_cases[] = { + { + .desc = "20 MHz VLP baseline - no boost expected", + .chandef = &chandef_6ghz_20mhz, + .power_type = IEEE80211_REG_VLP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 40, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 40, + }, + .expected_adj = 0, + }, + { + .desc = "40 MHz VLP - power limit prevents boost", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_VLP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .expected_adj = 0, + }, + { + .desc = "80 MHz LPI - power limit caps the boost", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + }, + { + .desc = "160 MHz LPI - power limit caps the boost", + .chandef = &chandef_6ghz_160mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .expected_adj = 3, + }, + { + .desc = "320 MHz SP - power limit caps the boost", + .chandef = &chandef_6ghz_320mhz_pri0, + .power_type = IEEE80211_REG_SP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 63, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 63, + }, + .expected_adj = 3, + }, + { + .desc = "80 MHz - EIRP prevents boost", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 20, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 20, + }, + .expected_adj = 0, + }, + { + .desc = "40 MHz - regulatory TPE sets lower limits", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 30, .psd_oper = 30, + .eirp_20 = 50, .eirp_oper = 56, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .expected_adj = 3, + }, + { + .desc = "80 MHz - PSD missing, use EIRP only", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = S8_MAX, .psd_oper = S8_MAX, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = S8_MAX, .psd_oper = S8_MAX, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 0, + .flags.no_psd_data = true, + }, + { + .desc = "80 MHz - single PSD source available", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + .flags.no_reg_psd_data = true, + }, + { + .desc = "80 MHz - partial PSD data present", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 24, .psd_oper = 24, + .eirp_20 = 40, .eirp_oper = 56, + }, + .reg = { + .psd_20 = 24, .psd_oper = 24, + .eirp_20 = 40, .eirp_oper = 56, + }, + .expected_adj = 0, + .flags.has_partial_psd = true, + .flags.psd_partial_count = 2, + }, + { + .desc = "160 MHz - different PSD per sub-channel", + .chandef = &chandef_6ghz_160mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 8, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .reg = { + .psd_20 = 8, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .expected_adj = 11, + .flags.non_uniform_psd = true, + }, + { + .desc = "80 MHz - EIRP missing, use PSD only", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = S8_MAX, .eirp_oper = S8_MAX, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = S8_MAX, .eirp_oper = S8_MAX, + }, + .expected_adj = 3, + .flags.no_eirp_data = true, + }, + { + .desc = "80 MHz - skip unusable channels in average", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + .flags.has_unusable_channels = true, + }, + { + .desc = "40 MHz - no negative adjustment", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 18, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 18, + }, + .expected_adj = 0, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_psd_eirp_rssi_adjust, psd_eirp_cases, desc); + +static void setup_psd(struct ieee80211_bss_conf *link_conf, + const struct psd_eirp_test_case *params, + int num_subchannels) +{ + int i; + + if (params->flags.no_psd_data) { + link_conf->tpe.psd_local[0].valid = false; + link_conf->tpe.psd_reg_client[0].valid = false; + link_conf->tpe.psd_local[0].count = 0; + link_conf->tpe.psd_reg_client[0].count = 0; + } else if (params->flags.no_reg_psd_data) { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = num_subchannels; + link_conf->tpe.psd_reg_client[0].valid = false; + link_conf->tpe.psd_reg_client[0].count = 0; + } else if (params->flags.has_partial_psd) { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = + params->flags.psd_partial_count; + link_conf->tpe.psd_reg_client[0].valid = true; + link_conf->tpe.psd_reg_client[0].count = + params->flags.psd_partial_count; + } else { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = num_subchannels; + link_conf->tpe.psd_reg_client[0].valid = true; + link_conf->tpe.psd_reg_client[0].count = num_subchannels; + } + + /* TPE element stores PSD limit as value * 2 */ + if (params->flags.non_uniform_psd) { + /* PSD varies per sub-channel: 10/12/10/8 dBm pattern */ + static const s8 psd_values[] = {20, 24, 20, 16, 20, 24, 20, 16, + 20, 24, 20, 16, 20, 24, 20}; + /* Set primary channel (index 0) explicitly */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Set remaining subchannels with pattern */ + for (i = 1; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = + psd_values[i - 1]; + link_conf->tpe.psd_reg_client[0].power[i] = + psd_values[i - 1]; + } + } else if (params->flags.no_psd_data) { + for (i = 0; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = S8_MAX; + link_conf->tpe.psd_reg_client[0].power[i] = S8_MAX; + } + } else if (params->flags.has_unusable_channels) { + /* Alternate usable/unusable channels for S8_MIN test */ + /* Set primary channel (index 0) explicitly */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Alternate usable/unusable for remaining subchannels */ + for (i = 1; i < num_subchannels; i++) { + if (i % 2 == 0) { + link_conf->tpe.psd_local[0].power[i] = + params->local.psd_oper * 2; + link_conf->tpe.psd_reg_client[0].power[i] = + params->reg.psd_oper * 2; + } else { + link_conf->tpe.psd_local[0].power[i] = S8_MIN; + link_conf->tpe.psd_reg_client[0].power[i] = + S8_MIN; + } + } + } else { + /* Set primary channel (index 0) separately */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Set remaining subchannels */ + for (i = 1; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = + params->local.psd_oper * 2; + link_conf->tpe.psd_reg_client[0].power[i] = + params->reg.psd_oper * 2; + } + } +} + +static void setup_eirp(struct ieee80211_bss_conf *link_conf, + const struct psd_eirp_test_case *params, + int num_subchannels) +{ + int i; + int count = ilog2(num_subchannels) + 1; + + link_conf->tpe.max_local[0].valid = !params->flags.no_eirp_data; + link_conf->tpe.max_reg_client[0].valid = !params->flags.no_eirp_data; + + if (params->flags.no_eirp_data) { + link_conf->tpe.max_local[0].count = 0; + link_conf->tpe.max_reg_client[0].count = 0; + return; + } + + link_conf->tpe.max_local[0].count = count; + link_conf->tpe.max_reg_client[0].count = count; + + /* TPE element stores EIRP limit as value * 2 */ + link_conf->tpe.max_local[0].power[0] = params->local.eirp_20 * 2; + link_conf->tpe.max_reg_client[0].power[0] = params->reg.eirp_20 * 2; + for (i = 1; i < count; i++) { + link_conf->tpe.max_local[0].power[i] = + params->local.eirp_oper * 2; + link_conf->tpe.max_reg_client[0].power[i] = + params->reg.eirp_oper * 2; + } +} + +static void test_dup_beacon_rssi_adjust(struct kunit *test) +{ + const struct dup_beacon_test_case *params = test->param_value; + struct iwl_mld *mld = test->priv; + struct ieee80211_bss_conf *link_conf; + struct cfg80211_bss *bss; + struct element *he_elem = NULL; + s8 result; + + KUNIT_ALLOC_AND_ASSERT(test, link_conf); + KUNIT_ALLOC_AND_ASSERT(test, bss); + link_conf->bss = bss; + + link_conf->chanreq.oper.chan = &chan_6ghz; + link_conf->chanreq.oper.width = params->bandwidth; + + if (params->has_he_oper) { + struct ieee80211_he_6ghz_oper he_6ghz = {}; + + if (params->dup_beacon_bit) + he_6ghz.control = + IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON; + he_elem = iwlmld_kunit_create_he_6ghz_oper(he_6ghz); + } + + rcu_assign_pointer(bss->beacon_ies, + iwlmld_kunit_create_bss_ies(he_elem)); + + guard(wiphy)(mld->wiphy); + result = iwl_mld_get_dup_beacon_rssi_adjust(mld, link_conf); + + KUNIT_EXPECT_EQ(test, result, params->expected_adj); +} + +static void test_psd_eirp_rssi_adjust(struct kunit *test) +{ + const struct psd_eirp_test_case *params = test->param_value; + struct ieee80211_bss_conf *link_conf; + int num_subchannels; + s8 result; + + KUNIT_ALLOC_AND_ASSERT(test, link_conf); + + link_conf->power_type = params->power_type; + link_conf->chanreq.oper = *params->chandef; + num_subchannels = + nl80211_chan_width_to_mhz(params->chandef->width) / 20; + + setup_psd(link_conf, params, num_subchannels); + setup_eirp(link_conf, params, num_subchannels); + + result = iwl_mld_get_psd_eirp_rssi_adjust(link_conf); + + KUNIT_EXPECT_EQ(test, result, params->expected_adj); +} + static struct kunit_case link_cases[] = { KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params), + KUNIT_CASE_PARAM(test_dup_beacon_rssi_adjust, + test_dup_beacon_rssi_adjust_gen_params), + KUNIT_CASE_PARAM(test_psd_eirp_rssi_adjust, + test_psd_eirp_rssi_adjust_gen_params), {}, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index dce747270167..fdeab7082e78 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include @@ -68,8 +68,6 @@ int iwlmld_kunit_test_init(struct kunit *test) return 0; } -static IWL_MLD_ALLOC_FN(link, bss_conf) - static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, struct ieee80211_bss_conf *link, struct iwl_mld_link *mld_link, int link_id) @@ -94,7 +92,7 @@ static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, rcu_assign_pointer(vif->link_conf[link_id], link); } -static IWL_MLD_ALLOC_FN(vif, vif) +IWL_MLD_ALLOC_FN_STATIC(vif, vif) /* Helper function to add and initialize a VIF for KUnit tests */ struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type) @@ -199,7 +197,7 @@ void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, vif->active_links |= BIT(link->link_id); } -static IWL_MLD_ALLOC_FN(link_sta, link_sta) +IWL_MLD_ALLOC_FN_STATIC(link_sta, link_sta) static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta, struct ieee80211_link_sta *link_sta, @@ -441,6 +439,66 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, return vif; } +struct element * +iwlmld_kunit_create_he_6ghz_oper(struct ieee80211_he_6ghz_oper he_6ghz) +{ + struct kunit *test = kunit_get_current_test(); + u8 *data; + size_t data_len; + size_t offset = 0; + __le32 he_oper_params; + __le16 he_mcs_nss_set = 0; + + /* Build HE Operation IE with 6 GHz info using raw buffer. + * Cannot use struct embedding because ieee80211_he_operation + * has a flexible array member (optional[]). + * + * Layout: + * 1 byte: ext_id (WLAN_EID_EXT_HE_OPERATION) + * he_oper_params: he_oper_params (from ieee80211_he_operation) + * he_mcs_nss_set: he_mcs_nss_set (from ieee80211_he_operation) + * he_6ghz: ieee80211_he_6ghz_oper (goes into optional[]) + */ + data_len = 1 + sizeof(he_oper_params) + sizeof(he_mcs_nss_set) + + sizeof(struct ieee80211_he_6ghz_oper); + + data = kunit_kzalloc(test, data_len, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, data); + + data[offset++] = WLAN_EID_EXT_HE_OPERATION; + + he_oper_params = cpu_to_le32(IEEE80211_HE_OPERATION_6GHZ_OP_INFO); + memcpy(&data[offset], &he_oper_params, sizeof(he_oper_params)); + offset += sizeof(he_oper_params); + + memcpy(&data[offset], &he_mcs_nss_set, sizeof(he_mcs_nss_set)); + offset += sizeof(he_mcs_nss_set); + + memcpy(&data[offset], &he_6ghz, sizeof(he_6ghz)); + + return iwlmld_kunit_gen_element(WLAN_EID_EXTENSION, data, data_len); +} + +struct cfg80211_bss_ies *iwlmld_kunit_create_bss_ies(struct element *elem) +{ + struct kunit *test = kunit_get_current_test(); + struct cfg80211_bss_ies *ies; + size_t ies_len = 0; + + if (elem) + ies_len = sizeof(*elem) + elem->datalen; + + ies = kunit_kzalloc(test, sizeof(*ies) + ies_len, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ies); + + ies->len = ies_len; + + if (elem) + memcpy(ies->data, elem, ies_len); + + return ies; +} + struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len) { struct kunit *test = kunit_get_current_test(); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index edf8eef4e81a..7cc8cb6eedd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_kunit_utils_h__ @@ -77,6 +77,8 @@ CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055); NL80211_CHAN_WIDTH_160) \ CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \ NL80211_CHAN_WIDTH_320) \ + CHANDEF(chandef_6ghz_320mhz_pri0, chan_6ghz, 6265, \ + NL80211_CHAN_WIDTH_320) \ CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \ NL80211_CHAN_WIDTH_160) \ /* Feel free to add more */ @@ -121,6 +123,11 @@ iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len); +struct element * +iwlmld_kunit_create_he_6ghz_oper(struct ieee80211_he_6ghz_oper he_6ghz); + +struct cfg80211_bss_ies *iwlmld_kunit_create_bss_ies(struct element *elem); + /** * iwlmld_kunit_get_phy_of_link - Get the phy of a link * diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index 78d6162d9297..b6c2fa3664cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -15,9 +15,9 @@ #include "fw/api/context.h" #include "fw/api/dhc.h" -static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta) +static u8 iwl_mld_fw_bw_from_sta_bw(enum ieee80211_sta_rx_bandwidth bandwidth) { - switch (link_sta->bandwidth) { + switch (bandwidth) { case IEEE80211_STA_RX_BW_320: return IWL_TLC_MNG_CH_WIDTH_320MHZ; case IEEE80211_STA_RX_BW_160: @@ -32,40 +32,61 @@ static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta) } } +struct iwl_mld_tlc_sta_capa { + u16 non_ht_rates; + u16 max_amsdu_len; + u8 rx_nss; + enum ieee80211_sta_rx_bandwidth bandwidth; + enum ieee80211_smps_mode smps_mode; + const struct ieee80211_sta_ht_cap *ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap; + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + const struct ieee80211_sta_uhr_cap *uhr_cap; + + const struct ieee80211_sta_he_cap *own_he_cap; + const struct ieee80211_sta_eht_cap *own_eht_cap; + const struct ieee80211_sta_uhr_cap *own_uhr_cap; +}; + static __le16 iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta, - const struct ieee80211_sta_he_cap *own_he_cap, - const struct ieee80211_sta_eht_cap *own_eht_cap, - const struct ieee80211_sta_uhr_cap *own_uhr_cap) + struct iwl_mld_tlc_sta_capa *capa) { - struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; - struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; - bool has_vht = vht_cap->vht_supported; + const struct ieee80211_sta_ht_cap *ht_cap = capa->ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap = capa->vht_cap; + const struct ieee80211_sta_he_cap *he_cap = capa->he_cap; + const struct ieee80211_sta_eht_cap *eht_cap = capa->eht_cap; + const struct ieee80211_sta_uhr_cap *uhr_cap = capa->uhr_cap; + const struct ieee80211_sta_he_cap *own_he_cap = capa->own_he_cap; + const struct ieee80211_sta_eht_cap *own_eht_cap = capa->own_eht_cap; + const struct ieee80211_sta_uhr_cap *own_uhr_cap = capa->own_uhr_cap; + bool has_vht = vht_cap && vht_cap->vht_supported; u16 flags = 0; /* STBC flags */ if (mld->cfg->ht_params.stbc && (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) { - if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & - IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) + if (he_cap && he_cap->has_he && + he_cap->he_cap_elem.phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; - else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) + else if (vht_cap && + vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; - else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) + else if (ht_cap && ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; } /* LDPC */ if (mld->cfg->ht_params.ldpc && - ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || + ((ht_cap && ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; - if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] & - IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + if (he_cap && he_cap->has_he && + (he_cap->he_cap_elem.phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; if (own_he_cap && @@ -74,7 +95,7 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; /* DCM */ - if (he_cap->has_he && + if (he_cap && he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[3] & IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && own_he_cap && @@ -85,16 +106,16 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, /* Extra EHT LTF */ if (own_eht_cap && own_eht_cap->eht_cap_elem.phy_cap_info[5] & - IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF && - link_sta->eht_cap.has_eht && - link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & - IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) { + IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF && + eht_cap && eht_cap->has_eht && + eht_cap->eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) { flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK; } - if (link_sta->uhr_cap.has_uhr && own_uhr_cap && - link_sta->uhr_cap.phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX && - own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX) + if (uhr_cap && uhr_cap->has_uhr && own_uhr_cap && + uhr_cap->phy.cap & cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX) && + own_uhr_cap->phy.cap & cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX)) flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK | IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK; @@ -114,27 +135,27 @@ static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld) return fw_chains; } -static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta) +static u8 iwl_mld_get_fw_sgi(struct iwl_mld_tlc_sta_capa *capa) { - struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; - struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + const struct ieee80211_sta_ht_cap *ht_cap = capa->ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap = capa->vht_cap; + const struct ieee80211_sta_he_cap *he_cap = capa->he_cap; u8 sgi_chwidths = 0; /* If the association supports HE, HT/VHT rates will never be used for * Tx and therefor there's no need to set the * sgi-per-channel-width-support bits */ - if (he_cap->has_he) + if (he_cap && he_cap->has_he) return 0; - if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) + if (ht_cap && ht_cap->cap & IEEE80211_HT_CAP_SGI_20) sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); - if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) + if (ht_cap && ht_cap->cap & IEEE80211_HT_CAP_SGI_40) sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); - if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) + if (vht_cap && vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); - if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) + if (vht_cap && vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); return sgi_chwidths; @@ -164,31 +185,30 @@ iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap, } static void -iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta, - const struct ieee80211_sta_vht_cap *vht_cap, +iwl_mld_fill_vht_rates(struct iwl_mld_tlc_sta_capa *capa, struct iwl_tlc_config_cmd *cmd) { u32 supp; int i, highest_mcs; - u8 max_nss = link_sta->rx_nss; + u8 max_nss = capa->rx_nss; struct ieee80211_vht_cap ieee_vht_cap = { - .vht_cap_info = cpu_to_le32(vht_cap->cap), - .supp_mcs = vht_cap->vht_mcs, + .vht_cap_info = cpu_to_le32(capa->vht_cap->cap), + .supp_mcs = capa->vht_cap->vht_mcs, }; /* the station support only a single receive chain */ - if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + if (capa->smps_mode == IEEE80211_SMPS_STATIC) max_nss = 1; for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { int nss = i + 1; - highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss); + highest_mcs = iwl_mld_get_highest_fw_mcs(capa->vht_cap, nss); if (!highest_mcs) continue; supp = BIT(highest_mcs + 1) - 1; - if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) + if (capa->bandwidth == IEEE80211_STA_RX_BW_20) supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le32(supp); @@ -196,7 +216,7 @@ iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta, * configuration is supported - only for MCS 0 since we already * decoded the MCS bits anyway ourselves. */ - if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 && + if (capa->bandwidth == IEEE80211_STA_RX_BW_160 && ieee80211_get_vht_max_nss(&ieee_vht_cap, IEEE80211_VHT_CHANWIDTH_160MHZ, 0, true, nss) >= nss) @@ -223,20 +243,19 @@ static u32 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs) } static void -iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta, - const struct ieee80211_sta_he_cap *own_he_cap, +iwl_mld_fill_he_rates(struct iwl_mld_tlc_sta_capa *capa, struct iwl_tlc_config_cmd *cmd) { - const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + const struct ieee80211_sta_he_cap *he_cap = capa->he_cap; u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); - u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80); - u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160); + u16 tx_mcs_80 = le16_to_cpu(capa->own_he_cap->he_mcs_nss_supp.tx_mcs_80); + u16 tx_mcs_160 = le16_to_cpu(capa->own_he_cap->he_mcs_nss_supp.tx_mcs_160); int i; - u8 nss = link_sta->rx_nss; + u8 nss = capa->rx_nss; /* the station support only a single receive chain */ - if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + if (capa->smps_mode == IEEE80211_SMPS_STATIC) nss = 1; for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { @@ -313,17 +332,15 @@ static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss) static void iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, - const struct ieee80211_link_sta *link_sta, - const struct ieee80211_sta_he_cap *own_he_cap, - const struct ieee80211_sta_eht_cap *own_eht_cap, + struct iwl_mld_tlc_sta_capa *capa, struct iwl_tlc_config_cmd *cmd) { /* peer RX mcs capa */ const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = - &link_sta->eht_cap.eht_mcs_nss_supp; + &capa->eht_cap->eht_mcs_nss_supp; /* our TX mcs capa */ const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs = - &own_eht_cap->eht_mcs_nss_supp; + &capa->own_eht_cap->eht_mcs_nss_supp; enum IWL_TLC_MCS_PER_BW bw; struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20; @@ -331,7 +348,7 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, /* peer is 20 MHz only */ if (vif->type == NL80211_IFTYPE_AP && - !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + !(capa->he_cap->he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { mcs_rx_20 = eht_rx_mcs->only_20mhz; } else { @@ -346,7 +363,7 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, } /* NIC is capable of 20 MHz only */ - if (!(own_he_cap->he_cap_elem.phy_cap_info[0] & + if (!(capa->own_he_cap->he_cap_elem.phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { mcs_tx_20 = eht_tx_mcs->only_20mhz; } else { @@ -402,202 +419,84 @@ iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, } /* the station support only a single receive chain */ - if (link_sta->smps_mode == IEEE80211_SMPS_STATIC || - link_sta->rx_nss < 2) + if (capa->smps_mode == IEEE80211_SMPS_STATIC || + capa->rx_nss < 2) memset(cmd->ht_rates[IWL_TLC_NSS_2], 0, sizeof(cmd->ht_rates[IWL_TLC_NSS_2])); } static void -iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta, - struct ieee80211_supported_band *sband, - const struct ieee80211_sta_he_cap *own_he_cap, - const struct ieee80211_sta_eht_cap *own_eht_cap, - const struct ieee80211_sta_uhr_cap *own_uhr_cap, +iwl_mld_fill_supp_rates(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_tlc_sta_capa *capa, struct iwl_tlc_config_cmd *cmd) { - int i; - u16 non_ht_rates = 0; - unsigned long rates_bitmap; - const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; - const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; - const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; - - /* non HT rates */ - rates_bitmap = link_sta->supp_rates[sband->band]; - for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG) - non_ht_rates |= BIT(sband->bitrates[i].hw_value); - - cmd->non_ht_rates = cpu_to_le16(non_ht_rates); + cmd->non_ht_rates = cpu_to_le16(capa->non_ht_rates); cmd->mode = IWL_TLC_MNG_MODE_NON_HT; - if (link_sta->uhr_cap.has_uhr && own_uhr_cap) { + if (capa->uhr_cap && capa->uhr_cap->has_uhr && capa->own_uhr_cap) { cmd->mode = IWL_TLC_MNG_MODE_UHR; /* * FIXME: spec currently inherits from EHT but has no * finer MCS bits. Once that's there, need to add them * to the bitmaps (and maybe copy this to UHR, or so.) */ - iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, - own_eht_cap, cmd); - } else if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) { + iwl_mld_fill_eht_rates(vif, capa, cmd); + } else if (capa->eht_cap && capa->eht_cap->has_eht && + capa->own_he_cap && capa->own_eht_cap) { cmd->mode = IWL_TLC_MNG_MODE_EHT; - iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, - own_eht_cap, cmd); - } else if (he_cap->has_he && own_he_cap) { + iwl_mld_fill_eht_rates(vif, capa, cmd); + } else if (capa->he_cap && capa->he_cap->has_he && capa->own_he_cap) { cmd->mode = IWL_TLC_MNG_MODE_HE; - iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd); - } else if (vht_cap->vht_supported) { + iwl_mld_fill_he_rates(capa, cmd); + } else if (capa->vht_cap && capa->vht_cap->vht_supported) { cmd->mode = IWL_TLC_MNG_MODE_VHT; - iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd); - } else if (ht_cap->ht_supported) { + iwl_mld_fill_vht_rates(capa, cmd); + } else if (capa->ht_cap && capa->ht_cap->ht_supported) { cmd->mode = IWL_TLC_MNG_MODE_HT; cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] = - cpu_to_le32(ht_cap->mcs.rx_mask[0]); + cpu_to_le32(capa->ht_cap->mcs.rx_mask[0]); /* the station support only a single receive chain */ - if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + if (capa->smps_mode == IEEE80211_SMPS_STATIC) cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = 0; else cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = - cpu_to_le32(ht_cap->mcs.rx_mask[1]); + cpu_to_le32(capa->ht_cap->mcs.rx_mask[1]); } } -static int iwl_mld_convert_tlc_cmd_to_v5(struct iwl_tlc_config_cmd *cmd, - struct iwl_tlc_config_cmd_v5 *cmd_v5) -{ - if (WARN_ON_ONCE(hweight32(le32_to_cpu(cmd->sta_mask)) != 1)) - return -EINVAL; - - /* Convert sta_mask to sta_id */ - cmd_v5->sta_id = __ffs(le32_to_cpu(cmd->sta_mask)); - - /* Copy all the rest */ - cmd_v5->max_ch_width = cmd->max_ch_width; - cmd_v5->mode = cmd->mode; - cmd_v5->chains = cmd->chains; - cmd_v5->sgi_ch_width_supp = cmd->sgi_ch_width_supp; - cmd_v5->flags = cmd->flags; - cmd_v5->non_ht_rates = cmd->non_ht_rates; - - BUILD_BUG_ON(sizeof(cmd_v5->ht_rates) != sizeof(cmd->ht_rates)); - memcpy(cmd_v5->ht_rates, cmd->ht_rates, sizeof(cmd->ht_rates)); - - cmd_v5->max_mpdu_len = cmd->max_mpdu_len; - cmd_v5->max_tx_op = cmd->max_tx_op; - - return 0; -} - -static int iwl_mld_convert_tlc_cmd_to_v4(struct iwl_tlc_config_cmd *cmd, - struct iwl_tlc_config_cmd_v4 *cmd_v4) -{ - if (WARN_ON_ONCE(hweight32(le32_to_cpu(cmd->sta_mask)) != 1)) - return -EINVAL; - - /* Convert sta_mask to sta_id */ - cmd_v4->sta_id = __ffs(le32_to_cpu(cmd->sta_mask)); - - /* Copy everything until ht_rates */ - cmd_v4->max_ch_width = cmd->max_ch_width; - cmd_v4->mode = cmd->mode; - cmd_v4->chains = cmd->chains; - cmd_v4->sgi_ch_width_supp = cmd->sgi_ch_width_supp; - cmd_v4->flags = cmd->flags; - cmd_v4->non_ht_rates = cmd->non_ht_rates; - - /* Convert ht_rates from __le32 to __le16 */ - BUILD_BUG_ON(ARRAY_SIZE(cmd_v4->ht_rates) != ARRAY_SIZE(cmd->ht_rates)); - BUILD_BUG_ON(ARRAY_SIZE(cmd_v4->ht_rates[0]) != ARRAY_SIZE(cmd->ht_rates[0])); - - for (int nss = 0; nss < ARRAY_SIZE(cmd->ht_rates); nss++) - for (int bw = 0; bw < ARRAY_SIZE(cmd->ht_rates[nss]); bw++) - cmd_v4->ht_rates[nss][bw] = - cpu_to_le16(le32_to_cpu(cmd->ht_rates[nss][bw])); - - /* Copy the rest */ - cmd_v4->max_mpdu_len = cmd->max_mpdu_len; - cmd_v4->max_tx_op = cmd->max_tx_op; - - return 0; -} - static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta, - struct ieee80211_bss_conf *link) + struct iwl_mld_sta *mld_sta, + int fw_sta_mask, int phy_id, + struct iwl_mld_tlc_sta_capa *capa) { - struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); - struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); - enum nl80211_band band = link->chanreq.oper.chan->band; - struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band]; - const struct ieee80211_sta_he_cap *own_he_cap = - ieee80211_get_he_iftype_cap_vif(sband, vif); - const struct ieee80211_sta_eht_cap *own_eht_cap = - ieee80211_get_eht_iftype_cap_vif(sband, vif); - const struct ieee80211_sta_uhr_cap *own_uhr_cap = - ieee80211_get_uhr_iftype_cap_vif(sband, vif); struct iwl_tlc_config_cmd cmd = { /* For AP mode, use 20 MHz until the STA is authorized */ .max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ? - iwl_mld_fw_bw_from_sta_bw(link_sta) : + iwl_mld_fw_bw_from_sta_bw(capa->bandwidth) : IWL_TLC_MNG_CH_WIDTH_20MHZ, - .flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta, - own_he_cap, own_eht_cap, - own_uhr_cap), + .flags = iwl_mld_get_tlc_cmd_flags(mld, capa), .chains = iwl_mld_get_fw_chains(mld), - .sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta), - .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len), + .sgi_ch_width_supp = iwl_mld_get_fw_sgi(capa), + .max_mpdu_len = cpu_to_le16(capa->max_amsdu_len), }; - int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD); u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0); - struct ieee80211_chanctx_conf *chan_ctx; - struct iwl_tlc_config_cmd_v5 cmd_v5 = {}; - struct iwl_tlc_config_cmd_v4 cmd_v4 = {}; void *cmd_ptr; u8 cmd_size; - u32 phy_id; int ret; - if (fw_sta_id < 0) - return; - - cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id)); - - if (WARN_ON_ONCE(!mld_link)) - return; - - chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx); - if (WARN_ON(!chan_ctx)) - return; - - phy_id = iwl_mld_phy_from_mac80211(chan_ctx)->fw_id; + cmd.sta_mask = cpu_to_le32(fw_sta_mask); cmd.phy_id = cpu_to_le32(phy_id); - iwl_mld_fill_supp_rates(mld, vif, link_sta, sband, - own_he_cap, own_eht_cap, - own_uhr_cap, &cmd); + iwl_mld_fill_supp_rates(mld, vif, capa, &cmd); if (cmd_ver == 6) { cmd_ptr = &cmd; cmd_size = sizeof(cmd); - } else if (cmd_ver == 5) { - /* TODO: remove support once FW moves to version 6 */ - ret = iwl_mld_convert_tlc_cmd_to_v5(&cmd, &cmd_v5); - if (ret) - return; - cmd_ptr = &cmd_v5; - cmd_size = sizeof(cmd_v5); - } else if (cmd_ver == 4) { - ret = iwl_mld_convert_tlc_cmd_to_v4(&cmd, &cmd_v4); - if (ret) - return; - cmd_ptr = &cmd_v4; - cmd_size = sizeof(cmd_v4); } else { IWL_ERR(mld, "Unsupported TLC config cmd version %d\n", cmd_ver); @@ -641,16 +540,32 @@ int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data) return ret; } -void iwl_mld_config_tlc_link(struct iwl_mld *mld, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *link_conf, - struct ieee80211_link_sta *link_sta) +static void _iwl_mld_config_tlc_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *chan_ctx, + u32 fw_sta_mask, + struct ieee80211_link_sta *link_sta, + struct iwl_mld_tlc_sta_capa *capa) { struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct ieee80211_supported_band *sband; + unsigned long rates_bitmap; + enum nl80211_band band; + int i; - if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) + /* + * Note: Due to NAN, the chan_ctx here might not be the same as the + * vif->links[link_sta->link_id] one (which is NULL in NAN), so some + * care is needed in this function (and the 'capa' exists to make it + * less error-prone, the other functions need not worry about it.) + */ + + if (WARN_ON(!chan_ctx)) return; + band = chan_ctx->def.chan->band; + sband = mld->hw->wiphy->bands[band]; + /* Before we have information about a station, configure the A-MSDU RC * limit such that iwlmd and mac80211 would not be allowed to build * A-MSDUs. @@ -660,7 +575,21 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld, ieee80211_sta_recalc_aggregates(link_sta->sta); } - iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf); + /* non HT rates */ + rates_bitmap = link_sta->supp_rates[sband->band]; + for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG) + capa->non_ht_rates |= BIT(sband->bitrates[i].hw_value); + + capa->max_amsdu_len = link_sta->agg.max_amsdu_len; + capa->ht_cap = &link_sta->ht_cap; + capa->vht_cap = &link_sta->vht_cap; + capa->he_cap = &link_sta->he_cap; + capa->eht_cap = &link_sta->eht_cap; + capa->uhr_cap = &link_sta->uhr_cap; + + iwl_mld_send_tlc_cmd(mld, vif, mld_sta, fw_sta_mask, + iwl_mld_phy_from_mac80211(chan_ctx)->fw_id, + capa); } void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif, @@ -706,6 +635,136 @@ void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif, } } +static void iwl_mld_config_tlc_nan(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + int fw_sta_id) +{ + struct ieee80211_nan_peer_sched *sched; + struct iwl_mld_nan_link *nan_link; + struct ieee80211_sta *nmi, *iter; + struct iwl_mld_vif *nan_mld_vif; + u32 fw_sta_mask; + + nmi = wiphy_dereference(mld->wiphy, link_sta->sta->nmi); + if (WARN_ON(!nmi)) + return; + + sched = nmi->nan_sched; + + /* This sta might not be in the list yet as it is just getting added */ + fw_sta_mask = BIT(fw_sta_id); + for_each_station(iter, mld->hw) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(iter); + struct ieee80211_sta *iter_nmi; + + iter_nmi = wiphy_dereference(mld->wiphy, iter->nmi); + + if (iter_nmi == nmi) + fw_sta_mask |= BIT(mld_sta->deflink.fw_id); + } + + if (WARN_ON(!mld->nan_device_vif)) + return; + + nan_mld_vif = iwl_mld_vif_from_mac80211(mld->nan_device_vif); + + for_each_mld_nan_valid_link(nan_mld_vif, nan_link) { + struct ieee80211_chanctx_conf *chan_ctx; + struct iwl_mld_tlc_sta_capa capa = {}; + struct cfg80211_chan_def *chandef; + bool common_channel = false; + + chan_ctx = nan_link->chanctx; + if (!chan_ctx) + continue; + + chandef = iwl_mld_get_chandef_from_chanctx(mld, chan_ctx); + + capa.smps_mode = IEEE80211_SMPS_OFF; /* always off */ + + capa.rx_nss = 2; /* maximum we support */ + capa.bandwidth = ieee80211_chan_width_to_rx_bw(chandef->width); + + for (int j = 0; j < (sched ? sched->n_channels : 0); j++) { + enum ieee80211_sta_rx_bandwidth rx_bw; + enum nl80211_chan_width width; + int chains; + + if (sched->channels[j].chanctx_conf != chan_ctx) + continue; + + common_channel = true; + + width = sched->channels[j].chanreq.oper.width; + rx_bw = ieee80211_chan_width_to_rx_bw(width); + capa.bandwidth = min(capa.bandwidth, rx_bw); + + chains = sched->channels[j].needed_rx_chains; + capa.rx_nss = min(capa.rx_nss, chains); + } + + /* Apply minimum parameters if there is no common channel */ + if (!common_channel) { + capa.bandwidth = IEEE80211_STA_RX_BW_20; + capa.rx_nss = 1; + } + + capa.own_he_cap = &mld->wiphy->nan_capa.phy.he; + /* no EHT/UHR for NAN */ + + _iwl_mld_config_tlc_link(mld, vif, chan_ctx, fw_sta_mask, + link_sta, &capa); + } +} + +void iwl_mld_config_tlc_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + struct ieee80211_chanctx_conf *chan_ctx; + struct ieee80211_supported_band *sband; + struct iwl_mld_tlc_sta_capa capa = {}; + struct cfg80211_chan_def *chandef; + enum nl80211_band band; + + if (fw_sta_id < 0) + return; + + if (vif->type == NL80211_IFTYPE_NAN) + return; + + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + iwl_mld_config_tlc_nan(mld, vif, link_sta, fw_sta_id); + return; + } + + if (WARN_ON_ONCE(!mld_link)) + return; + + chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx); + if (WARN_ON_ONCE(!chan_ctx)) + return; + + chandef = &iwl_mld_phy_from_mac80211(chan_ctx)->chandef; + band = chandef->chan->band; + sband = mld->hw->wiphy->bands[band]; + + capa.smps_mode = link_sta->smps_mode; + capa.rx_nss = link_sta->rx_nss; + capa.bandwidth = ieee80211_chan_width_to_rx_bw(chandef->width); + capa.bandwidth = min(capa.bandwidth, link_sta->bandwidth); + capa.own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + capa.own_eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif); + capa.own_uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, vif); + + _iwl_mld_config_tlc_link(mld, vif, chan_ctx, BIT(fw_sta_id), + link_sta, &capa); +} + void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { @@ -714,6 +773,7 @@ void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, lockdep_assert_wiphy(mld->wiphy); + /* Note: for NAN this is only the mac80211-level deflink */ for_each_vif_active_link(vif, link, link_id) { struct ieee80211_link_sta *link_sta = link_sta_dereference_check(sta, link_id); @@ -822,9 +882,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, if (WARN_ON(!mld_link_sta)) return; - mld_link_sta->last_rate_n_flags = - iwl_v3_rate_from_v2_v3(notif->rate, - mld->fw_rates_ver_3); + mld_link_sta->last_rate_n_flags = le32_to_cpu(notif->rate); rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), mld_link_sta->last_rate_n_flags); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index 0bcb1ae69468..2185dade95f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 - 2025 Intel Corporation + * Copyright (C) 2024 - 2026 Intel Corporation */ #include @@ -71,14 +71,19 @@ static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) { u8 tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : txq->tid; u32 fw_sta_mask = iwl_mld_fw_sta_id_mask(mld, txq->sta); - /* We can't know when the station is asleep or awake, so we - * must disable the queue hang detection. - */ - unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ? - IWL_WATCHDOG_DISABLED : - mld->trans->mac_cfg->base->wd_timeout; + unsigned int watchdog_timeout; int queue, size; + switch (txq->vif->type) { + case NL80211_IFTYPE_AP: /* STA might go to PS */ + case NL80211_IFTYPE_NAN_DATA: /* peer might ULW/break schedule */ + watchdog_timeout = IWL_WATCHDOG_DISABLED; + break; + default: + watchdog_timeout = mld->trans->mac_cfg->base->wd_timeout; + break; + } + lockdep_assert_wiphy(mld->wiphy); if (tid == IWL_MGMT_TID) @@ -346,7 +351,8 @@ u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld, iwl_mld_get_basic_rates_and_band(mld, vif, info, &basic_rates, &band); if (band >= NUM_NL80211_BANDS) { - WARN_ON(vif->type != NL80211_IFTYPE_NAN); + WARN_ON(vif->type != NL80211_IFTYPE_NAN && + vif->type != NL80211_IFTYPE_NAN_DATA); return IWL_FIRST_OFDM_RATE; } @@ -489,7 +495,7 @@ static __le32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, rate = iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | iwl_mld_get_tx_ant(mld, info, sta, fc); - return iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3); + return cpu_to_le32(rate); } static void @@ -552,10 +558,13 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, flags |= IWL_TX_FLAGS_ENCRYPT_DIS; /* For data and mgmt packets rate info comes from the fw. - * Only set rate/antenna for injected frames with fixed rate, or - * when no sta is given. + * Only set rate/antenna for: + * - injected frames with fixed rate, + * - when no sta is given. + * - frames that are sent to an NMI sta, which is only used for management. */ if (unlikely(!sta || + (mld_sta && mld_sta->vif->type == NL80211_IFTYPE_NAN) || info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) { flags |= IWL_TX_FLAGS_CMD_RATE; rate_n_flags = iwl_mld_get_tx_rate_n_flags(mld, info, sta, @@ -673,11 +682,16 @@ iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq, WARN_ON(!ieee80211_is_mgmt(fc)); return mld_vif->aux_sta.queue_id; case NL80211_IFTYPE_NAN: - mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); - WARN_ON(!ieee80211_is_mgmt(fc)); + return iwl_mld_nan_get_mgmt_queue(mld, info->control.vif); + case NL80211_IFTYPE_NAN_DATA: + WARN_ON(!ieee80211_is_data(fc)); - return mld_vif->aux_sta.queue_id; + if (!iwl_mld_nan_use_nan_stations(mld)) + break; + + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + return mld_vif->nan.mcast_data_sta.queue_id; default: WARN_ONCE(1, "Unsupported vif type\n"); break; @@ -1018,14 +1032,11 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) rcu_read_unlock(); } -static void iwl_mld_hwrate_to_tx_rate(struct iwl_mld *mld, - __le32 rate_n_flags_fw, +static void iwl_mld_hwrate_to_tx_rate(u32 rate_n_flags, struct ieee80211_tx_info *info) { enum nl80211_band band = info->band; struct ieee80211_tx_rate *tx_rate = &info->status.rates[0]; - u32 rate_n_flags = iwl_v3_rate_from_v2_v3(rate_n_flags_fw, - mld->fw_rates_ver_3); u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK; u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; @@ -1156,7 +1167,8 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL); } - iwl_mld_hwrate_to_tx_rate(mld, tx_resp->initial_rate, info); + iwl_mld_hwrate_to_tx_rate(le32_to_cpu(tx_resp->initial_rate), + info); if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1))) ieee80211_tx_status_skb(mld->hw, skb); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index 58e9a940024d..0812522edea0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2012-2014, 2020 Intel Corporation * Copyright (C) 2016 Intel Deutschland GmbH - * Copyright (C) 2022, 2024 Intel Corporation + * Copyright (C) 2022, 2024, 2026 Intel Corporation */ #include #include "fw-api.h" @@ -76,6 +76,9 @@ static void iwl_mvm_iface_iterator(void *_data, u8 *mac, if (vif == data->ignore_vif) return; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE && !mvmvif->p2p_in_binding) + return; + if (mvmvif->deflink.phy_ctxt != data->phyctxt) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 1b67836b1fac..3a14ca5e512a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -261,20 +261,28 @@ iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm, switch (peer->chandef.width) { case NL80211_CHAN_WIDTH_20_NOHT: - *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; - *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_20: - *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; - *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_40: - *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; - *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_80: - *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; - *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_160: cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, @@ -282,8 +290,10 @@ iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm, IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver >= 13) { - *format_bw = IWL_LOCATION_FRAME_FORMAT_HE; - *format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; } fallthrough; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index 83f6e508a094..ae7a163c81c9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2024, 2026 Intel Corporation */ #include #include @@ -54,27 +54,37 @@ static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef, { switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: - *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; - *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_20: - *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; - *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); break; case NL80211_CHAN_WIDTH_40: - *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; - *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); break; case NL80211_CHAN_WIDTH_80: - *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; - *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); break; case NL80211_CHAN_WIDTH_160: if (cmd_ver >= 9) { - *format_bw = IWL_LOCATION_FRAME_FORMAT_HE; - *format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS; + *format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE, + IWL_LOCATION_FMT_BW_FORMAT); + *format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ, + IWL_LOCATION_FMT_BW_BANDWIDTH); *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); break; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 8ffa72aca3cf..d6a8624b1ae5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -935,9 +935,9 @@ u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) : IWL_MAC_BEACON_CCK_V1; if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) - flags |= iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); + flags |= iwl_mvm_rate_idx_to_fw_idx(fw, rate_idx); else - flags |= iwl_fw_rate_idx_to_plcp(rate_idx); + flags |= iwl_mvm_rate_idx_to_plcp(rate_idx); return flags; } @@ -998,7 +998,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, else rate_n_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; - rate_n_flags |= iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate); + rate_n_flags |= iwl_mvm_rate_idx_to_fw_idx(mvm->fw, rate); tx_params->rate_n_flags = iwl_mvm_v3_rate_to_fw(rate_n_flags, mvm->fw_rates_ver); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 1ec9807e4827..74bd4038fd56 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-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -263,10 +263,10 @@ static const u8 tm_if_types_ext_capa_sta[] = { */ #define IWL_MVM_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \ - IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \ - __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ + IEEE80211_EML_CAP_EML_PADDING_DELAY_32US << \ + __bf_shf(IEEE80211_EML_CAP_EML_PADDING_DELAY) | \ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ - __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) + __bf_shf(IEEE80211_EML_CAP_EML_TRANSITION_DELAY)) #define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ @@ -1104,6 +1104,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, spin_unlock_bh(&mvm->time_event_lock); mvmvif->roc_activity = ROC_NUM_ACTIVITIES; + mvmvif->p2p_in_binding = false; mvmvif->bf_enabled = false; mvmvif->ba_enabled = false; @@ -3144,7 +3145,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, &vif->bss_conf)) IWL_WARN(mvm, "Failed updating beacon data\n"); - if (changes & BSS_CHANGED_FTM_RESPONDER) { + if ((changes & BSS_CHANGED_FTM_RESPONDER) && bss_conf->ftm_responder) { int ret = iwl_mvm_ftm_start_responder(mvm, vif, &vif->bss_conf); if (ret) @@ -4634,6 +4635,7 @@ static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id) static int iwl_mvm_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; lockdep_assert_held(&mvm->mutex); @@ -4642,10 +4644,18 @@ static int iwl_mvm_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN(ret, "Failed binding P2P_DEVICE\n")) return ret; + mvmvif->p2p_in_binding = true; + /* The station and queue allocation must be done only after the binding * is done, as otherwise the FW might incorrectly configure its state. */ - return iwl_mvm_add_p2p_bcast_sta(mvm, vif); + ret = iwl_mvm_add_p2p_bcast_sta(mvm, vif); + if (ret) { + iwl_mvm_binding_remove_vif(mvm, vif); + mvmvif->p2p_in_binding = false; + } + + return ret; } static int iwl_mvm_roc(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index f1dbfeae20bc..6ea9a7c8da0c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022-2025 Intel Corporation + * Copyright (C) 2022-2026 Intel Corporation */ #include "mvm.h" @@ -729,7 +729,7 @@ iwl_mvm_mld_link_info_changed_ap_ibss(struct iwl_mvm *mvm, IWL_WARN(mvm, "Failed updating beacon data\n"); /* FIXME: need to decide if we need FTM responder per link */ - if (changes & BSS_CHANGED_FTM_RESPONDER) { + if (changes & BSS_CHANGED_FTM_RESPONDER && link_conf->ftm_responder) { int ret = iwl_mvm_ftm_start_responder(mvm, vif, link_conf); if (ret) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 402ba5dee8b2..683cac56822c 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-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -383,6 +383,8 @@ struct iwl_mvm_vif_link_info { * @pm_enabled: indicates powersave is enabled * @roc_activity: currently running ROC activity for this vif (or * ROC_NUM_ACTIVITIES if no activity is running). + * @p2p_in_binding: indicates that this P2P-Device interface should be + * added to the binding, i.e. is running ROC right now * @session_prot_connection_loss: the connection was lost due to session * protection ending without receiving a beacon, so we need to now * protect the deauth separately @@ -469,7 +471,7 @@ struct iwl_mvm_vif { struct dentry *dbgfs_slink; struct iwl_dbgfs_pm dbgfs_pm; struct iwl_dbgfs_bf dbgfs_bf; - struct iwl_mac_power_cmd mac_pwr_cmd; + struct iwl_mac_power_cmd_v2 mac_pwr_cmd; int dbgfs_quota_min; bool ftm_unprotected; #endif @@ -492,6 +494,7 @@ struct iwl_mvm_vif { struct iwl_mvm_time_event_data time_event_data; struct iwl_mvm_time_event_data hs_time_event_data; enum iwl_roc_activity roc_activity; + bool p2p_in_binding; /* TCP Checksum Offload */ netdev_features_t features; @@ -1646,6 +1649,7 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm); int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm); /* Utils */ +u8 iwl_mvm_rate_idx_to_plcp(int idx); int iwl_mvm_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band); int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, @@ -1653,7 +1657,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, enum nl80211_band band, struct ieee80211_tx_rate *r); -u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx); +u8 iwl_mvm_rate_idx_to_fw_idx(const struct iwl_fw *fw, int rate_idx); u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -1669,16 +1673,16 @@ void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, int clock_type, u32 *gp2, u32 iwl_mvm_get_systime(struct iwl_mvm *mvm); /* Tx / Host Commands */ -int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd); -int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data); -int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd, - u32 *status); -int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, - u16 len, const void *data, - u32 *status); +int iwl_mvm_send_cmd(struct iwl_mvm *mvm, + struct iwl_host_cmd *cmd); +int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, + u32 flags, u16 len, const void *data); +int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, + struct iwl_host_cmd *cmd, + u32 *status); +int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, + u16 len, const void *data, + u32 *status); int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta); int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 384bed95835d..2297392db955 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -335,7 +335,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_NOTIF, iwl_mvm_handle_rx_system_oper_stats, RX_HANDLER_ASYNC_LOCKED_WIPHY, - struct iwl_system_statistics_notif_oper), + struct iwl_system_statistics_notif_oper_v3), RX_HANDLER_GRP(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF, iwl_mvm_handle_rx_system_oper_part1_stats, RX_HANDLER_ASYNC_LOCKED, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 610de29b7be0..46792c508753 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -83,7 +83,7 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, } static void iwl_mvm_power_log(struct iwl_mvm *mvm, - struct iwl_mac_power_cmd *cmd) + struct iwl_mac_power_cmd_v2 *cmd) { IWL_DEBUG_POWER(mvm, "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n", @@ -121,7 +121,7 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd) + struct iwl_mac_power_cmd_v2 *cmd) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); enum ieee80211_ac_numbers ac; @@ -296,7 +296,7 @@ static bool iwl_mvm_power_is_radar(struct ieee80211_bss_conf *link_conf) static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd) + struct iwl_mac_power_cmd_v2 *cmd) { struct ieee80211_bss_conf *link_conf; unsigned int min_link_skip = ~0; @@ -344,7 +344,7 @@ static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd) + struct iwl_mac_power_cmd_v2 *cmd) { int dtimper, bi; int keep_alive; @@ -466,7 +466,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - struct iwl_mac_power_cmd cmd = {}; + struct iwl_mac_power_cmd_v2 cmd = {}; iwl_mvm_power_build_cmd(mvm, vif, &cmd); iwl_mvm_power_log(mvm, &cmd); @@ -717,7 +717,7 @@ int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, int bufsz) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_power_cmd cmd = {}; + struct iwl_mac_power_cmd_v2 cmd = {}; int pos = 0; mutex_lock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index bcd6f7cead2a..f39eb48864eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -326,11 +326,11 @@ void iwl_mvm_ptp_remove(struct iwl_mvm *mvm) mvm->ptp_data.ptp_clock_info.name, ptp_clock_index(mvm->ptp_data.ptp_clock)); + cancel_delayed_work_sync(&mvm->ptp_data.dwork); ptp_clock_unregister(mvm->ptp_data.ptp_clock); mvm->ptp_data.ptp_clock = NULL; memset(&mvm->ptp_data.ptp_clock_info, 0, sizeof(mvm->ptp_data.ptp_clock_info)); mvm->ptp_data.last_gp2 = 0; - cancel_delayed_work_sync(&mvm->ptp_data.dwork); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index d0c0faae0122..269c4b45de80 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -757,8 +757,9 @@ iwl_mvm_update_tcm_from_stats(struct iwl_mvm *mvm, __le32 *air_time_le, spin_unlock(&mvm->tcm.lock); } -static void iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm, - struct iwl_stats_ntfy_per_phy *per_phy) +static void +iwl_mvm_handle_per_phy_stats(struct iwl_mvm *mvm, + struct iwl_stats_ntfy_per_phy_v1 *per_phy) { int i; @@ -937,7 +938,7 @@ void iwl_mvm_handle_rx_system_oper_stats(struct iwl_mvm *mvm, { u8 average_energy[IWL_STATION_COUNT_MAX]; struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_system_statistics_notif_oper *stats; + struct iwl_system_statistics_notif_oper_v3 *stats; int i; u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, STATISTICS_GROUP, STATISTICS_OPER_NOTIF, 0); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 2b52a4f3bff9..1692b6e75f57 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH */ @@ -88,6 +88,7 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm) } else { iwl_mvm_rm_p2p_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); + mvmvif->p2p_in_binding = false; } /* Do not remove the PHY context as removing and adding diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index bca13417e82c..dc69c71faa76 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -268,7 +268,6 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, int rate_idx) { u32 rate_flags = 0; - u8 rate_plcp; bool is_cck; /* if the rate isn't a well known legacy rate, take the lowest one */ @@ -277,8 +276,9 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, info, info->control.vif); - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate_idx); + /* Get FW rate for tx_cmd->rate_n_flags */ + rate_flags |= iwl_mvm_rate_idx_to_fw_idx(mvm->fw, rate_idx); + is_cck = (rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE); @@ -288,7 +288,7 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, else rate_flags |= RATE_MCS_MOD_TYPE_CCK; - return (u32)rate_plcp | rate_flags; + return rate_flags; } static u32 iwl_mvm_get_inject_tx_rate(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index f052537e9567..2e12f93ad32b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -14,6 +14,30 @@ #include "fw/api/rs.h" #include "fw/img.h" +#define IWL_DECLARE_RATE_INFO(r) \ + [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP + +/* Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP */ +static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1), + IWL_DECLARE_RATE_INFO(2), + IWL_DECLARE_RATE_INFO(5), + IWL_DECLARE_RATE_INFO(11), + IWL_DECLARE_RATE_INFO(6), + IWL_DECLARE_RATE_INFO(9), + IWL_DECLARE_RATE_INFO(12), + IWL_DECLARE_RATE_INFO(18), + IWL_DECLARE_RATE_INFO(24), + IWL_DECLARE_RATE_INFO(36), + IWL_DECLARE_RATE_INFO(48), + IWL_DECLARE_RATE_INFO(54), +}; + +u8 iwl_mvm_rate_idx_to_plcp(int idx) +{ + return fw_rate_idx_to_plcp[idx]; +} + /* * Will return 0 even if the cmd failed when RFKILL is asserted unless * CMD_WANT_SKB is set in cmd->flags. @@ -151,13 +175,13 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, if (band != NL80211_BAND_2GHZ) band_offset = IWL_FIRST_OFDM_RATE; for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) - if (iwl_fw_rate_idx_to_plcp(idx) == rate) + if (iwl_mvm_rate_idx_to_plcp(idx) == rate) return idx - band_offset; return -1; } -u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx) +u8 iwl_mvm_rate_idx_to_fw_idx(const struct iwl_fw *fw, int rate_idx) { return rate_idx >= IWL_FIRST_OFDM_RATE ? rate_idx - IWL_FIRST_OFDM_RATE : @@ -1243,7 +1267,7 @@ static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags) int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE; for (idx = offset; idx < last; idx++) - if (iwl_fw_rate_idx_to_plcp(idx) == rate) + if (iwl_mvm_rate_idx_to_plcp(idx) == rate) return idx - offset; return IWL_RATE_INVALID; } @@ -1353,7 +1377,7 @@ __le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver) rate_idx = u32_get_bits(rate, RATE_LEGACY_RATE_MSK); if (!(result & RATE_MCS_CCK_MSK_V1)) rate_idx += IWL_FIRST_OFDM_RATE; - result |= u32_encode_bits(iwl_fw_rate_idx_to_plcp(rate_idx), + result |= u32_encode_bits(iwl_mvm_rate_idx_to_plcp(rate_idx), RATE_LEGACY_RATE_MSK_V1); break; case RATE_MCS_MOD_TYPE_HT: diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index eb3c5a6dd088..7a7b10152e5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -537,6 +537,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0xA840, 0x4314, iwl_bz_mac_cfg)}, {IWL_PCI_DEVICE(0xA840, 0x1775, iwl_bz_mac_cfg)}, {IWL_PCI_DEVICE(0xA840, 0x1776, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1735, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1736, iwl_bz_mac_cfg)}, {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_mac_cfg)}, {IWL_PCI_DEVICE(0x4D40, PCI_ANY_ID, iwl_bz_mac_cfg)}, @@ -1014,6 +1016,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { /* GF RF */ IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_name, RF_TYPE(GF)), + IWL_DEV_INFO(iwl_rf_ot, iwl_ax231_name, RF_TYPE(GF), SUBDEV(0x0294)), IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_name, RF_TYPE(GF), CDB), IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_name, DEVICE(0x2725)), @@ -1052,6 +1055,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750i_name, SUBDEV(0x1772)), IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790s_name, SUBDEV(0x1791)), IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790i_name, SUBDEV(0x1792)), + IWL_DEV_INFO(iwl_rf_fm_160mhz, iwl_killer_be1730x_name, SUBDEV(0x1730)), + IWL_DEV_INFO(iwl_rf_fm_160mhz, iwl_killer_be1730x_name, SUBDEV(0x1731)), /* Killer discrete */ IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750w_name, @@ -1069,7 +1074,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { /* PE RF */ IWL_DEV_INFO(iwl_rf_pe, iwl_bn201_name, RF_TYPE(PE)), - IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE), + IWL_DEV_INFO(iwl_rf_pe_no_uhr, iwl_be223_name, RF_TYPE(PE), SUBDEV_MASKED(0x0524, 0xFFF)), IWL_DEV_INFO(iwl_rf_pe, iwl_bn203_name, RF_TYPE(PE), SUBDEV_MASKED(0x0324, 0xFFF)), @@ -1077,6 +1082,8 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { /* Killer */ IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775s_name, SUBDEV(0x1776)), IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775i_name, SUBDEV(0x1775)), + IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_killer_be1735x_name, SUBDEV(0x1735)), + IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_killer_be1735x_name, SUBDEV(0x1736)), IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850w2_name, SUBDEV(0x1851)), IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850i_name, SUBDEV(0x1852)), diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 7b7b35e442f9..d84c7c1efee1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2003-2015, 2018-2025 Intel Corporation + * Copyright (C) 2003-2015, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -287,6 +287,106 @@ enum iwl_pcie_imr_status { IMR_D2S_ERROR, }; +/* + * The FH will write back to the first TB only, so we need to copy some data + * into the buffer regardless of whether it should be mapped or not. + * This indicates how big the first TB must be to include the scratch buffer + * and the assigned PN. + * Since PN location is 8 bytes at offset 12, it's 20 now. + * If we make it bigger then allocations will be bigger and copy slower, so + * that's probably not useful. + */ +#define IWL_FIRST_TB_SIZE 20 +#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64) + +struct iwl_pcie_txq_entry { + void *cmd; + struct sk_buff *skb; + /* buffer to free after command completes */ + const void *free_buf; + struct iwl_cmd_meta meta; +}; + +struct iwl_pcie_first_tb_buf { + u8 buf[IWL_FIRST_TB_SIZE_ALIGN]; +}; + +/** + * struct iwl_txq - Tx Queue for DMA + * @tfds: transmit frame descriptors (DMA memory) + * @first_tb_bufs: start of command headers, including scratch buffers, for + * the writeback -- this is DMA memory and an array holding one buffer + * for each command on the queue + * @first_tb_dma: DMA address for the first_tb_bufs start + * @entries: transmit entries (driver state) + * @lock: queue lock + * @reclaim_lock: reclaim lock + * @stuck_timer: timer that fires if queue gets stuck + * @trans: pointer back to transport (for timer) + * @need_update: indicates need to update read/write index + * @ampdu: true if this queue is an ampdu queue for a specific RA/TID + * @wd_timeout: queue watchdog timeout (jiffies) - per queue + * @frozen: tx stuck queue timer is frozen + * @frozen_expiry_remainder: remember how long until the timer fires + * @block: queue is blocked + * @bc_tbl: byte count table of the queue (relevant only for gen2 transport) + * @write_ptr: 1-st empty entry (index) host_w + * @read_ptr: last used entry (index) host_r + * @dma_addr: physical addr for BDs + * @n_window: safe queue window + * @id: queue id + * @low_mark: low watermark, resume queue if free space more than this + * @high_mark: high watermark, stop queue if free space less than this + * @overflow_q: overflow queue for handling frames that didn't fit on HW queue + * @overflow_tx: need to transmit from overflow + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + * + * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware + * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless + * there might be HW changes in the future). For the normal TX + * queues, n_window, which is the size of the software queue data + * is also 256; however, for the command queue, n_window is only + * 32 since we don't need so many commands pending. Since the HW + * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. + * This means that we end up with the following: + * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | + * SW entries: | 0 | ... | 31 | + * where N is a number between 0 and 7. This means that the SW + * data is a window overlaid over the HW queue. + */ +struct iwl_txq { + void *tfds; + struct iwl_pcie_first_tb_buf *first_tb_bufs; + dma_addr_t first_tb_dma; + struct iwl_pcie_txq_entry *entries; + /* lock for syncing changes on the queue */ + spinlock_t lock; + /* lock to prevent concurrent reclaim */ + spinlock_t reclaim_lock; + unsigned long frozen_expiry_remainder; + struct timer_list stuck_timer; + struct iwl_trans *trans; + bool need_update; + bool frozen; + bool ampdu; + int block; + unsigned long wd_timeout; + struct sk_buff_head overflow_q; + struct iwl_dma_ptr bc_tbl; + + int write_ptr; + int read_ptr; + dma_addr_t dma_addr; + int n_window; + u32 id; + int low_mark; + int high_mark; + + bool overflow_tx; +}; + /** * struct iwl_pcie_txqs - TX queues data * @@ -1086,6 +1186,8 @@ u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg); void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val); int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, void *buf, int dwords); +int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords); int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership); struct iwl_trans_dump_data * iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, @@ -1101,6 +1203,7 @@ void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); +void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans); void __releases(nic_access_nobh) iwl_trans_pcie_release_nic_access(struct iwl_trans *trans); void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); @@ -1153,6 +1256,9 @@ int iwl_trans_pcie_copy_imr(struct iwl_trans *trans, int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue, struct iwl_trans_rxq_dma_data *data); +int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd); + static inline bool iwl_pcie_gen1_is_pm_supported(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c index 64262bcca55d..f41cbe62e7aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-trans.h" #include "iwl-prph.h" @@ -327,6 +327,13 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) else pos = scnprintf(buf, buflen, "WH"); break; + case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_PE): + if (SILICON_Z_STEP == + CSR_HW_RFID_STEP(trans->info.hw_rf_id)) + pos = scnprintf(buf, buflen, "PETC"); + else + pos = scnprintf(buf, buflen, "PE"); + break; default: return; } @@ -369,6 +376,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 step_reg; iwl_pcie_reset_ict(trans); @@ -397,10 +405,13 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans) iwl_pcie_get_rf_name(trans); mutex_unlock(&trans_pcie->mutex); - if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) - trans->step_urm = !!(iwl_read_prph(trans, - CNVI_PMU_STEP_FLOW) & - CNVI_PMU_STEP_FLOW_FORCE_URM); + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + return; + + step_reg = trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC ? + CNVI_PMU_STEP_FLOW_SC : CNVI_PMU_STEP_FLOW_BZ; + trans->step_urm = !!(iwl_read_prph(trans, step_reg) & + CNVI_PMU_STEP_FLOW_FORCE_URM); } static bool iwl_pcie_set_ltr(struct iwl_trans *trans) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index a05f60f9224b..28b276c65952 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2007-2015, 2018-2024 Intel Corporation + * Copyright (C) 2007-2015, 2018-2024, 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -141,10 +141,10 @@ static void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans, return; if (power != max_power) - IWL_ERR(trans, - "Sorry - debug buffer is only %luK while you requested %luK\n", - (unsigned long)BIT(power - 10), - (unsigned long)BIT(max_power - 10)); + IWL_INFO(trans, + "Sorry - debug buffer is only %luK while you requested %luK\n", + (unsigned long)BIT(power - 10), + (unsigned long)BIT(max_power - 10)); fw_mon->block = block; fw_mon->physical = physical; @@ -521,7 +521,6 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans) #define HW_READY_TIMEOUT (50) -/* Note: returns poll_bit return value, which is >= 0 if success */ static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) { int ret; @@ -2044,7 +2043,7 @@ iwl_trans_pcie_call_prod_reset_dsm(struct pci_dev *pdev, u16 cmd, u16 value) 0xDD, 0x26, 0xB5, 0xFD); if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &dsm_guid, ACPI_DSM_REV, - DSM_INTERNAL_FUNC_PRODUCT_RESET)) + BIT(DSM_INTERNAL_FUNC_PRODUCT_RESET))) return ERR_PTR(-ENODEV); return iwl_acpi_get_dsm_object(&pdev->dev, ACPI_DSM_REV, @@ -2424,6 +2423,15 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) return false; } +void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_unlock_bh(&trans_pcie->reg_lock); + cond_resched(); + spin_lock_bh(&trans_pcie->reg_lock); +} + void __releases(nic_access_nobh) iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) { @@ -2506,6 +2514,51 @@ int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, return 0; } +int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); +#define IWL_MAX_HW_ERRS 5 + unsigned int num_consec_hw_errors = 0; + u32 offs = 0; + u32 *vals = buf; + + lockdep_assert_held(&trans_pcie->reg_lock); + + while (offs < dwords) { + /* limit the time we spin here under lock to 1/2s */ + unsigned long end = jiffies + HZ / 2; + bool resched = false; + + iwl_write32(trans, HBUS_TARG_MEM_RADDR, + addr + 4 * offs); + + while (offs < dwords) { + vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + + if (iwl_trans_is_hw_error_value(vals[offs])) + num_consec_hw_errors++; + else + num_consec_hw_errors = 0; + + if (num_consec_hw_errors >= IWL_MAX_HW_ERRS) + return -EIO; + + offs++; + + if (time_after(jiffies, end)) { + resched = true; + break; + } + } + + if (resched) + iwl_trans_pcie_resched_with_nic_access(trans); + } + + return 0; +} + int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val) { @@ -2537,7 +2590,7 @@ int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) struct iwl_txq *txq; unsigned long now = jiffies; bool overflow_tx; - u8 wr_ptr; + int wr_ptr; /* Make sure the NIC is still alive in the bus */ if (test_bit(STATUS_TRANS_DEAD, &trans->status)) @@ -2560,7 +2613,7 @@ int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) overflow_tx) && !time_after(jiffies, now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { - u8 write_ptr = READ_ONCE(txq->write_ptr); + int write_ptr = READ_ONCE(txq->write_ptr); /* * If write pointer moved during the wait, warn only @@ -3201,8 +3254,7 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file, return -EINVAL; trans->request_top_reset = 1; } - iwl_op_mode_nic_error(trans->op_mode, IWL_ERR_TYPE_DEBUGFS); - iwl_trans_schedule_reset(trans, IWL_ERR_TYPE_DEBUGFS); + iwl_trans_fw_error(trans, IWL_ERR_TYPE_DEBUGFS); return count; } @@ -4000,6 +4052,30 @@ static void get_crf_id(struct iwl_trans *iwl_trans, else sd_reg_ver_addr = SD_REG_VER; + /* wait until the device is ready to access the prph registers */ + if (iwl_trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_DR || + iwl_trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_SC) { + u32 req = iwl_read_umac_prph_no_grab(iwl_trans, + WFPM_RSRCS_4PHS_REQ_STTS); + int ret; + + if (!(req & RSRC_REQ_CNVR_TOP)) { + IWL_ERR(iwl_trans, + "WFPM_RSRCS_4PHS_REQ_STTS bit 6 is clear 0x%x\n", + req); + return; + } + + ret = iwl_poll_umac_prph_bits_no_grab(iwl_trans, + WFPM_RSRCS_4PHS_ACK_STTS, + RSRC_ACK_CNVR_TOP, + RSRC_ACK_CNVR_TOP, + 50 * 1000); + if (ret < 0) + IWL_ERR(iwl_trans, + "WFPM_RSRCS_4PHS_ACK_STTS bit 6 is clear\n"); + } + /* Enable access to peripheral registers */ val = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG); val |= WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK; diff --git a/drivers/net/wireless/intersil/p54/p54spi.c b/drivers/net/wireless/intersil/p54/p54spi.c index 9d66dcae54e0..d18be2545028 100644 --- a/drivers/net/wireless/intersil/p54/p54spi.c +++ b/drivers/net/wireless/intersil/p54/p54spi.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -15,7 +16,7 @@ #include #include #include -#include +#include #include #include "p54spi.h" @@ -30,19 +31,6 @@ MODULE_FIRMWARE("3826.arm"); MODULE_FIRMWARE("3826.eeprom"); -/* gpios should be handled in board files and provided via platform data, - * but because it's currently impossible for p54spi to have a header file - * in include/linux, let's use module parameters for now - */ - -static int p54spi_gpio_power = 97; -module_param(p54spi_gpio_power, int, 0444); -MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line"); - -static int p54spi_gpio_irq = 87; -module_param(p54spi_gpio_irq, int, 0444); -MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line"); - static void p54spi_spi_read(struct p54s_priv *priv, u8 address, void *buf, size_t len) { @@ -262,14 +250,14 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev) static void p54spi_power_off(struct p54s_priv *priv) { - disable_irq(gpio_to_irq(p54spi_gpio_irq)); - gpio_set_value(p54spi_gpio_power, 0); + disable_irq(priv->irq); + gpiod_set_value(priv->gpio_powerdown, 1); } static void p54spi_power_on(struct p54s_priv *priv) { - gpio_set_value(p54spi_gpio_power, 1); - enable_irq(gpio_to_irq(p54spi_gpio_irq)); + gpiod_set_value(priv->gpio_powerdown, 0); + enable_irq(priv->irq); /* need to wait a while before device can be accessed, the length * is just a guess @@ -608,31 +596,20 @@ static int p54spi_probe(struct spi_device *spi) goto err_free; } - ret = gpio_request(p54spi_gpio_power, "p54spi power"); - if (ret < 0) { - dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret); + priv->gpio_powerdown = gpiod_get_optional(&spi->dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpio_powerdown)) { + ret = dev_err_probe(&priv->spi->dev, PTR_ERR(priv->gpio_powerdown), + "powerdown GPIO request failed\n"); goto err_free; } - ret = gpio_request(p54spi_gpio_irq, "p54spi irq"); + ret = request_irq(spi->irq, p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi", priv->spi); if (ret < 0) { - dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret); + dev_err(&priv->spi->dev, "request_irq() failed"); goto err_free_gpio_power; } - gpio_direction_output(p54spi_gpio_power, 0); - gpio_direction_input(p54spi_gpio_irq); - - ret = request_irq(gpio_to_irq(p54spi_gpio_irq), - p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi", - priv->spi); - if (ret < 0) { - dev_err(&priv->spi->dev, "request_irq() failed"); - goto err_free_gpio_irq; - } - - irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING); - + priv->irq = spi->irq; INIT_WORK(&priv->work, p54spi_work); init_completion(&priv->fw_comp); INIT_LIST_HEAD(&priv->tx_pending); @@ -659,11 +636,9 @@ static int p54spi_probe(struct spi_device *spi) err_free_common: release_firmware(priv->firmware); - free_irq(gpio_to_irq(p54spi_gpio_irq), spi); -err_free_gpio_irq: - gpio_free(p54spi_gpio_irq); + free_irq(priv->irq, spi); err_free_gpio_power: - gpio_free(p54spi_gpio_power); + gpiod_put(priv->gpio_powerdown); err_free: p54_free_common(priv->hw); return ret; @@ -675,10 +650,8 @@ static void p54spi_remove(struct spi_device *spi) p54_unregister_common(priv->hw); - free_irq(gpio_to_irq(p54spi_gpio_irq), spi); - - gpio_free(p54spi_gpio_power); - gpio_free(p54spi_gpio_irq); + free_irq(priv->irq, spi); + gpiod_put(priv->gpio_powerdown); release_firmware(priv->firmware); mutex_destroy(&priv->mutex); @@ -686,10 +659,16 @@ static void p54spi_remove(struct spi_device *spi) p54_free_common(priv->hw); } +static const struct of_device_id p54spi_of_ids[] = { + { .compatible = "st,stlc4560", }, + { }, +}; +MODULE_DEVICE_TABLE(of, p54spi_of_ids); static struct spi_driver p54spi_driver = { .driver = { .name = "p54spi", + .of_match_table = p54spi_of_ids, }, .probe = p54spi_probe, diff --git a/drivers/net/wireless/intersil/p54/p54spi.h b/drivers/net/wireless/intersil/p54/p54spi.h index e5619a13fd61..118785cc635a 100644 --- a/drivers/net/wireless/intersil/p54/p54spi.h +++ b/drivers/net/wireless/intersil/p54/p54spi.h @@ -107,6 +107,9 @@ struct p54s_priv { enum fw_state fw_state; const struct firmware *firmware; + + struct gpio_desc *gpio_powerdown; + int irq; }; #endif /* P54SPI_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index c9a651bdf882..c9daf893472f 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -304,7 +304,8 @@ static int mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie) + unsigned int duration, u64 *cookie, + const u8 *rx_addr) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); int ret; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 70ce31d7c76e..623ddde8c8e5 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1568,28 +1568,23 @@ int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data, return -EINVAL; } - if (start_raw) { - while ((*pos != '\r' && *pos != '\n') && - (token = strsep((char **)&pos, " "))) { - if (ptr - hostcmd->cmd >= - MWIFIEX_SIZE_OF_CMD_BUFFER) { - mwifiex_dbg( - adapter, ERROR, - "%s: hostcmd is larger than %d, aborting\n", - __func__, MWIFIEX_SIZE_OF_CMD_BUFFER); - return -ENOMEM; - } - - ret = kstrtou8(token, 16, ptr); - if (ret < 0) { - mwifiex_dbg( - adapter, ERROR, - "%s: failed to parse hostcmd %d token: %s\n", - __func__, ret, token); - return ret; - } - ptr++; + while ((*pos != '\r' && *pos != '\n') && + (token = strsep((char **)&pos, " "))) { + if (ptr - hostcmd->cmd >= MWIFIEX_SIZE_OF_CMD_BUFFER) { + mwifiex_dbg(adapter, ERROR, + "%s: hostcmd is larger than %d, aborting\n", + __func__, MWIFIEX_SIZE_OF_CMD_BUFFER); + return -ENOMEM; } + + ret = kstrtou8(token, 16, ptr); + if (ret < 0) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to parse hostcmd %d token: %s\n", + __func__, ret, token); + return ret; + } + ptr++; } } diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 05eee64706ea..6edcb3b8f279 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -307,7 +307,7 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); - kfree(mlink); + kfree_rcu(mlink, rcu_head); } void mt76_roc_complete(struct mt76_phy *phy) diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index afdb73661866..b99d7452800f 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -181,7 +181,7 @@ mt76_eeprom_override(struct mt76_phy *phy) if (err == -EPROBE_DEFER) return err; - if (!is_valid_ether_addr(phy->macaddr)) { + if (err) { eth_random_addr(phy->macaddr); dev_info(dev->dev, "Invalid MAC address, using random address %pM\n", diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 4ae5e4715a9c..13c4e8abe281 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -449,6 +449,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL); + if (!wiphy->max_remain_on_channel_duration) + wiphy->max_remain_on_channel_duration = 5000; if (!wiphy->available_antennas_tx) wiphy->available_antennas_tx = phy->antenna_mask; if (!wiphy->available_antennas_rx) @@ -1321,7 +1323,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb) * All further fragments will be validated by mac80211 only. */ if (ieee80211_is_frag(hdr) && - !ieee80211_is_first_frag(hdr->frame_control)) + !ieee80211_is_first_frag(hdr->seq_ctrl)) return; } @@ -1576,6 +1578,7 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, { struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; struct mt76_dev *dev = phy->dev; + struct mt76_wcid *published; int ret; int i; @@ -1595,11 +1598,19 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, mtxq->wcid = wcid->idx; } - ewma_signal_init(&wcid->rssi); - rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + published = rcu_dereference_protected(dev->wcid[wcid->idx], + lockdep_is_held(&dev->mutex)); + if (published != wcid) { + WARN_ON_ONCE(published); + ewma_signal_init(&wcid->rssi); + rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + mt76_wcid_init(wcid, phy->band_idx); + } else { + wcid->phy_idx = phy->band_idx; + } + phy->num_sta++; - mt76_wcid_init(wcid, phy->band_idx); out: mutex_unlock(&dev->mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 527bef97e122..07955555f84d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -361,6 +361,7 @@ enum mt76_wcid_flags { MT_WCID_FLAG_PS, MT_WCID_FLAG_4ADDR, MT_WCID_FLAG_HDR_TRANS, + MT_WCID_FLAG_TDLS_PEER, }; #define MT76_N_WCIDS 1088 diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c index 0f7b20152279..6a1475e3c899 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c @@ -550,8 +550,6 @@ int mt7615_init_debugfs(struct mt7615_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); - if (!dir) - return -ENOMEM; if (is_mt7615(&dev->mt76)) debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", dir, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index 42e11ba1206e..e437e088b2e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -195,7 +195,6 @@ mt7615_check_offload_capability(struct mt7615_dev *dev) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); wiphy->flags &= ~WIPHY_FLAG_4ADDR_STATION; - wiphy->max_remain_on_channel_duration = 5000; wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c index d91feffadda9..bab7b91f14be 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c @@ -151,7 +151,6 @@ static int mt7663u_probe(struct usb_interface *usb_intf, return -ENOMEM; dev = container_of(mdev, struct mt7615_dev, mt76); - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(usb_intf, dev); @@ -193,7 +192,6 @@ static int mt7663u_probe(struct usb_interface *usb_intf, 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); @@ -211,7 +209,6 @@ static void mt7663u_disconnect(struct usb_interface *usb_intf) mt7663u_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 51423c7740bd..2aa6078993e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -172,11 +172,26 @@ struct mt76_connac_tx_free { extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; +static inline bool is_connac3(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927; +} + static inline bool is_mt7925(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7925; } +static inline bool is_mt7927(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7927; +} + +static inline bool is_320mhz_supported(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7927; +} + static inline bool is_mt7920(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7920; @@ -279,6 +294,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev) case 0x7922: case 0x7902: case 0x7925: + case 0x7927: case 0x7663: case 0x7622: return false; @@ -420,7 +436,10 @@ mt76_connac_mutex_release(struct mt76_dev *dev, struct mt76_connac_pm *pm) void mt76_connac_gen_ppe_thresh(u8 *he_ppet, int nss, enum nl80211_band band); int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, int ring_base, void *wed, u32 flags); - +void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power); +s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *limits); void mt76_connac_write_hw_txp(struct mt76_dev *dev, struct mt76_tx_info *tx_info, void *txp_ptr, u32 id); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 0339e2e7ab60..fc9f782032ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -173,7 +173,7 @@ void mt76_connac_write_hw_txp(struct mt76_dev *dev, txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID); - if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev)) + if (is_mt7663(dev) || is_connac2(dev) || is_connac3(dev)) last_mask = MT_TXD_LEN_LAST; else last_mask = MT_TXD_LEN_AMSDU_LAST | @@ -217,7 +217,7 @@ mt76_connac_txp_skb_unmap_hw(struct mt76_dev *dev, u32 last_mask; int i; - if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev)) + if (is_mt7663(dev) || is_connac2(dev) || is_connac3(dev)) last_mask = MT_TXD_LEN_LAST; else last_mask = MT_TXD_LEN_MSDU_LAST; @@ -275,6 +275,15 @@ int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, } EXPORT_SYMBOL_GPL(mt76_connac_init_tx_queues); +void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power) +{ + int delta; + + delta = mt76_tx_power_path_delta(hweight16(phy->chainmask)); + phy->txpower_cur = max_power - delta; +} +EXPORT_SYMBOL_GPL(mt76_connac_set_txpower_cur); + #define __bitrate_mask_check(_mcs, _mode) \ ({ \ u8 i = 0; \ diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 89bd52ea8bf7..6596c9e198f4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -67,7 +67,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || (is_connac2(dev) && addr == 0x900000) || - (is_mt7925(dev) && (addr == 0x900000 || addr == 0xe0002800)) || + (is_connac3(dev) && (addr == 0x900000 || addr == 0xe0002800)) || (is_mt799x(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else @@ -491,6 +491,11 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, htr->to_ds = true; htr->from_ds = true; } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + htr->to_ds = false; + htr->from_ds = false; + } } EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_hdr_trans_tlv); @@ -1227,6 +1232,9 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, len = enable ? sizeof(dev_req) : sizeof(basic_req); err = mt76_mcu_send_msg(dev, cmd, data, len, true); + if (err && cmd == MCU_UNI_CMD(BSS_INFO_UPDATE)) + err = mt76_connac_mcu_bss_deact_err(dev, err, enable); + if (err < 0) return err; @@ -1234,7 +1242,11 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, data = enable ? (void *)&basic_req : (void *)&dev_req; len = enable ? sizeof(basic_req) : sizeof(dev_req); - return mt76_mcu_send_msg(dev, cmd, data, len, true); + err = mt76_mcu_send_msg(dev, cmd, data, len, true); + if (err && cmd == MCU_UNI_CMD(BSS_INFO_UPDATE)) + err = mt76_connac_mcu_bss_deact_err(dev, err, enable); + + return err; } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_dev); @@ -2223,14 +2235,14 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, .hw_value = ch_list[idx], .band = band, }; - s8 reg_power, sar_power; + s8 max_power; - reg_power = mt76_connac_get_ch_power(phy, &chan, - tx_power); - sar_power = mt76_get_sar_power(phy, &chan, reg_power); - - mt76_get_rate_power_limits(phy, &chan, limits, - sar_power); + max_power = mt76_connac_get_rate_power_limit(phy, &chan, + limits); + if (phy->chandef.chan && + phy->chandef.chan->hw_value == ch_list[idx] && + phy->chandef.chan->band == band) + mt76_connac_set_txpower_cur(phy, max_power); tx_power_tlv.last_msg = ch_list[idx] == last_ch; sku_tlbv.channel = ch_list[idx]; @@ -2962,6 +2974,23 @@ int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_rdd_cmd); +s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *limits) +{ + s8 reg_power, sar_power; + int tx_power; + + tx_power = 2 * phy->hw->conf.power_level; + if (!tx_power) + tx_power = 127; + + reg_power = mt76_connac_get_ch_power(phy, chan, tx_power); + sar_power = mt76_get_sar_power(phy, chan, reg_power); + return mt76_get_rate_power_limits(phy, chan, limits, sar_power); +} +EXPORT_SYMBOL_GPL(mt76_connac_get_rate_power_limit); + static int mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, const struct mt76_connac2_fw_trailer *hdr, @@ -3084,7 +3113,7 @@ static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info) { u32 mode = DL_MODE_NEED_RSP; - if ((!is_connac2(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT) + if ((!is_connac2(dev) && !is_connac3(dev)) || info == PATCH_SEC_NOT_SUPPORT) return mode; switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index ac5126ab68ff..78f633ad81a0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1069,6 +1069,7 @@ enum { MCU_UNI_EVENT_SCAN_DONE = 0x0e, MCU_UNI_EVENT_RDD_REPORT = 0x11, MCU_UNI_EVENT_ROC = 0x27, + MCU_UNI_EVENT_MBMC = 0x28, MCU_UNI_EVENT_TX_DONE = 0x2d, MCU_UNI_EVENT_THERMAL = 0x35, MCU_UNI_EVENT_RSSI_MONITOR = 0x41, @@ -1598,7 +1599,7 @@ struct mt76_connac_hw_scan_done { u8 pno_enabled; u8 pad2[3]; u8 sparse_channel_valid_num; - u8 pad3[3]; + u8 alpha2[3]; u8 channel_num[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; /* idle format for channel_idle_time * 0: first bytes: idle time(ms) 2nd byte: dwell time(ms) @@ -1611,6 +1612,7 @@ struct mt76_connac_hw_scan_done { u8 mdrdy_count[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; __le32 beacon_2g_num; __le32 beacon_5g_num; + __le16 channel_scan_time[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; } __packed; struct mt76_connac_sched_scan_req { @@ -1878,7 +1880,7 @@ mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa) ret |= feature_set & FW_FEATURE_SET_ENCRYPT ? DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0; - if (is_connac2(dev) || is_mt7925(dev)) + if (is_connac2(dev) || is_connac3(dev)) ret |= feature_set & FW_FEATURE_ENCRY_MODE ? DL_CONFIG_ENCRY_MODE_SEL : 0; ret |= FIELD_PREP(DL_MODE_KEY_IDX, @@ -1906,6 +1908,29 @@ mt76_connac_mcu_get_wlan_idx(struct mt76_dev *dev, struct mt76_wcid *wcid, } } +#define MT76_CONNAC_MCU_STATUS_WLAN_FAILURE 0xc0000001 + +static inline int +mt76_connac_mcu_bss_deact_err(struct mt76_dev *mdev, int err, bool enable) +{ + if (err != (int)MT76_CONNAC_MCU_STATUS_WLAN_FAILURE) + return err; + + /* Ignore wlan_failure state false alarm when deactivating an + * inactive network. It does not harm the firmware state. + */ + if (!enable) { + dev_dbg(mdev->dev, + "ignore wlan_failure when bss is deactivated\n"); + return 0; + } + + dev_warn(mdev->dev, + "wlan_failure when bss is activated\n"); + + return err; +} + struct sk_buff * __mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, struct mt76_wcid *wcid, int len); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 90e5666c0857..2acce121fb14 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -245,7 +245,6 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, if (id->driver_info) dev->no_2ghz = true; - usb_dev = usb_get_dev(usb_dev); usb_reset_device(usb_dev); usb_set_intfdata(usb_intf, dev); @@ -284,7 +283,6 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, err: usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76u_queues_deinit(&dev->mt76); mt76_free_device(&dev->mt76); @@ -303,7 +301,6 @@ static void mt76x0_disconnect(struct usb_interface *usb_intf) mt76x0u_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 01cb3b2830f3..ade15bddefc5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -16,6 +16,7 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */ { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ + { USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */ { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */ { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */ @@ -57,7 +58,6 @@ static int mt76x2u_probe(struct usb_interface *intf, dev = container_of(mdev, struct mt76x02_dev, mt76); - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(intf, dev); @@ -84,14 +84,12 @@ static int mt76x2u_probe(struct usb_interface *intf, mt76u_queues_deinit(&dev->mt76); mt76_free_device(&dev->mt76); usb_set_intfdata(intf, NULL); - usb_put_dev(udev); return err; } static void mt76x2u_disconnect(struct usb_interface *intf) { - struct usb_device *udev = interface_to_usbdev(intf); struct mt76x02_dev *dev = usb_get_intfdata(intf); struct ieee80211_hw *hw = mt76_hw(dev); @@ -100,7 +98,6 @@ static void mt76x2u_disconnect(struct usb_interface *intf) mt76x2u_cleanup(dev); mt76_free_device(&dev->mt76); usb_set_intfdata(intf, NULL); - usb_put_dev(udev); } static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 26ed3745af43..4d0854fe785b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -1296,8 +1296,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) struct dentry *dir; dir = mt76_register_debugfs_fops(phy->mt76, NULL); - if (!dir) - return -ENOMEM; + debugfs_create_file("muru_debug", 0600, dir, dev, &fops_muru_debug); debugfs_create_file("muru_stats", 0400, dir, phy, &mt7915_muru_stats_fops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index cec2c4208255..334c19ab2b22 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) } if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT_V3, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT_V3, info); } else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT, info); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index e1d83052aa6d..51643a48ed15 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -760,6 +760,9 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->wcid.phy_idx = ext_phy; msta->jiffies = jiffies; + if (sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->wcid.flags); + ewma_avg_signal_init(&msta->avg_ack_signal); mt7915_mac_wtbl_update(dev, idx, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 318c38149463..4a381d351e61 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3534,10 +3534,18 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len, if (category == TX_POWER_INFO_RATE) { s8 res[MT7915_SKU_RATE_NUM][2]; + if (skb->len < sizeof(res) + 4) { + dev_kfree_skb(skb); + return -EINVAL; + } memcpy(res, skb->data + 4, sizeof(res)); for (i = 0; i < len; i++) txpower[i] = res[i][req.band_idx]; } else if (category == TX_POWER_INFO_PATH) { + if (skb->len < len + 4) { + dev_kfree_skb(skb); + return -EINVAL; + } memcpy(txpower, skb->data + 4, len); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile index 2ad3c1cc3779..3ef7c9c45386 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MT7921E) += mt7921e.o obj-$(CONFIG_MT7921S) += mt7921s.o obj-$(CONFIG_MT7921U) += mt7921u.o -mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o +mt7921-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o mt7921e-y := pci.o pci_mac.o pci_mcu.o mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c index 4333005b3ad9..a5a70d8e8544 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c @@ -266,8 +266,6 @@ int mt7921_init_debugfs(struct mt792x_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); - if (!dir) - return -ENOMEM; if (mt76_is_mmio(&dev->mt76)) debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index 8e7790702191..49be2fa4e3fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -9,6 +9,7 @@ #include "mt7921.h" #include "../mt76_connac2_mac.h" #include "mcu.h" +#include "regd.h" static ssize_t mt7921_thermal_temp_show(struct device *dev, struct device_attribute *attr, @@ -60,103 +61,6 @@ static int mt7921_thermal_init(struct mt792x_phy *phy) return PTR_ERR_OR_ZERO(hwmon); } -static void -mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) -{ -#define IS_UNII_INVALID(idx, sfreq, efreq) \ - (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) - struct ieee80211_supported_band *sband; - struct mt76_dev *mdev = &dev->mt76; - struct device_node *np, *band_np; - struct ieee80211_channel *ch; - int i, cfreq; - - np = mt76_find_power_limits_node(mdev); - - sband = wiphy->bands[NL80211_BAND_5GHZ]; - band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL; - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - cfreq = ch->center_freq; - - if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { - ch->flags |= IEEE80211_CHAN_DISABLED; - continue; - } - - /* UNII-4 */ - if (IS_UNII_INVALID(0, 5845, 5925)) - ch->flags |= IEEE80211_CHAN_DISABLED; - } - - sband = wiphy->bands[NL80211_BAND_6GHZ]; - if (!sband) - return; - - band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL; - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - cfreq = ch->center_freq; - - if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { - ch->flags |= IEEE80211_CHAN_DISABLED; - continue; - } - - /* UNII-5/6/7/8 */ - if (IS_UNII_INVALID(1, 5925, 6425) || - IS_UNII_INVALID(2, 6425, 6525) || - IS_UNII_INVALID(3, 6525, 6875) || - IS_UNII_INVALID(4, 6875, 7125)) - ch->flags |= IEEE80211_CHAN_DISABLED; - } -} - -void mt7921_regd_update(struct mt792x_dev *dev) -{ - struct mt76_dev *mdev = &dev->mt76; - struct ieee80211_hw *hw = mdev->hw; - struct wiphy *wiphy = hw->wiphy; - - mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env); - mt7921_regd_channel_update(wiphy, dev); - mt76_connac_mcu_set_channel_domain(hw->priv); - mt7921_set_tx_sar_pwr(hw, NULL); -} -EXPORT_SYMBOL_GPL(mt7921_regd_update); - -static void -mt7921_regd_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct mt792x_dev *dev = mt792x_hw_dev(hw); - struct mt76_connac_pm *pm = &dev->pm; - - memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); - dev->mt76.region = request->dfs_region; - dev->country_ie_env = request->country_ie_env; - - if (request->initiator == NL80211_REGDOM_SET_BY_USER) { - if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0') - wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; - else - wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; - } - - if (pm->suspended) - return; - - dev->regd_in_progress = true; - - mt792x_mutex_acquire(dev); - mt7921_regd_update(dev); - mt792x_mutex_release(dev); - - dev->regd_in_progress = false; - wake_up(&dev->wait); -} - int mt7921_mac_init(struct mt792x_dev *dev) { int i; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 03b4960db73f..1c2377d0a53d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -7,6 +7,7 @@ #include "mt7921.h" #include "../dma.h" #include "../mt76_connac2_mac.h" +#include "regd.h" #include "mcu.h" #define MT_WTBL_TXRX_CAP_RATE_OFFSET 7 @@ -530,8 +531,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len) stat = FIELD_GET(MT_TX_FREE_STATUS, info); if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TX_FREE_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!stat; } @@ -675,7 +677,9 @@ void mt7921_mac_reset_work(struct work_struct *work) if (!ret) break; } - if (mt76_is_sdio(&dev->mt76) && atomic_read(&dev->mt76.bus_hung)) + + if ((mt76_is_sdio(&dev->mt76) || mt76_is_usb(&dev->mt76)) && + atomic_read(&dev->mt76.bus_hung)) return; if (i == 10) @@ -697,6 +701,8 @@ void mt7921_mac_reset_work(struct work_struct *work) IEEE80211_IFACE_ITER_RESUME_ALL, mt7921_vif_connect_iter, NULL); mt76_connac_power_save_sched(&dev->mt76.phy, pm); + + mt7921_regd_change(&dev->phy, "00"); } void mt7921_coredump_work(struct work_struct *work) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 3d74fabe7408..3480205d5fb9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -7,6 +7,7 @@ #include #include #include "mt7921.h" +#include "regd.h" #include "mcu.h" static int @@ -801,7 +802,7 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) out: if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) - mt7921_regd_update(dev); + mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); } int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, @@ -828,6 +829,9 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->deflink.last_txs = jiffies; msta->deflink.sta = msta; + if (sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->deflink.wcid.flags); + ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); if (ret) return ret; @@ -1027,8 +1031,16 @@ void mt7921_scan_work(struct work_struct *work) rxd = (struct mt76_connac2_mcu_rxd *)skb->data; if (rxd->eid == MCU_EVENT_SCHED_SCAN_DONE) { ieee80211_sched_scan_results(phy->mt76->hw); - } else if (test_and_clear_bit(MT76_HW_SCANNING, - &phy->mt76->state)) { + } else if (rxd->eid == MCU_EVENT_SCAN_DONE) { + struct mt76_connac_hw_scan_done *event = NULL; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_hw_scan_done *)skb->data; + mt7921_regd_change(phy, event->alpha2); + } + + if (test_and_clear_bit(MT76_HW_SCANNING, + &phy->mt76->state)) { struct cfg80211_scan_info info = { .aborted = false, }; @@ -1405,10 +1417,12 @@ mt7921_change_chanctx(struct ieee80211_hw *hw, vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); mt792x_mutex_acquire(phy->dev); - if (vif->type == NL80211_IFTYPE_MONITOR) + if (vif->type == NL80211_IFTYPE_MONITOR) { + mt7921_mcu_set_sniffer(mvif->phy->dev, vif, true); mt7921_mcu_config_sniffer(mvif, ctx); - else + } else { mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx); + } mt792x_mutex_release(phy->dev); } @@ -1508,6 +1522,9 @@ static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw, struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; u16 beacon_interval = vif->bss_conf.beacon_int; + if (!dev->new_ctx) + return; + if (cfg80211_chandef_identical(&chsw->chandef, &dev->new_ctx->def) && chsw->count) { @@ -1552,7 +1569,7 @@ const struct ieee80211_ops mt7921_ops = { .wake_tx_queue = mt76_wake_tx_queue, .release_buffered_frames = mt76_release_buffered_frames, .channel_switch_beacon = mt7921_channel_switch_beacon, - .get_txpower = mt76_get_txpower, + .get_txpower = mt792x_get_txpower, .get_stats = mt792x_get_stats, .get_et_sset_count = mt792x_get_et_sset_count, .get_et_strings = mt792x_get_et_strings, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 8442dbd2ee23..25b9437250f7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -4,6 +4,7 @@ #include #include #include "mt7921.h" +#include "regd.h" #include "mcu.h" #include "../mt76_connac2_mac.h" #include "../mt792x_trace.h" @@ -11,10 +12,6 @@ #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) -static bool mt7921_disable_clc; -module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); -MODULE_PARM_DESC(disable_clc, "disable CLC support"); - int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { @@ -422,8 +419,8 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) u8 *clc_base = NULL, hw_encap = 0; dev->phy.clc_chan_conf = 0xff; - if (mt7921_disable_clc || - mt76_is_usb(&dev->mt76)) + dev->regd_user = false; + if (!mt7921_regd_clc_supported(dev)) return 0; if (mt76_is_mmio(&dev->mt76)) { @@ -488,7 +485,8 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) goto out; } } - ret = mt7921_mcu_set_clc(dev, "00", ENVIRON_INDOOR); + + ret = mt7921_regd_init(phy); out: release_firmware(fw); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 64f60c4fc60c..fb8654b7c5e7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -260,7 +260,6 @@ mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val) #define mt7921_l1_set(dev, addr, val) mt7921_l1_rmw(dev, addr, 0, val) #define mt7921_l1_clear(dev, addr, val) mt7921_l1_rmw(dev, addr, val, 0) -void mt7921_regd_update(struct mt792x_dev *dev); int mt7921_mac_init(struct mt792x_dev *dev); bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask); int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 7a790ddf43bb..7728c5ae6791 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -12,6 +12,7 @@ #include "../mt76_connac2_mac.h" #include "../dma.h" #include "mcu.h" +#include "regd.h" static const struct pci_device_id mt7921_pci_device_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961), @@ -343,11 +344,14 @@ static int mt7921_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mdev); - regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); - if (IS_ERR(regs)) - return PTR_ERR(regs); - dev = container_of(mdev, struct mt792x_dev, mt76); + + regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err_free_dev; + } + dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; dev->irq_map = &irq_map; @@ -359,8 +363,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, /* MT7902 needs a mutable copy because wm2_complete_mask differs */ map = devm_kmemdup(&pdev->dev, &irq_map, sizeof(irq_map), GFP_KERNEL); - if (!map) - return -ENOMEM; + if (!map) { + ret = -ENOMEM; + goto err_free_dev; + } map->rx.wm2_complete_mask = 0; dev->irq_map = map; @@ -583,7 +589,7 @@ static int mt7921_pci_resume(struct device *device) if (err < 0) goto failed; - mt7921_regd_update(dev); + mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env); err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED); failed: pm->suspended = false; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c new file mode 100644 index 000000000000..c0e2b48a50bf --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2025 MediaTek Inc. */ + +#include +#include "mt7921.h" +#include "regd.h" +#include "mcu.h" + +static bool mt7921_disable_clc; +module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); +MODULE_PARM_DESC(disable_clc, "disable CLC support"); + +bool mt7921_regd_clc_supported(struct mt792x_dev *dev) +{ + if (mt7921_disable_clc || + mt76_is_usb(&dev->mt76)) + return false; + + return true; +} + +static void +mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) +{ +#define IS_UNII_INVALID(idx, sfreq, efreq) \ + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct device_node *np, *band_np; + struct ieee80211_channel *ch; + int i, cfreq; + + np = mt76_find_power_limits_node(mdev); + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + cfreq = ch->center_freq; + + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + + /* UNII-4 */ + if (IS_UNII_INVALID(0, 5845, 5925)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return; + + band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + cfreq = ch->center_freq; + + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + + /* UNII-5/6/7/8 */ + if (IS_UNII_INVALID(1, 5925, 6425) || + IS_UNII_INVALID(2, 6425, 6525) || + IS_UNII_INVALID(3, 6525, 6875) || + IS_UNII_INVALID(4, 6875, 7125)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } +} + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_hw *hw = mdev->hw; + struct wiphy *wiphy = hw->wiphy; + int ret = 0; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + if (!dev->regd_change) + goto err; + + ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env); + if (ret < 0) + goto err; + + mt7921_regd_channel_update(wiphy, dev); + + ret = mt76_connac_mcu_set_channel_domain(hw->priv); + if (ret < 0) + goto err; + + ret = mt7921_set_tx_sar_pwr(hw, NULL); + if (ret < 0) + goto err; + +err: + mt792x_mutex_release(dev); + dev->regd_change = false; + dev->regd_in_progress = false; + wake_up(&dev->wait); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update); + +void mt7921_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; + + if (req->initiator == NL80211_REGDOM_SET_BY_USER && + !dev->regd_user) + dev->regd_user = true; + + /* do not need to update the same country twice */ + if (!memcmp(req->alpha2, mdev->alpha2, 2) && + dev->country_ie_env == req->country_ie_env) + return; + + memcpy(mdev->alpha2, req->alpha2, 2); + mdev->region = req->dfs_region; + dev->country_ie_env = req->country_ie_env; + + if (req->initiator == NL80211_REGDOM_SET_BY_USER) { + if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0') + wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; + else + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + } + + dev->regd_change = true; + + if (pm->suspended) + return; + + mt7921_mcu_regd_update(dev, req->alpha2, + req->country_ie_env); +} + +static bool +mt7921_regd_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (dev->hw_full_reset) + return 0; + + if (!mt7921_regd_is_valid_alpha2(alpha2) || + !mt7921_regd_clc_supported(dev) || + dev->regd_user) + return -EINVAL; + + if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') + return 0; + + /* do not need to update the same country twice */ + if (!memcmp(alpha2, mdev->alpha2, 2)) + return 0; + + if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + return regulatory_hint(wiphy, alpha2); + else + return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR); +} + +int mt7921_regd_init(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE | + REGULATORY_DISABLE_BEACON_HINTS; + else + memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2)); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h new file mode 100644 index 000000000000..571f31629e9e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2025 MediaTek Inc. */ + +#ifndef __MT7921_REGD_H +#define __MT7921_REGD_H + +struct mt792x_dev; +struct wiphy; +struct regulatory_request; + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); +void mt7921_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request); +bool mt7921_regd_clc_supported(struct mt792x_dev *dev); +int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2); +int mt7921_regd_init(struct mt792x_phy *phy); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 17057e68bf21..2fdecf239125 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -13,6 +13,8 @@ #include "../mt76_connac2_mac.h" static const struct usb_device_id mt7921u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7902, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7902_FIRMWARE_WM }, { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, /* Comfast CF-952AX */ @@ -88,6 +90,10 @@ static int mt7921u_mac_reset(struct mt792x_dev *dev) { int err; + mt792xu_reset_on_bus_error(dev); + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + mt76_txq_schedule_all(&dev->mphy); mt76_worker_disable(&dev->mt76.tx_worker); @@ -196,8 +202,9 @@ static int mt7921u_probe(struct usb_interface *usb_intf, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &hif_ops; + atomic_set(&dev->mt76.bus_hung, false); + mt792xu_reset_work_init(dev); - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(usb_intf, dev); @@ -244,9 +251,9 @@ static int mt7921u_probe(struct usb_interface *usb_intf, error: mt76u_queues_deinit(&dev->mt76); + mt792xu_reset_work_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c index e2498659c884..d01ff49de47a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c @@ -291,8 +291,6 @@ int mt7925_init_debugfs(struct mt792x_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); - if (!dir) - return -ENOMEM; if (mt76_is_mmio(&dev->mt76)) debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index c0c5cb9aff75..e85b0d104fbe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -117,6 +117,15 @@ static int __mt7925_init_hardware(struct mt792x_dev *dev) if (ret) goto out; + if (is_mt7927(&dev->mt76)) { + ret = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (ret) { + dev_warn(dev->mt76.dev, + "MT7927 DBDC enable failed: %d\n", ret); + ret = 0; + } + } + out: return ret; } @@ -232,7 +241,8 @@ int mt7925_register_device(struct mt792x_dev *dev) dev->pm.idle_timeout = MT792x_PM_TIMEOUT; dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; - if (!mt76_is_usb(&dev->mt76)) { + /* MT7927: runtime PM crashes BT firmware on the shared CONNINFRA domain */ + if (!mt76_is_usb(&dev->mt76) && !is_mt7927(&dev->mt76)) { dev->pm.enable_user = true; dev->pm.enable = true; dev->pm.ds_enable_user = true; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index c47bd812b66b..0641a7131d7c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -339,6 +339,11 @@ mt7925_mac_fill_rx_rate(struct mt792x_dev *dev, case IEEE80211_STA_RX_BW_160: status->bw = RATE_INFO_BW_160; break; + /* RXV can report 320 in two positions */ + case IEEE80211_STA_RX_BW_320: + case IEEE80211_STA_RX_BW_320 + 1: + status->bw = RATE_INFO_BW_320; + break; default: return -EINVAL; } @@ -841,7 +846,6 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_link_sta *link_sta; - struct mt792x_link_sta *mlink; struct mt792x_sta *msta; bool is_8023; u16 fc, tid; @@ -880,14 +884,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, msta = (struct mt792x_sta *)sta->drv_priv; - if (sta->mlo && msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) - mlink = rcu_dereference(msta->link[msta->deflink_id]); - else - mlink = &msta->deflink; - - if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) { + /* Packets belonging to the same TID can be transmitted over multiple + * links. Keep the TX BA session state in the primary link so all links + * share the same AMPDU bookkeeping. + */ + if (!test_and_set_bit(tid, &msta->deflink.wcid.ampdu_state)) { if (ieee80211_start_tx_ba_session(sta, tid, 0)) - clear_bit(tid, &mlink->wcid.ampdu_state); + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + } } @@ -997,6 +1001,10 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid, stats->tx_mode[mode]++; switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_320: + rate.bw = RATE_INFO_BW_320; + stats->tx_bw[4]++; + break; case IEEE80211_STA_RX_BW_160: rate.bw = RATE_INFO_BW_160; stats->tx_bw[3]++; @@ -1141,8 +1149,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len) if (info & MT_TXFREE_INFO_HEADER) { if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!FIELD_GET(MT_TXFREE_INFO_STAT, info); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 73d3722739d0..a9059866b701 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -183,6 +183,10 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE; + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[0] |= + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + eht_cap_elem->phy_cap_info[0] |= u8_encode_bits(u8_get_bits(sts - 1, BIT(0)), IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK); @@ -193,10 +197,20 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK); + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[1] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK); + eht_cap_elem->phy_cap_info[2] = u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) | u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK); + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[2] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK); + eht_cap_elem->phy_cap_info[3] = IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | @@ -217,7 +231,8 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)), IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK); - val = width == NL80211_CHAN_WIDTH_160 ? 0x7 : + val = width == NL80211_CHAN_WIDTH_320 ? 0xf : + width == NL80211_CHAN_WIDTH_160 ? 0x7 : width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1; eht_cap_elem->phy_cap_info[6] = u8_encode_bits(u8_get_bits(0x11, GENMASK(4, 2)), @@ -239,6 +254,11 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, eht_nss->bw._160.rx_tx_mcs9_max_nss = val; eht_nss->bw._160.rx_tx_mcs11_max_nss = val; eht_nss->bw._160.rx_tx_mcs13_max_nss = val; + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) { + eht_nss->bw._320.rx_tx_mcs9_max_nss = val; + eht_nss->bw._320.rx_tx_mcs11_max_nss = val; + eht_nss->bw._320.rx_tx_mcs13_max_nss = val; + } } int mt7925_init_mlo_caps(struct mt792x_phy *phy) @@ -376,6 +396,18 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, mconf->mt76.omac_idx = ieee80211_vif_is_mld(vif) ? 0 : mconf->mt76.idx; mconf->mt76.band_idx = 0xff; + + if (is_mt7927(&dev->mt76)) { + struct ieee80211_channel *chan; + + if (link_conf->chanreq.oper.chan) + chan = link_conf->chanreq.oper.chan; + else + chan = mvif->phy->mt76->chandef.chan; + + mconf->mt76.band_idx = mt7927_band_idx(chan->band); + } + mconf->mt76.wmm_idx = ieee80211_vif_is_mld(vif) ? 0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS; mconf->mt76.link_idx = hweight16(mvif->valid_links); @@ -887,6 +919,9 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, mlink->wcid.link_valid = !!link_sta->sta->valid_links; mlink->sta = msta; + if (link_sta->sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &mlink->wcid.flags); + wcid = &mlink->wcid; ewma_signal_init(&wcid->rssi); rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid); @@ -1265,6 +1300,9 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_AP) break; + if (vif->type == NL80211_IFTYPE_STATION && sta->tdls) + continue; + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); if (!link_sta) continue; @@ -1318,14 +1356,14 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - unsigned long rem; - rem = ieee80211_vif_is_mld(vif) ? msta->valid_links : BIT(0); - - mt7925_mac_sta_remove_links(dev, vif, sta, rem); - - if (ieee80211_vif_is_mld(vif)) + if (ieee80211_vif_is_mld(vif)) { + mt7925_mac_sta_remove_links(dev, vif, sta, msta->valid_links); mt7925_mcu_del_dev(mdev, vif); + } else { + mt7925_mac_link_sta_remove(mdev, vif, &sta->deflink, + &msta->deflink); + } if (vif->type == NL80211_IFTYPE_STATION) { mvif->wep_sta = NULL; @@ -1372,22 +1410,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_RX_START: mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn, params->buf_size); - mt7925_mcu_uni_rx_ba(dev, params, true); + mt7925_mcu_uni_rx_ba(dev, params, vif, true); break; case IEEE80211_AMPDU_RX_STOP: mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); - mt7925_mcu_uni_rx_ba(dev, params, false); + mt7925_mcu_uni_rx_ba(dev, params, vif, false); break; case IEEE80211_AMPDU_TX_OPERATIONAL: mtxq->aggr = true; mtxq->send_bar = false; - mt7925_mcu_uni_tx_ba(dev, params, true); + mt7925_mcu_uni_tx_ba(dev, params, vif, true); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, params, false); + mt7925_mcu_uni_tx_ba(dev, params, vif, false); break; case IEEE80211_AMPDU_TX_START: set_bit(tid, &msta->deflink.wcid.ampdu_state); @@ -1396,7 +1434,7 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, params, false); + mt7925_mcu_uni_tx_ba(dev, params, vif, false); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } @@ -2179,9 +2217,9 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->link_conf[link_id], NULL); rcu_assign_pointer(mvif->sta.link[link_id], NULL); - if (mconf != &mvif->bss_conf) + if (mconfs[link_id] != &mvif->bss_conf) devm_kfree(dev->mt76.dev, mconfs[link_id]); - if (mlink != &mvif->sta.deflink) + if (mlinks[link_id] != &mvif->sta.deflink) devm_kfree(dev->mt76.dev, mlinks[link_id]); } @@ -2218,6 +2256,29 @@ mt7925_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return err; } +static int +mt7927_reconfig_band(struct mt792x_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt792x_bss_conf *mconf, + u8 band_idx) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_link_sta *mlink = &mvif->sta.deflink; + int ret; + + ret = mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, + &mconf->mt76, &mlink->wcid, + false); + if (ret) + return ret; + + mconf->mt76.band_idx = band_idx; + + return mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, + &mconf->mt76, &mlink->wcid, + true); +} + static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, @@ -2228,6 +2289,7 @@ static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, struct mt792x_dev *dev = mt792x_hw_dev(hw); struct ieee80211_bss_conf *pri_link_conf; struct mt792x_bss_conf *mconf; + u8 band_idx; mutex_lock(&dev->mt76.mutex); @@ -2241,6 +2303,12 @@ static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, NULL, true); } else { mconf = &mvif->bss_conf; + + if (is_mt7927(&dev->mt76)) { + band_idx = mt7927_band_idx(ctx->def.chan->band); + + mt7927_reconfig_band(dev, vif, link_conf, mconf, band_idx); + } } mconf->mt76.ctx = ctx; @@ -2400,6 +2468,9 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, if (ieee80211_vif_is_mld(vif)) return; + if (!dev->new_ctx) + return; + beacon_interval = vif->bss_conf.beacon_int; if (cfg80211_chandef_identical(&chsw->chandef, @@ -2433,7 +2504,7 @@ const struct ieee80211_ops mt7925_ops = { .wake_tx_queue = mt76_wake_tx_queue, .release_buffered_frames = mt76_release_buffered_frames, .channel_switch_beacon = mt7925_channel_switch_beacon, - .get_txpower = mt76_get_txpower, + .get_txpower = mt792x_get_txpower, .get_stats = mt792x_get_stats, .get_et_sset_count = mt792x_get_et_sset_count, .get_et_strings = mt792x_get_et_strings, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 37cdf3e8a067..e94fa544ff20 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -566,6 +566,34 @@ mt7925_mcu_uni_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb) } } +static void +mt7925_mcu_handle_mbmc_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mbmc_conf_tlv *tlv; + u32 tlv_len; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv_len = skb->len; + tlv = (struct mbmc_conf_tlv *)skb->data; + + while (tlv_len >= sizeof(*tlv) && + le16_to_cpu(tlv->len) >= sizeof(*tlv) && + le16_to_cpu(tlv->len) <= tlv_len) { + u16 tag = le16_to_cpu(tlv->tag); + + if (tag == UNI_MBMC_SETTING || tag == UNI_MBMC_NO_RESP_SETTING) { + dev_dbg(dev->mt76.dev, + "MBMC event: tag=%u mbmc_en=%u\n", + tag, tlv->mbmc_en); + break; + } + + tlv_len -= le16_to_cpu(tlv->len); + tlv = (struct mbmc_conf_tlv *) + ((u8 *)tlv + le16_to_cpu(tlv->len)); + } +} + static void mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) @@ -584,6 +612,9 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, case MCU_UNI_EVENT_ROC: mt7925_mcu_uni_roc_event(dev, skb); break; + case MCU_UNI_EVENT_MBMC: + mt7925_mcu_handle_mbmc_event(dev, skb); + break; case MCU_UNI_EVENT_SCAN_DONE: mt7925_mcu_scan_event(dev, skb); return; @@ -624,9 +655,8 @@ void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) static int mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, struct ieee80211_ampdu_params *params, - bool enable, bool tx) + struct mt76_wcid *wcid, bool enable, bool tx) { - struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct sta_rec_ba_uni *ba; struct sk_buff *skb; struct tlv *tlv; @@ -652,30 +682,69 @@ mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, MCU_UNI_CMD(STA_REC_UPDATE), true); } -/** starec & wtbl **/ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct ieee80211_vif *vif, bool enable) { - struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = msta->vif; + struct ieee80211_sta *sta = params->sta; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - if (enable && !params->amsdu) - msta->deflink.wcid.amsdu = false; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt792x_link_sta *mlink; + struct mt792x_bss_conf *mconf; + int ret; - return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, - enable, true); + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + return -EINVAL; + + mconf = mt792x_vif_to_link(msta->vif, link_id); + if (!mconf) + return -EINVAL; + + if (enable && !params->amsdu) + mlink->wcid.amsdu = false; + + ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, + &mlink->wcid, enable, true); + if (ret) + return ret; + } + + return 0; } int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct ieee80211_vif *vif, bool enable) { - struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = msta->vif; + struct ieee80211_sta *sta = params->sta; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, - enable, false); + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt792x_link_sta *mlink; + struct mt792x_bss_conf *mconf; + int ret; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + return -EINVAL; + + mconf = mt792x_vif_to_link(msta->vif, link_id); + if (!mconf) + return -EINVAL; + + ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, + &mlink->wcid, enable, false); + if (ret) + return ret; + } + + return 0; } static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) @@ -1091,6 +1160,11 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, hdr_trans->to_ds = true; hdr_trans->from_ds = true; } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + hdr_trans->to_ds = false; + hdr_trans->from_ds = false; + } } int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, @@ -1667,6 +1741,7 @@ mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20)); memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80)); memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160)); + memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320)); } static void @@ -1880,8 +1955,8 @@ mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb, eml_cap = (vif->cfg.eml_cap & (IEEE80211_EML_CAP_EMLSR_SUPP | IEEE80211_EML_CAP_TRANSITION_TIMEOUT)) | - (ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EMLSR_PADDING_DELAY | - IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)); + (ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EML_PADDING_DELAY | + IEEE80211_EML_CAP_EML_TRANSITION_DELAY)); if (eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) { eht_mld->eml_cap[0] = u16_get_bits(eml_cap, GENMASK(7, 0)); @@ -2334,6 +2409,10 @@ void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy, case NL80211_CHAN_WIDTH_160: req->bw = CMD_CBW_160MHZ; break; + case NL80211_CHAN_WIDTH_320: + req->bw = CMD_CBW_320MHZ; + req->center_chan2 = ieee80211_frequency_to_channel(freq2); + break; case NL80211_CHAN_WIDTH_5: req->bw = CMD_CBW_5MHZ; break; @@ -2827,6 +2906,7 @@ int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_dev *dev = phy->dev; struct sk_buff *skb; + int err; skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, MT7925_BSS_UPDATE_MAX_SIZE); @@ -2852,8 +2932,9 @@ int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, mt7925_mcu_bss_mbssid_tlv(skb, link_conf, enable); } - return mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_UNI_CMD(BSS_INFO_UPDATE), true); + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); + return mt76_connac_mcu_bss_deact_err(&dev->mt76, err, enable); } int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, @@ -2911,6 +2992,7 @@ int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) return err; } +EXPORT_SYMBOL_GPL(mt7925_mcu_set_dbdc); static void mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 56e2772f3ffe..293f173b23dd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -143,7 +143,8 @@ enum { }; enum { - UNI_MBMC_SETTING, + UNI_MBMC_SETTING = 0, + UNI_MBMC_NO_RESP_SETTING = 1, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 46b480f7d813..4cc259418afc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -126,6 +126,19 @@ enum mt7925_rxq_id { MT7925_RXQ_MCU_WM2, /* for tx done */ }; +enum mt7927_txq_id { + MT7927_TXQ_BAND0 = MT7925_TXQ_BAND0, + MT7927_TXQ_BAND1 = MT7925_TXQ_BAND1, + MT7927_TXQ_MCU_WM = MT7925_TXQ_MCU_WM, + MT7927_TXQ_FWDL = MT7925_TXQ_FWDL, +}; + +enum mt7927_rxq_id { + MT7927_RXQ_BAND0 = 4, + MT7927_RXQ_MCU_WM = 6, + MT7927_RXQ_DATA2 = 7, +}; + enum { MODE_OPEN = 0, MODE_SHARED = 1, @@ -239,6 +252,18 @@ struct mt7925_txpwr { s8 eht996x3_484[16][2]; }; +static inline u8 mt7927_band_idx(enum nl80211_band band) +{ + switch (band) { + case NL80211_BAND_2GHZ: + return 0; + case NL80211_BAND_5GHZ: + case NL80211_BAND_6GHZ: + default: + return 1; + } +} + extern const struct ieee80211_ops mt7925_ops; int __mt7925_start(struct mt792x_phy *phy); @@ -293,10 +318,10 @@ int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, bool enable); int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable); + struct ieee80211_vif *vif, bool enable); int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable); + struct ieee80211_vif *vif, bool enable); void mt7925_mlo_pm_work(struct work_struct *work); void mt7925_scan_work(struct work_struct *work); void mt7925_roc_work(struct work_struct *work); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index c4161754c01d..ea64303283ed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -16,6 +16,12 @@ static const struct pci_device_id mt7925_pci_device_table[] = { .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717), .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7927), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x6639), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0738), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, { }, }; @@ -210,48 +216,55 @@ static u32 mt7925_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) return dev->bus_ops->rmw(mdev, addr, mask, val); } -static int mt7925_dma_init(struct mt792x_dev *dev) +static const struct mt792x_dma_layout mt7925_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7925_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7925_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7925_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7925_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7925_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + +static const struct mt792x_dma_layout mt7927_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7927_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7927_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7927_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7927_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7927_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + +static int mt7927_dma_init(struct mt792x_dev *dev) { int ret; - mt76_dma_attach(&dev->mt76); - - ret = mt792x_dma_disable(dev, true); + ret = mt792x_dma_alloc_queues(dev, &mt7927_dma_layout); if (ret) return ret; - /* init tx queue */ - ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7925_TXQ_BAND0, - MT7925_TX_RING_SIZE, - MT_TX_RING_BASE, NULL, 0); - if (ret) - return ret; - - mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); - - /* command to WM */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7925_TXQ_MCU_WM, - MT7925_TX_MCU_RING_SIZE, MT_TX_RING_BASE); - if (ret) - return ret; - - /* firmware download */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7925_TXQ_FWDL, - MT7925_TX_FWDL_RING_SIZE, MT_TX_RING_BASE); - if (ret) - return ret; - - /* rx event */ - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], - MT7925_RXQ_MCU_WM, MT7925_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); - if (ret) - return ret; - - /* rx data */ - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], - MT7925_RXQ_BAND0, MT7925_RX_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE); + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT7927_RXQ_DATA2, + MT7925_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RX_DATA_RING_BASE); if (ret) return ret; @@ -266,6 +279,37 @@ static int mt7925_dma_init(struct mt792x_dev *dev) return mt792x_dma_enable(dev); } +static int mt7925_dma_init(struct mt792x_dev *dev) +{ + int ret; + + ret = mt792x_dma_alloc_queues(dev, &mt7925_dma_layout); + if (ret) + return ret; + + ret = mt76_init_queues(dev, mt792x_poll_rx); + if (ret < 0) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt792x_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return mt792x_dma_enable(dev); +} + +static const struct mt792x_irq_map mt7927_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .data_complete_mask = MT7927_RX_DONE_INT_ENA4, + .wm_complete_mask = MT7927_RX_DONE_INT_ENA6, + .wm2_complete_mask = MT7927_RX_DONE_INT_ENA7, + }, +}; static int mt7925_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -310,6 +354,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, struct mt76_bus_ops *bus_ops; struct mt792x_dev *dev; struct mt76_dev *mdev; + bool is_mt7927_hw; u8 features; int ret; u16 cmd; @@ -337,7 +382,11 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret) goto err_free_pci_vec; - if (mt7925_disable_aspm) + is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927 || + pdev->device == 0x0738); + + /* MT7927: ASPM L1 causes unreliable WFDMA register access */ + if (mt7925_disable_aspm || is_mt7927_hw) mt76_pci_disable_aspm(pdev); ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7925_ops, @@ -358,7 +407,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; - dev->irq_map = &irq_map; + dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &irq_map; mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); @@ -394,6 +443,13 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + if (is_mt7927_hw && mt76_chip(mdev) != 0x7927) { + dev_info(mdev->dev, + "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", + mt76_chip(mdev)); + mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } + mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1); ret = mt792x_wfsys_reset(dev); @@ -409,16 +465,24 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret) goto err_free_dev; - ret = mt7925_dma_init(dev); + if (is_mt7927(&dev->mt76)) + ret = mt7927_dma_init(dev); + else if (is_mt7925(&dev->mt76)) + ret = mt7925_dma_init(dev); + else + ret = -EINVAL; + if (ret) goto err_free_irq; ret = mt7925_register_device(dev); if (ret) - goto err_free_irq; + goto err_free_dma; return 0; +err_free_dma: + mt792x_dma_cleanup(dev); err_free_irq: devm_free_irq(&pdev->dev, pdev->irq, dev); err_free_dev: @@ -549,7 +613,10 @@ static int _mt7925_pci_resume(struct device *device, bool restore) mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); + dev->irq_map->rx.data_complete_mask | + dev->irq_map->rx.wm_complete_mask | + dev->irq_map->rx.wm2_complete_mask | + MT_INT_MCU_CMD); mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); /* put dma enabled */ @@ -633,6 +700,8 @@ module_pci_driver(mt7925_pci_driver); MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table); MODULE_FIRMWARE(MT7925_FIRMWARE_WM); MODULE_FIRMWARE(MT7925_ROM_PATCH); +MODULE_FIRMWARE(MT7927_FIRMWARE_WM); +MODULE_FIRMWARE(MT7927_ROM_PATCH); MODULE_AUTHOR("Deren Wu "); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c index 3072850c2752..97683949a305 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c @@ -3,6 +3,7 @@ #include "mt7925.h" #include "../dma.h" +#include "mcu.h" #include "mac.h" int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, @@ -118,7 +119,10 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) mt76_wr(dev, dev->irq_map->host_irq_enable, dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); + dev->irq_map->rx.data_complete_mask | + dev->irq_map->rx.wm_complete_mask | + dev->irq_map->rx.wm2_complete_mask | + MT_INT_MCU_CMD); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); err = mt792xe_mcu_fw_pmctrl(dev); @@ -141,6 +145,12 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) if (err) goto out; + if (is_mt7927(&dev->mt76)) { + err = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (err) + goto out; + } + err = __mt7925_start(&dev->phy); out: clear_bit(MT76_RESET, &dev->mphy.state); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c index 3d40aacfc011..22a8f1ddc321 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -105,6 +105,11 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req, if (ret) goto out; + if (skb->len < MT7925_EVT_RSP_LEN + 8) { + ret = -EINVAL; + goto out; + } + memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN); out: diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index d9968f03856d..e9f58492bf7d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -10,8 +10,13 @@ #include "mac.h" static const struct usb_device_id mt7925u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x6639, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + /* Netgear, Inc. A8500 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9050, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, /* Netgear, Inc. A9000 */ { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9072, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, @@ -120,6 +125,12 @@ static int mt7925u_mac_reset(struct mt792x_dev *dev) if (err) goto out; + if (is_mt7927(&dev->mt76)) { + err = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (err) + goto out; + } + err = __mt7925_start(&dev->phy); out: clear_bit(MT76_RESET, &dev->mphy.state); @@ -184,8 +195,8 @@ static int mt7925u_probe(struct usb_interface *usb_intf, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &hif_ops; + mt792xu_reset_work_init(dev); - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(usb_intf, dev); @@ -198,6 +209,13 @@ static int mt7925u_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_chip(mdev) == 0x6639) { + dev_dbg(mdev->dev, + "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", + mt76_chip(mdev)); + mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } + if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) { ret = mt792xu_wfsys_reset(dev); if (ret) @@ -232,9 +250,9 @@ static int mt7925u_probe(struct usb_interface *usb_intf, error: mt76u_queues_deinit(&dev->mt76); + mt792xu_reset_work_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 4ff93f2cd624..70073b43af54 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -46,12 +46,14 @@ #define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" #define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin" #define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin" +#define MT7927_FIRMWARE_WM "mediatek/mt7927/WIFI_RAM_CODE_MT6639_2_1.bin" #define MT7902_ROM_PATCH "mediatek/WIFI_MT7902_patch_mcu_1_1_hdr.bin" #define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin" #define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" #define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin" #define MT7925_ROM_PATCH "mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin" +#define MT7927_ROM_PATCH "mediatek/mt7927/WIFI_MT6639_PATCH_MCU_2_1_hdr.bin" #define MT792x_SDIO_HDR_TX_BYTES GENMASK(15, 0) #define MT792x_SDIO_HDR_PKT_TYPE GENMASK(17, 16) @@ -204,6 +206,23 @@ struct mt792x_irq_map { } rx; }; +struct mt792x_dma_ring { + u8 qid; + u16 n_desc; + u32 ring_base; +}; + +struct mt792x_dma_layout { + struct mt792x_dma_ring tx_data0; + struct mt792x_dma_ring tx_mcu; + struct mt792x_dma_ring tx_fwdl; + struct mt792x_dma_ring rx_data; + struct mt792x_dma_ring rx_mcu; +}; + +#define mt792x_dma_ring(_qid, _n_desc, _ring_base) \ + { .qid = (_qid), .n_desc = (_n_desc), .ring_base = (_ring_base) } + #define mt792x_init_reset(dev) ((dev)->hif_ops->init_reset(dev)) #define mt792x_dev_reset(dev) ((dev)->hif_ops->reset(dev)) #define mt792x_mcu_init(dev) ((dev)->hif_ops->mcu_init(dev)) @@ -243,6 +262,8 @@ struct mt792x_dev { wait_queue_head_t wait; struct work_struct init_work; + struct work_struct usb_reset_work; + atomic_t usb_reset_pending; u8 fw_debug; u8 fw_features; @@ -397,6 +418,8 @@ void mt792x_roc_timer(struct timer_list *timer); void mt792x_csa_timer(struct timer_list *timer); void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop); +int mt792x_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm); int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, @@ -419,6 +442,8 @@ void mt792x_sta_statistics(struct ieee80211_hw *hw, void mt792x_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, s16 coverage_class); void mt792x_dma_cleanup(struct mt792x_dev *dev); +int mt792x_dma_alloc_queues(struct mt792x_dev *dev, + const struct mt792x_dma_layout *layout); int mt792x_dma_enable(struct mt792x_dev *dev); int mt792x_wpdma_reset(struct mt792x_dev *dev, bool force); int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev); @@ -459,6 +484,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev) return MT7922_FIRMWARE_WM; case 0x7925: return MT7925_FIRMWARE_WM; + case 0x7927: + return MT7927_FIRMWARE_WM; default: return MT7921_FIRMWARE_WM; } @@ -475,6 +502,8 @@ static inline char *mt792x_patch_name(struct mt792x_dev *dev) return MT7922_ROM_PATCH; case 0x7925: return MT7925_ROM_PATCH; + case 0x7927: + return MT7927_ROM_PATCH; default: return MT7921_ROM_PATCH; } @@ -489,6 +518,10 @@ int mt792xu_dma_init(struct mt792x_dev *dev, bool resume); int mt792xu_mcu_power_on(struct mt792x_dev *dev); int mt792xu_wfsys_reset(struct mt792x_dev *dev); int mt792xu_init_reset(struct mt792x_dev *dev); +void mt792xu_reset_work_init(struct mt792x_dev *dev); +void mt792xu_reset_work_cleanup(struct mt792x_dev *dev); +int mt792xu_check_bus(struct mt792x_dev *dev); +int mt792xu_reset_on_bus_error(struct mt792x_dev *dev); u32 mt792xu_rr(struct mt76_dev *dev, u32 addr); void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val); u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 152cfcca2f90..b50825eccdaf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -105,7 +105,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, wcid = &mvif->sta.deflink.wcid; } - if (vif && control->sta && ieee80211_vif_is_mld(vif)) { + if (vif && control->sta && ieee80211_vif_is_mld(vif) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_link_sta *link_sta; struct ieee80211_bss_conf *conf; @@ -329,6 +330,47 @@ void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } EXPORT_SYMBOL_GPL(mt792x_flush); +int mt792x_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt76_power_limits limits = {}; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_channel *chan; + struct mt792x_bss_conf *mconf; + struct mt792x_vif *mvif; + struct mt76_phy *phy; + s8 max_power; + + if (!vif) + return mt76_get_txpower(hw, vif, link_id, dbm); + + mvif = (struct mt792x_vif *)vif->drv_priv; + phy = mvif->phy->mt76; + + mt792x_mutex_acquire(mvif->phy->dev); + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + mconf = link_conf ? mt792x_link_conf_to_mconf(link_conf) : NULL; + if (mconf && mconf->mt76.ctx && mconf->mt76.ctx->def.chan) + chan = mconf->mt76.ctx->def.chan; + else + /* Fall back to the current PHY chandef if the requested link + * does not have a valid channel context. + */ + chan = phy->chandef.chan; + + mt792x_mutex_release(mvif->phy->dev); + + if (!chan) + return -EINVAL; + + max_power = mt76_connac_get_rate_power_limit(phy, chan, &limits); + *dbm = DIV_ROUND_UP(max_power, 2); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_get_txpower); + int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, @@ -657,7 +699,6 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); - wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = @@ -759,6 +800,13 @@ mt792x_get_offload_capability(struct device *dev, const char *fw_wm) return offload_caps; } +static bool mt792x_needs_cnm_runtime(const void *drv_data) +{ + const char *fw_wm = drv_data; + + return fw_wm && !strcmp(fw_wm, MT7927_FIRMWARE_WM); +} + struct ieee80211_ops * mt792x_get_mac80211_ops(struct device *dev, const struct ieee80211_ops *mac80211_ops, @@ -772,6 +820,10 @@ mt792x_get_mac80211_ops(struct device *dev, return NULL; *fw_features = mt792x_get_offload_capability(dev, drv_data); + + if (mt792x_needs_cnm_runtime(drv_data)) + *fw_features |= MT792x_FW_CAP_CNM; + if (!(*fw_features & MT792x_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 002aece857b2..fc326447c792 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -87,22 +87,122 @@ void mt792x_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) } EXPORT_SYMBOL_GPL(mt792x_rx_poll_complete); +int mt792x_dma_alloc_queues(struct mt792x_dev *dev, + const struct mt792x_dma_layout *layout) +{ + int ret; + + mt76_dma_attach(&dev->mt76); + + ret = mt792x_dma_disable(dev, true); + if (ret) + return ret; + + /* init tx queue */ + ret = mt76_connac_init_tx_queues(dev->phy.mt76, layout->tx_data0.qid, + layout->tx_data0.n_desc, + layout->tx_data0.ring_base, + NULL, 0); + if (ret) + return ret; + + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + layout->tx_mcu.qid, + layout->tx_mcu.n_desc, + layout->tx_mcu.ring_base); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + layout->tx_fwdl.qid, + layout->tx_fwdl.n_desc, + layout->tx_fwdl.ring_base); + if (ret) + return ret; + + /* rx event */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + layout->rx_mcu.qid, + layout->rx_mcu.n_desc, + MT_RX_BUF_SIZE, + layout->rx_mcu.ring_base); + if (ret) + return ret; + + /* rx data */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + layout->rx_data.qid, + layout->rx_data.n_desc, + MT_RX_BUF_SIZE, + layout->rx_data.ring_base); + return ret; +} +EXPORT_SYMBOL_GPL(mt792x_dma_alloc_queues); + #define PREFETCH(base, depth) ((base) << 16 | (depth)) + +static void mt7925_dma_prefetch_setup(struct mt792x_dev *dev) +{ + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); + + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4)); +} + +static void mt7925_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); + mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); + mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); +} + +static void mt7927_dma_prefetch_setup(struct mt792x_dev *dev) +{ + mt76_wr(dev, MT_WFDMA_PREFETCH_CTRL, + mt76_rr(dev, MT_WFDMA_PREFETCH_CTRL)); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG0, 0x660077); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG1, 0x1100); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG2, 0x30004f); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG3, 0x542200); + mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0x0000, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING6_EXT_CTRL, PREFETCH(0x0080, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING7_EXT_CTRL, PREFETCH(0x0100, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0180, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0280, 0x4)); +} + +static void mt7927_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_ADDR_EXT_EN | + MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL); + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN); + mt76_rmw(dev, MT_WFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); + mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); + mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); +} + static void mt792x_dma_prefetch(struct mt792x_dev *dev) { - if (is_mt7925(&dev->mt76)) { - /* rx ring */ - mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); - /* tx ring */ - mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4)); - mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4)); + if (is_mt7927(&dev->mt76)) { + mt7927_dma_prefetch_setup(dev); + } else if (is_mt7925(&dev->mt76)) { + mt7925_dma_prefetch_setup(dev); } else if (is_mt7902(&dev->mt76)) { /* rx ring */ mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); @@ -166,11 +266,11 @@ int mt792x_dma_enable(struct mt792x_dev *dev) mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - if (is_mt7925(&dev->mt76)) { - mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); - mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); - mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); - } + if (is_mt7927(&dev->mt76)) + mt7927_wfdma_setup(dev); + else if (is_mt7925(&dev->mt76)) + mt7925_wfdma_setup(dev); + mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); /* enable interrupts for TX/RX rings */ @@ -281,6 +381,11 @@ int mt792x_dma_disable(struct mt792x_dev *dev, bool force) MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1)) return -ETIMEDOUT; + if (is_mt7927(&dev->mt76)) { + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0); + } + /* disable dmashdl */ mt76_clear(dev, MT_WFDMA0_GLO_CFG_EXT0, MT_WFDMA0_CSR_TX_DMASHDL_ENABLE); @@ -370,7 +475,54 @@ int mt792x_poll_rx(struct napi_struct *napi, int budget) } EXPORT_SYMBOL_GPL(mt792x_poll_rx); -int mt792x_wfsys_reset(struct mt792x_dev *dev) +static void mt7927_sema_status_read(struct mt792x_dev *dev, u32 addr) +{ + u32 remap; + + remap = mt76_rr(dev, MT7927_PCIE2AP_REMAP_WF_0_54); + mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, + (remap & ~MT7927_PCIE2AP_REMAP_WF_0_54_MASK) | + MT7927_PCIE2AP_REMAP_WF_0_54_VAL); + usleep_range(10, 20); + + mt76_rr(dev, addr); + + mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, remap); + usleep_range(10, 20); +} + +static int mt7927_wfsys_reset(struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 val; + + mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA); + + mt76_set(dev, MT7927_CBINFRA_RGU_WF_RST, + MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS); + usleep_range(1000, 2000); + + mt76_clear(dev, MT7927_CBINFRA_RGU_WF_RST, + MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS); + usleep_range(5000, 10000); + + mt76_wr(dev, MT7927_CBINFRA_MCU_OWN_SET, BIT(0)); + + if (!__mt76_poll_msec(mdev, MT7927_ROMCODE_INDEX, 0xffff, + MT7927_MCU_IDLE_VALUE, 200)) { + val = mt76_rr(dev, MT7927_ROMCODE_INDEX); + dev_err(mdev->dev, + "MT7927 WFSYS reset timeout (ROMCODE_INDEX=0x%04x)\n", + val & 0xffff); + return -ETIMEDOUT; + } + + mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA_REP); + + return 0; +} + +static int mt792x_wfsys_reset_default(struct mt792x_dev *dev) { u32 addr = is_connac2(&dev->mt76) ? 0x18000140 : 0x7c000140; @@ -384,5 +536,13 @@ int mt792x_wfsys_reset(struct mt792x_dev *dev) return 0; } + +int mt792x_wfsys_reset(struct mt792x_dev *dev) +{ + if (is_mt7927(&dev->mt76)) + return mt7927_wfsys_reset(dev); + + return mt792x_wfsys_reset_default(dev); +} EXPORT_SYMBOL_GPL(mt792x_wfsys_reset); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index d2a8b2b0df32..4cd5b33b640e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -301,7 +301,9 @@ #define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) #define MT_WFDMA0_GLO_CFG_RX_WB_DDONE BIT(13) #define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN BIT(15) +#define MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN BIT(20) #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) +#define MT_WFDMA0_GLO_CFG_ADDR_EXT_EN BIT(26) #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) #define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) #define MT_WFDMA0_GLO_CFG_CLK_GAT_DIS BIT(30) @@ -310,6 +312,9 @@ #define HOST_RX_DONE_INT_ENA1 BIT(1) #define HOST_RX_DONE_INT_ENA2 BIT(2) #define HOST_RX_DONE_INT_ENA3 BIT(3) +#define MT7927_RX_DONE_INT_ENA4 BIT(12) +#define MT7927_RX_DONE_INT_ENA6 BIT(14) +#define MT7927_RX_DONE_INT_ENA7 BIT(15) #define HOST_TX_DONE_INT_ENA0 BIT(4) #define HOST_TX_DONE_INT_ENA1 BIT(5) #define HOST_TX_DONE_INT_ENA2 BIT(6) @@ -365,6 +370,13 @@ #define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) #define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) +#define MT_WFDMA_PREFETCH_CTRL MT_WFDMA_EXT_CSR(0x30) +#define MT_WFDMA_PREFETCH_CFG0 MT_WFDMA_EXT_CSR(0xf0) +#define MT_WFDMA_PREFETCH_CFG1 MT_WFDMA_EXT_CSR(0xf4) +#define MT_WFDMA_PREFETCH_CFG2 MT_WFDMA_EXT_CSR(0xf8) +#define MT_WFDMA_PREFETCH_CFG3 MT_WFDMA_EXT_CSR(0xfc) +#define MT_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) + #define MT_SWDEF_BASE 0x41f200 #define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) #define MT_SWDEF_MODE MT_SWDEF(0x3c) @@ -490,4 +502,15 @@ #define WFSYS_SW_RST_B BIT(0) #define WFSYS_SW_INIT_DONE BIT(4) +/* CBInfra registers - MT7927 combo chip */ +#define MT7927_CBINFRA_RGU_WF_RST 0x1f8600 +#define MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS BIT(4) +#define MT7927_CBINFRA_MCU_OWN_SET 0x1f5034 +#define MT7927_ROMCODE_INDEX 0xc1604 +#define MT7927_MCU_IDLE_VALUE 0x1d1e +#define MT7927_PCIE2AP_REMAP_WF_0_54 0x21008 +#define MT7927_PCIE2AP_REMAP_WF_0_54_MASK GENMASK(15, 0) +#define MT7927_PCIE2AP_REMAP_WF_0_54_VAL 0x00001807 +#define MT7927_SEMA_OWN_STA 0x40000 +#define MT7927_SEMA_OWN_STA_REP 0x40400 #endif /* __MT792X_REGS_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 47827d1c5ccb..910132e94956 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -11,6 +11,81 @@ #include "mt792x.h" #include "mt76_connac2_mac.h" +static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf) +{ + return __mt76u_vendor_request(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, + (u16)(addr >> 16), (u16)addr, + buf, sizeof(__le32)); +} + +static void mt792xu_reset_work(struct work_struct *work) +{ + struct mt792x_dev *dev = + container_of(work, struct mt792x_dev, usb_reset_work); + struct usb_interface *usb_intf = to_usb_interface(dev->mt76.dev); + + if (usb_intf && usb_get_intfdata(usb_intf) == dev) + usb_queue_reset_device(usb_intf); + + atomic_set(&dev->usb_reset_pending, 0); +} + +void mt792xu_reset_work_init(struct mt792x_dev *dev) +{ + INIT_WORK(&dev->usb_reset_work, mt792xu_reset_work); + atomic_set(&dev->usb_reset_pending, 0); +} +EXPORT_SYMBOL_GPL(mt792xu_reset_work_init); + +void mt792xu_reset_work_cleanup(struct mt792x_dev *dev) +{ + cancel_work_sync(&dev->usb_reset_work); + atomic_set(&dev->usb_reset_pending, 0); +} +EXPORT_SYMBOL_GPL(mt792xu_reset_work_cleanup); + +int mt792xu_check_bus(struct mt792x_dev *dev) +{ + int ret; + + mutex_lock(&dev->mt76.usb.usb_ctrl_mtx); + ret = mt792xu_read32(&dev->mt76, MT_HW_CHIPID, dev->mt76.usb.data); + mutex_unlock(&dev->mt76.usb.usb_ctrl_mtx); + + if (ret == sizeof(__le32)) + return 0; + + return ret < 0 ? ret : -EIO; +} +EXPORT_SYMBOL_GPL(mt792xu_check_bus); + +int mt792xu_reset_on_bus_error(struct mt792x_dev *dev) +{ + int err = 0; + + if (!atomic_read(&dev->mt76.bus_hung)) + err = mt792xu_check_bus(dev); + + if (err) { + atomic_set(&dev->mt76.bus_hung, true); + + if (!atomic_xchg(&dev->usb_reset_pending, 1)) { + dev_warn(dev->mt76.dev, + "USB transport access failed (%d), queueing device reset\n", + err); + + schedule_work(&dev->usb_reset_work); + } + + return err; + } + + atomic_set(&dev->mt76.bus_hung, false); + return 0; +} +EXPORT_SYMBOL_GPL(mt792xu_reset_on_bus_error); + u32 mt792xu_rr(struct mt76_dev *dev, u32 addr) { u32 ret; @@ -263,7 +338,7 @@ EXPORT_SYMBOL_GPL(mt792xu_dma_init); int mt792xu_wfsys_reset(struct mt792x_dev *dev) { - const struct mt792xu_wfsys_desc *desc = is_mt7925(&dev->mt76) ? + const struct mt792xu_wfsys_desc *desc = is_connac3(&dev->mt76) ? &mt7925_wfsys_desc : &mt7921_wfsys_desc; u32 val; @@ -333,6 +408,7 @@ void mt792xu_disconnect(struct usb_interface *usb_intf) { struct mt792x_dev *dev = usb_get_intfdata(usb_intf); + mt792xu_reset_work_cleanup(dev); cancel_work_sync(&dev->init_work); if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) return; @@ -341,7 +417,6 @@ void mt792xu_disconnect(struct usb_interface *usb_intf) mt792xu_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index 34af800964d1..b85c3e549f8b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -664,7 +664,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) if (!mlink) continue; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) continue; @@ -875,8 +875,6 @@ int mt7996_init_debugfs(struct mt7996_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, NULL); - if (!dir) - return -ENOMEM; debugfs_create_file("hw-queues", 0400, dir, dev, &mt7996_hw_queues_fops); @@ -1042,7 +1040,7 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file, mutex_lock(&dev->mt76.mutex); - msta_link = mt76_dereference(msta->link[link_sta->link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_sta->link_id); if (!msta_link) { ret = -EINVAL; goto out; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c index 8f5d297dafce..3d9353811a02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -683,7 +683,7 @@ int mt7996_dma_init(struct mt7996_dev *dev) ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(0), MT7996_NPU_TX_RING_SIZE, MT_TXQ_RING_BASE(0) + hif1_ofs, - NULL); + wed); else ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(dev->mphy.band_idx), diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index e2a83da3a09c..0eebc8182ca9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -48,7 +48,7 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, if (mlink->band_idx != band_idx) continue; - msta_link = rcu_dereference(msta->link[i]); + msta_link = mt7996_sta_link(msta, i); break; } @@ -757,6 +757,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, bool multicast = is_multicast_ether_addr(hdr->addr1); u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + struct ieee80211_vif *vif = info->control.vif; u16 seqno = le16_to_cpu(sc); bool hw_bigtk = false; u8 fc_type, fc_stype; @@ -819,7 +820,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); } - if (multicast && ieee80211_vif_is_mld(info->control.vif)) { + if (multicast && vif && ieee80211_vif_is_mld(vif)) { val = MT_TXD3_SN_VALID | FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); txwi[3] |= cpu_to_le32(val); @@ -839,12 +840,12 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); } - if (ieee80211_vif_is_mld(info->control.vif) && + if (vif && ieee80211_vif_is_mld(vif) && (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) txwi[5] |= cpu_to_le32(MT_TXD5_FL); if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && - ieee80211_vif_is_mld(info->control.vif)) { + vif && ieee80211_vif_is_mld(vif)) { txwi[5] |= cpu_to_le32(MT_TXD5_FL); txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } @@ -856,7 +857,8 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed) + enum mt76_txq_id qid, u32 changed, + unsigned int link_id) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -866,7 +868,6 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; struct mt76_vif_link *mlink = NULL; struct mt7996_vif *mvif; - unsigned int link_id; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -874,17 +875,11 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - if (wcid != &dev->mt76.global_wcid) - link_id = wcid->link_id; - else - link_id = u32_get_bits(info->control.flags, - IEEE80211_TX_CTRL_MLO_LINK); - mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; if (mvif) { if (wcid->offchannel) mlink = rcu_dereference(mvif->mt76.offchannel_link); - if (!mlink) + if (!mlink && link_id != IEEE80211_LINK_UNSPECIFIED) mlink = rcu_dereference(mvif->mt76.link[link_id]); } @@ -1038,7 +1033,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) { if (msta) { struct mt7996_sta_link *msta_link = - rcu_dereference(msta->link[link_id]); + mt7996_sta_link(msta, link_id); if (msta_link) wcid = &msta_link->wcid; @@ -1067,11 +1062,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, link_conf = rcu_dereference(vif->link_conf[wcid->link_id]); if (!link_conf) - return -EINVAL; + goto error_release_token; link_sta = rcu_dereference(sta->link[wcid->link_id]); if (!link_sta) - return -EINVAL; + goto error_release_token; dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr, tx_info->buf[1].len, DMA_TO_DEVICE); @@ -1096,7 +1091,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, /* Transmit non qos data by 802.11 header and need to fill txd by host*/ if (!is_8023 || pid >= MT_PACKET_ID_FIRST) mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, - pid, qid, 0); + pid, qid, 0, link_id); /* MT7996 and MT7992 require driver to provide the MAC TXP for AddBA * req @@ -1176,6 +1171,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, tx_info->nbuf = MT_CT_DMA_BUF_NUM; return 0; + +error_release_token: + mt76_token_release(mdev, id, NULL); + return -EINVAL; } u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) @@ -1346,7 +1345,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) IEEE80211_MLD_MAX_NUM_LINKS) { struct mt7996_sta_link *msta_link; - msta_link = rcu_dereference(msta->link[id]); + msta_link = mt7996_sta_link(msta, id); if (!msta_link) continue; @@ -1360,13 +1359,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) cur_info++; continue; } else if (info & MT_TXFREE_INFO_HEADER) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (!wcid) continue; - tx_retries = - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TXFREE_INFO_STAT, info); @@ -2059,8 +2058,6 @@ void mt7996_mac_set_coverage_class(struct mt7996_phy *phy) { s16 coverage_class = phy->coverage_class; struct mt7996_dev *dev = phy->dev; - struct mt7996_phy *phy2 = mt7996_phy2(dev); - struct mt7996_phy *phy3 = mt7996_phy3(dev); u32 reg_offset; u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); @@ -2072,14 +2069,6 @@ void mt7996_mac_set_coverage_class(struct mt7996_phy *phy) if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) return; - if (phy2) - coverage_class = max_t(s16, dev->phy.coverage_class, - phy2->coverage_class); - - if (phy3) - coverage_class = max_t(s16, coverage_class, - phy3->coverage_class); - offset = 3 * coverage_class; reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index a8a6552d49f6..afbcc8c7b18b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -207,8 +207,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct mt7996_sta *msta; msta = (struct mt7996_sta *)sta->drv_priv; - msta_link = mt76_dereference(msta->link[link_id], - &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) return 0; @@ -1164,6 +1163,9 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev, msta_link->wcid.link_valid = !!sta->valid_links; msta_link->wcid.def_wcid = &msta->deflink.wcid; + if (link_sta->sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta_link->wcid.flags); + ewma_avg_signal_init(&msta_link->avg_ack_signal); ewma_signal_init(&msta_link->wcid.rssi); @@ -1381,7 +1383,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, if (!link) continue; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -1573,7 +1575,7 @@ static void mt7996_tx(struct ieee80211_hw *hw, if (msta) { struct mt7996_sta_link *msta_link; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (msta_link) wcid = &msta_link->wcid; } @@ -1944,7 +1946,7 @@ static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw, rcu_read_lock(); - msta_link = rcu_dereference(msta->link[link_sta->link_id]); + msta_link = mt7996_sta_link(msta, link_sta->link_id); if (msta_link) { struct mt7996_dev *dev = mt7996_hw_dev(hw); @@ -1959,9 +1961,13 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_sta_link *msta_link; - u32 *changed = data; + struct mt7996_vif *mvif = data; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; - msta_link = rcu_dereference(msta->link[msta->deflink_id]); + if (msta->vif != mvif) + return; + + msta_link = mt7996_sta_link(msta, msta->deflink_id); if (msta_link) mt7996_link_rate_ctrl_update(&changed, msta_link); } @@ -1972,7 +1978,6 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; mvif->deflink.bitrate_mask = *mask; @@ -1985,7 +1990,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * then multiple MCS setting (MCS 4,5,6) is not supported. */ ieee80211_iterate_stations_atomic(hw, mt7996_sta_rate_ctrl_update, - &changed); + mvif); ieee80211_queue_work(hw, &dev->rc_work); return 0; @@ -2011,7 +2016,7 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, if (!link) continue; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2049,7 +2054,7 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, if (!link) continue; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2389,7 +2394,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, if (!link) return -EIO; - msta_link = rcu_dereference(msta->link[msta->deflink_id]); + msta_link = mt7996_sta_link(msta, msta->deflink_id); if (!msta_link) return -EIO; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 16420375112d..f119f023bcd5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1136,7 +1136,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct mt7996_sta_link *msta_link; int link_id = link_conf->link_id; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (msta_link) sta_wlan_idx = msta_link->wcid.idx; } @@ -1281,6 +1281,10 @@ int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *li PROT_NONGF_STA = BIT(7), }; + /* The current firmware causes TX failure. Need further investigation */ + if (is_mt7996(&dev->mt76)) + return 0; + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, MT7996_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) @@ -1429,7 +1433,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -1463,7 +1467,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2139,6 +2143,11 @@ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb, hdr_trans->from_ds = true; hdr_trans->mesh = true; } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + hdr_trans->to_ds = false; + hdr_trans->from_ds = false; + } } static enum mcu_mmps_mode @@ -2200,7 +2209,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!mlink) goto error_unlock; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) goto error_unlock; @@ -2290,7 +2299,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!link) goto error_unlock; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) goto error_unlock; @@ -2508,7 +2517,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!link) goto error_unlock; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) goto error_unlock; @@ -2663,7 +2672,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, unsigned int link_id; struct tlv *tlv; - msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id); if (!msta_link) return; @@ -2677,8 +2686,8 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx); if (nlinks > 1) { - msta_link = mt76_dereference(msta->link[msta->seclink_id], - &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, + msta->seclink_id); if (!msta_link) return; } @@ -2689,7 +2698,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, for_each_sta_active_link(vif, sta, link_sta, link_id) { struct mt7996_vif_link *link; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2837,7 +2846,7 @@ void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta) if (!link_sta) return; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) return; @@ -3098,7 +3107,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, buf = (u8 *)bcn + sizeof(*bcn); mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, - BSS_CHANGED_BEACON); + BSS_CHANGED_BEACON, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); } @@ -3244,7 +3253,8 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, buf = (u8 *)tlv + sizeof(*discov); - mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed); + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, + changed, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index bdcf72457954..0d6488522ba7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -642,6 +642,25 @@ mt7996_vif_conf_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, link_conf); } +static inline struct mt7996_sta_link * +mt7996_sta_link(struct mt7996_sta *msta, u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return rcu_dereference(msta->link[link_id]); +} + +static inline struct mt7996_sta_link * +mt7996_sta_link_protected(struct mt7996_dev *dev, struct mt7996_sta *msta, + u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return mt76_dereference(msta->link[link_id], &dev->mt76); +} + #define mt7996_for_each_phy(dev, phy) \ for (int __i = 0; __i < ARRAY_SIZE((dev)->radio_phy); __i++) \ if (((phy) = (dev)->radio_phy[__i]) != NULL) @@ -855,7 +874,8 @@ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed); + enum mt76_txq_id qid, u32 changed, + unsigned int link_id); void mt7996_mac_update_beacons(struct mt7996_phy *phy); void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); void mt7996_mac_work(struct work_struct *work); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c index 12523ddba630..b7d9193e042f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -141,7 +141,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev, dev->hif2 = hif2; mt76_npu_init(mdev, pci_resource_start(pdev, 0), - pdev->bus && pci_domain_nr(pdev->bus) ? 3 : 2); + pci_domain_nr(pdev->bus) ? 3 : 2); ret = mt7996_mmio_wed_init(dev, pdev, false, &irq); if (ret < 0) diff --git a/drivers/net/wireless/mediatek/mt76/pci.c b/drivers/net/wireless/mediatek/mt76/pci.c index 833923ab2483..11fe62aa8113 100644 --- a/drivers/net/wireless/mediatek/mt76/pci.c +++ b/drivers/net/wireless/mediatek/mt76/pci.c @@ -30,8 +30,14 @@ void mt76_pci_disable_aspm(struct pci_dev *pdev) if (IS_ENABLED(CONFIG_PCIEASPM)) { int err; + int state = 0; - err = pci_disable_link_state(pdev, aspm_conf); + if (aspm_conf & PCI_EXP_LNKCTL_ASPM_L0S) + state |= PCIE_LINK_STATE_L0S; + if (aspm_conf & PCI_EXP_LNKCTL_ASPM_L1) + state |= PCIE_LINK_STATE_L1; + + err = pci_disable_link_state(pdev, state); if (!err) return; } diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 22f9690634c9..f96d9c471853 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -933,7 +933,7 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) #endif } - if (dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && + if (wake && dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && dev->phy.q_tx[0]->blocked) *wake = true; diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 632ae755c7a6..d9638a9b749b 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -925,6 +925,8 @@ mt76u_ac_to_hwq(struct mt76_dev *dev, struct mt76_queue *q, u8 qid) q->ep = q->hw_idx + 1; break; } + case 0x6639: + case 0x7902: case 0x7961: case 0x7925: q->hw_idx = mt76_ac_to_hwq(ac); diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c index c41ae251cb95..9306870cbc91 100644 --- a/drivers/net/wireless/mediatek/mt7601u/usb.c +++ b/drivers/net/wireless/mediatek/mt7601u/usb.c @@ -274,7 +274,6 @@ static int mt7601u_probe(struct usb_interface *usb_intf, if (!dev) return -ENOMEM; - usb_dev = usb_get_dev(usb_dev); usb_reset_device(usb_dev); usb_set_intfdata(usb_intf, dev); @@ -319,7 +318,6 @@ static int mt7601u_probe(struct usb_interface *usb_intf, mt7601u_cleanup(dev); err: usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); destroy_workqueue(dev->stat_wq); ieee80211_free_hw(dev->hw); @@ -334,7 +332,6 @@ static void mt7601u_disconnect(struct usb_interface *usb_intf) mt7601u_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); destroy_workqueue(dev->stat_wq); ieee80211_free_hw(dev->hw); diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c index 3a774cc44b26..6654fce4ded8 100644 --- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c +++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c @@ -1100,7 +1100,8 @@ static void wilc_wfi_remain_on_channel_expired(struct wilc_vif *vif, u64 cookie) static int remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie) + unsigned int duration, u64 *cookie, + const u8 *rx_addr) { int ret = 0; struct wilc_vif *vif = netdev_priv(wdev->netdev); diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c index 5724ec173e64..6d24086eb8b7 100644 --- a/drivers/net/wireless/purelifi/plfxlc/usb.c +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c @@ -851,7 +851,7 @@ static int resume(struct usb_interface *interface) #endif -static struct usb_driver driver = { +static struct usb_driver usbdriver = { .name = KBUILD_MODNAME, .id_table = usb_ids, .probe = probe, @@ -865,25 +865,7 @@ static struct usb_driver driver = { .disable_hub_initiated_lpm = 1, }; -static int __init usb_init(void) -{ - int r; - - r = usb_register(&driver); - if (r) { - pr_err("%s usb_register() failed %d\n", driver.name, r); - return r; - } - - pr_debug("Driver initialized :%s\n", driver.name); - return 0; -} - -static void __exit usb_exit(void) -{ - usb_deregister(&driver); - pr_debug("%s %s\n", driver.name, __func__); -} +module_usb_driver(usbdriver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("USB driver for pureLiFi devices"); @@ -891,6 +873,3 @@ MODULE_AUTHOR("pureLiFi"); MODULE_VERSION("1.0"); MODULE_FIRMWARE("plfxlc/lifi-x.bin"); MODULE_DEVICE_TABLE(usb, usb_ids); - -module_init(usb_init); -module_exit(usb_exit); diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index d9c4dd6beaec..77929ed9ef78 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -261,8 +261,8 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, cmd->p2p_ctwindow = s->p2p_ctwindow; cmd->p2p_opp_ps = s->p2p_opp_ps; cmd->pbss = s->pbss; - cmd->ht_required = s->ht_required; - cmd->vht_required = s->vht_required; + cmd->ht_required = s->beacon.ht_required; + cmd->vht_required = s->beacon.vht_required; cmd->twt_responder = s->twt_responder; if (s->he_obss_pd.enable) { cmd->sr_params.sr_control |= QLINK_SR_SRG_INFORMATION_PRESENT; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c index ef5c069542d4..a3a285f17dca 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c @@ -1132,9 +1132,9 @@ static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, /* assign host msi irq before card init */ if (ts->base.msi_enabled) - irqflags = IRQF_NOBALANCING; + irqflags = IRQF_NOBALANCING | IRQF_NO_AUTOEN; else - irqflags = IRQF_NOBALANCING | IRQF_SHARED; + irqflags = IRQF_NOBALANCING | IRQF_SHARED | IRQF_NO_AUTOEN; ret = devm_request_irq(&pdev->dev, pdev->irq, &qtnf_pcie_topaz_interrupt, @@ -1144,8 +1144,6 @@ static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, return ret; } - disable_irq(pdev->irq); - ret = qtnf_pre_init_ep(bus); if (ret) { pr_err("failed to init card\n"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 665887e9b118..7d313e86d3f2 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -1009,11 +1009,12 @@ struct rt2x00_dev { /* Extra TX headroom required for alignment purposes. */ unsigned int extra_tx_headroom; - struct usb_anchor *anchor; unsigned int num_proto_errs; /* Clock for System On Chip devices. */ struct clk *clk; + + struct usb_anchor anchor[]; }; struct rt2x00_bar_list_entry { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index 174d89b0b1d7..47e427ea8622 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -804,7 +804,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, usb_reset_device(usb_dev); - hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + hw = ieee80211_alloc_hw(struct_size(rt2x00dev, anchor, 1), ops->hw); if (!hw) { rt2x00_probe_err("Failed to allocate hardware\n"); return -ENOMEM; @@ -826,13 +826,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, if (retval) goto exit_free_device; - rt2x00dev->anchor = devm_kmalloc(&usb_intf->dev, - sizeof(struct usb_anchor), - GFP_KERNEL); - if (!rt2x00dev->anchor) { - retval = -ENOMEM; - goto exit_free_reg; - } init_usb_anchor(rt2x00dev->anchor); retval = rt2x00lib_probe_dev(rt2x00dev); @@ -843,8 +836,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, exit_free_anchor: usb_kill_anchored_urbs(rt2x00dev->anchor); - -exit_free_reg: rt2x00usb_free_reg(rt2x00dev); exit_free_device: diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c index 766a7a7c7d28..fea5aec9ced9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c @@ -1866,6 +1866,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = { .has_tx_report = 1, .init_reg_pkt_life_time = 1, .gen2_thermal_meter = 1, + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c index 3abf14d7044f..5556aad1a7ad 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c @@ -1745,6 +1745,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x70, .ustime_tsf_edca = 0x28, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 444872131c66..508322a8f310 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -723,6 +723,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), .supports_ap = 1, + .hw_feature_report = 0, .max_macid_num = 32, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c index 8e123bbfc665..690ace303150 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c @@ -1751,6 +1751,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .has_s0s1 = 0, .gen2_thermal_meter = 1, .needs_full_init = 1, + .hw_feature_report = 0, .supports_ap = 1, .max_macid_num = 128, .max_sec_cam_num = 64, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c index cd2156b7a57a..5f076da4b550 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c @@ -2074,6 +2074,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x1f1f, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c index 11c63c320eae..a87e692bfc81 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c @@ -1852,6 +1852,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, /* TODO, it's different */ .ampdu_max_time = 0x5e, /* * The RTL8710BU vendor driver uses 0x50 here and it works fine, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c index 4f4493d0bfc2..18e038b82f49 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c @@ -632,6 +632,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c index cc2e60b06f64..e314ef991b38 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c @@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .gen2_thermal_meter = 1, .needs_full_init = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index f20fade0c099..646fe76b086e 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -14,6 +14,7 @@ */ #include +#include #include "regs.h" #include "rtl8xxxu.h" @@ -3915,6 +3916,46 @@ static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv, return sta_info->macid; } +static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv) +{ + if (!priv->fops->hw_feature_report) + return; + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP); +} + +static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv) +{ + static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 }; + struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature; + u8 feature[13]; + int i, ret; + u8 id, bw; + + if (!priv->fops->hw_feature_report) { + hw_feature->max_bw = 40; + return 0; + } + + ret = read_poll_timeout(rtl8xxxu_read8, id, + id == C2H_HW_FEATURE_REPORT, + 10000, 800000, false, + priv, REG_C2HEVT_MSG_NORMAL); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(feature); i++) + feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i); + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0); + + bw = u8_get_bits(feature[6], GENMASK(2, 0)); + + hw_feature->max_bw = bw_map[bw]; + + return 0; +} + static int rtl8xxxu_init_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -3961,6 +4002,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) */ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary); + rtl8xxxu_request_hw_feature(priv); + for (int retry = 5; retry >= 0 ; retry--) { ret = rtl8xxxu_download_firmware(priv); dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); @@ -3976,6 +4019,12 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; + ret = rtl8xxxu_dump_hw_feature(priv); + if (ret) { + dev_err(dev, "failed to dump hw feature\n"); + goto exit; + } + if (fops->phy_init_antenna_selection) fops->phy_init_antenna_selection(priv); @@ -5126,7 +5175,7 @@ static void rtl8xxxu_tx_complete(struct urb *urb) } static void rtl8xxxu_dump_action(struct device *dev, - struct ieee80211_hdr *hdr) + struct ieee80211_hdr *hdr, unsigned int skb_len) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)hdr; u16 cap, timeout; @@ -5134,8 +5183,14 @@ static void rtl8xxxu_dump_action(struct device *dev, if (!(rtl8xxxu_debug & RTL8XXXU_DEBUG_ACTION)) return; + if (skb_len < IEEE80211_MIN_ACTION_SIZE(action_code)) + return; + switch (mgmt->u.action.action_code) { case WLAN_ACTION_ADDBA_RESP: + if (skb_len < IEEE80211_MIN_ACTION_SIZE(addba_resp)) + break; + cap = le16_to_cpu(mgmt->u.action.addba_resp.capab); timeout = le16_to_cpu(mgmt->u.action.addba_resp.timeout); dev_info(dev, "WLAN_ACTION_ADDBA_RESP: " @@ -5148,6 +5203,9 @@ static void rtl8xxxu_dump_action(struct device *dev, le16_to_cpu(mgmt->u.action.addba_resp.status)); break; case WLAN_ACTION_ADDBA_REQ: + if (skb_len < IEEE80211_MIN_ACTION_SIZE(addba_req)) + break; + cap = le16_to_cpu(mgmt->u.action.addba_req.capab); timeout = le16_to_cpu(mgmt->u.action.addba_req.timeout); dev_info(dev, "WLAN_ACTION_ADDBA_REQ: " @@ -5437,7 +5495,7 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, } if (ieee80211_is_action(hdr->frame_control)) - rtl8xxxu_dump_action(dev, hdr); + rtl8xxxu_dump_action(dev, hdr, skb->len); tx_info->rate_driver_data[0] = hw; @@ -7835,15 +7893,20 @@ static int rtl8xxxu_probe(struct usb_interface *interface, sband->ht_cap.ht_supported = true; sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20; + + if (priv->hw_feature.max_bw >= 40) { + sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } else { + dev_info(&udev->dev, "hardware doesn't support HT40\n"); + } + memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs)); sband->ht_cap.mcs.rx_mask[0] = 0xff; sband->ht_cap.mcs.rx_mask[4] = 0x01; - if (priv->rf_paths > 1) { + if (priv->rf_paths > 1) sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - } sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h index 0741db8d08bf..eafaef1438b9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h @@ -447,6 +447,8 @@ /* 8188EU */ #define REG_32K_CTRL 0x0194 #define REG_C2HEVT_MSG_NORMAL 0x01a0 +#define C2H_HW_FEATURE_REPORT 0x19 +#define C2H_HW_FEATURE_DUMP 0xfd /* 8192EU/8723BU/8812 */ #define REG_C2HEVT_CMD_ID_8723B 0x01ae #define REG_C2HEVT_CLEAR 0x01af diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 9fb2583ffffc..eeb18eb0e4c0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1789,11 +1789,17 @@ struct rtl8xxxu_cfo_tracking { #define RTL8XXXU_BC_MC_MACID1 1 #define RTL8XXXU_MAX_SEC_CAM_NUM 64 +struct rtl8xxxu_hw_feature { + u8 max_bw; +}; + struct rtl8xxxu_priv { struct ieee80211_hw *hw; struct usb_device *udev; struct rtl8xxxu_fileops *fops; + struct rtl8xxxu_hw_feature hw_feature; + spinlock_t tx_urb_lock; struct list_head tx_urb_free_list; int tx_urb_free_count; @@ -2009,6 +2015,7 @@ struct rtl8xxxu_fileops { u8 init_reg_pkt_life_time:1; u8 init_reg_hmtfr:1; u8 supports_concurrent:1; + u8 hw_feature_report:1; u8 ampdu_max_time; u8 ustime_tsf_edca; u16 max_aggr_num; diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index aad377864e73..9e98c01bb90e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1369,18 +1369,19 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_mgmt *mgmt; __le16 fc = rtl_get_fc(skb); - u8 *act = (u8 *)(((u8 *)skb->data + MAC80211_3ADDR_LEN)); - u8 category; if (!ieee80211_is_action(fc)) return true; - category = *act; - act++; - switch (category) { + mgmt = (void *)skb->data; + if (skb->len < IEEE80211_MIN_ACTION_SIZE(action_code)) + return true; + + switch (mgmt->u.action.category) { case ACT_CAT_BA: - switch (*act) { + switch (mgmt->u.action.action_code) { case ACT_ADDBAREQ: if (mac->act_scanning) return false; @@ -1394,9 +1395,11 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct ieee80211_sta *sta = NULL; struct rtl_sta_info *sta_entry = NULL; struct rtl_tid_data *tid_data; - struct ieee80211_mgmt *mgmt = (void *)skb->data; u16 capab = 0, tid = 0; + if (skb->len < IEEE80211_MIN_ACTION_SIZE(addba_req)) + return true; + rcu_read_lock(); sta = rtl_find_sta(hw, hdr->addr3); if (sta == NULL) { diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 9cc0a871ea3c..73018a0498b4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -505,9 +505,10 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) } } if (ieee80211_is_action(fc)) { - struct ieee80211_mgmt *action_frame = - (struct ieee80211_mgmt *)skb->data; - if (action_frame->u.action.action_code == + struct ieee80211_mgmt *action_frame = (void *)skb->data; + + if (skb->len >= IEEE80211_MIN_ACTION_SIZE(action_code) && + action_frame->u.action.action_code == WLAN_HT_ACTION_SMPS) { dev_kfree_skb(skb); goto tx_status_ok; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 3a4a33476205..9b119a51bc30 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -2355,7 +2355,7 @@ void rtl8821ae_card_disable(struct ieee80211_hw *hw) rtl8821ae_set_fw_disconnect_decision_ctrl_cmd(hw, true); } - /* 3 <3> Hw Configutations */ + /* 3 <3> Hw Configurations */ /* Wait until Rx DMA Finished before host sleep. * FW Pause Rx DMA may happen when received packet doing DMA. @@ -3257,9 +3257,9 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, u32 ratr_value; u8 ratr_index = 0; u8 b_nmode = mac->ht_enable; - u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; + u32 ratr_mask; u8 curtxbw_40mhz = mac->bw_40; u8 b_curshortgi_40mhz = (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; @@ -3288,19 +3288,14 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: b_nmode = 1; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - ratr_value &= 0x0007F005; - } else { - u32 ratr_mask; - if (get_rf_type(rtlphy) == RF_1T2R || - get_rf_type(rtlphy) == RF_1T1R) - ratr_mask = 0x000ff005; - else - ratr_mask = 0x0f0ff005; + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) + ratr_mask = 0x000ff005; + else + ratr_mask = 0x0f0ff005; - ratr_value &= ratr_mask; - } + ratr_value &= ratr_mask; break; default: if (rtlphy->rf_type == RF_1T2R) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h index 1155365348f3..d5de09d75f45 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h @@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le32 *__pdesc) static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc) { - return le32_get_bits(*(__pdesc + 1), BIT(28)); + return le32_get_bits(*(__pdesc + 2), BIT(28)); } static inline int get_rx_desc_rxmcs(__le32 *__pdesc) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 48207052e3f8..945fedcd375b 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -279,6 +279,9 @@ static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload, const struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th; struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload; + if (!edcca_th) + return; + rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n", result->density, result->igi, result->l2h_th_init, result->l2h, diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index bba370ad510c..a30467228912 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1042,20 +1042,21 @@ static int rtw_pci_get_hw_rx_ring_nr(struct rtw_dev *rtwdev, static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, u8 hw_queue, u32 limit) { + struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; const struct rtw_chip_info *chip = rtwdev->chip; struct napi_struct *napi = &rtwpci->napi; - struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; - struct rtw_rx_pkt_stat pkt_stat; + u32 pkt_desc_sz = chip->rx_pkt_desc_sz; + u32 buf_desc_sz = chip->rx_buf_desc_sz; struct ieee80211_rx_status rx_status; + struct rtw_rx_pkt_stat pkt_stat; struct sk_buff *skb, *new; u32 cur_rp = ring->r.rp; u32 count, rx_done = 0; u32 pkt_offset; - u32 pkt_desc_sz = chip->rx_pkt_desc_sz; - u32 buf_desc_sz = chip->rx_buf_desc_sz; + dma_addr_t dma; u32 new_len; u8 *rx_desc; - dma_addr_t dma; + int ret; count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci); count = min(count, limit); @@ -1067,7 +1068,10 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_desc = skb->data; - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) + goto next_rp; /* offset from rx_desc to payload */ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + @@ -1077,6 +1081,11 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, * discard the frame if none available */ new_len = pkt_stat.pkt_len + pkt_offset; + if (unlikely(new_len > RTK_PCI_RX_BUF_SIZE)) { + rtw_dbg(rtwdev, RTW_DBG_RX, + "oversized RX packet: %u\n", new_len); + goto next_rp; + } new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp; @@ -1711,9 +1720,9 @@ static void rtw_pci_napi_deinit(struct rtw_dev *rtwdev) static pci_ers_result_t rtw_pci_io_err_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -1730,12 +1739,12 @@ static pci_ers_result_t rtw_pci_io_slot_reset(struct pci_dev *pdev) static void rtw_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw_pci_err_handler = { diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c index d9e11343d498..01fd299abb7f 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -3,6 +3,7 @@ */ #include "main.h" +#include "mac.h" #include "rx.h" #include "ps.h" #include "debug.h" @@ -261,9 +262,9 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev, } } -void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, - struct rtw_rx_pkt_stat *pkt_stat, - struct ieee80211_rx_status *rx_status) +int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, + struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status) { u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; struct rtw_rx_desc *rx_desc = rx_desc8; @@ -295,20 +296,28 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, pkt_stat->tsf_low = le32_get_bits(rx_desc->w5, RTW_RX_DESC_W5_TSFL); - if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) { - rtw_dbg(rtwdev, RTW_DBG_UNEXP, - "unexpected RX rate=0x%x\n", pkt_stat->rate); + if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) + return -EINVAL; - pkt_stat->rate = DESC_RATE1M; - pkt_stat->bw = RTW_CHANNEL_WIDTH_20; - } + if (unlikely(pkt_stat->drv_info_sz && + pkt_stat->drv_info_sz != PHY_STATUS_SIZE)) + return -EINVAL; + + if (unlikely(pkt_stat->phy_status && !pkt_stat->drv_info_sz)) + return -EINVAL; + + if (unlikely(pkt_stat->pkt_len > IEEE80211_MAX_MPDU_LEN_VHT_11454)) + return -EINVAL; /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) - return; + return 0; + + if (unlikely(pkt_stat->pkt_len <= FCS_LEN)) + return -EINVAL; phy_status = rx_desc8 + desc_sz + pkt_stat->shift; hdr = phy_status + pkt_stat->drv_info_sz; @@ -318,5 +327,7 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, rtwdev->chip->ops->query_phy_status(rtwdev, phy_status, pkt_stat); rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status); + + return 0; } EXPORT_SYMBOL(rtw_rx_query_rx_desc); diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h index 6b7dee245c0a..74359f641c76 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.h +++ b/drivers/net/wireless/realtek/rtw88/rx.h @@ -45,9 +45,9 @@ struct rtw_rx_desc { void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct sk_buff *skb); -void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, - struct rtw_rx_pkt_stat *pkt_stat, - struct ieee80211_rx_status *rx_status); +int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, + struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status); void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct rtw_rx_pkt_stat *pkt_stat); diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index 1318e94f8524..5b40d74b16ee 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -995,7 +995,13 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len) while (true) { rx_desc = skb->data; - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) { + dev_kfree_skb_any(skb); + return; + } + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 3106edb84fb4..9d747a060b98 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -196,6 +196,7 @@ void rtw_tx_report_purge_timer(struct timer_list *t) void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; + unsigned long timeout = RTW_TX_PROBE_TIMEOUT; unsigned long flags; u8 *drv_data; @@ -207,7 +208,11 @@ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); - mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT); + if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D && + rtwdev->hci.type == RTW_HCI_TYPE_USB) + timeout = msecs_to_jiffies(2500); + + mod_timer(&tx_report->purge_timer, jiffies + timeout); } EXPORT_SYMBOL(rtw_tx_report_enqueue); @@ -619,31 +624,19 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev, return 0; } -static struct sk_buff *rtw_txq_dequeue(struct rtw_dev *rtwdev, - struct rtw_txq *rtwtxq) -{ - struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); - struct sk_buff *skb; - - skb = ieee80211_tx_dequeue(rtwdev->hw, txq); - if (!skb) - return NULL; - - return skb; -} - static void rtw_txq_push(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq, unsigned long frames) { + struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); struct sk_buff *skb; + unsigned long i; int ret; - int i; rcu_read_lock(); for (i = 0; i < frames; i++) { - skb = rtw_txq_dequeue(rtwdev, rtwtxq); + skb = ieee80211_tx_dequeue(rtwdev->hw, txq); if (!skb) break; diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 718940ebba31..64e1c3420e0a 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list int agg_num = 0; unsigned int align_next = 0; u8 qsel; + int ret; if (skb_queue_empty(list)) return false; @@ -456,7 +457,13 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list tx_desc = (struct rtw_tx_desc *)skb_head->data; qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL); - rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb); + ret = rtw_usb_write_port(rtwdev, qsel, skb_head, + rtw_usb_write_port_tx_complete, txcb); + if (ret) { + ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue); + kfree(txcb); + return false; + } return true; } @@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev, ret = rtw_usb_write_port(rtwdev, qsel, skb, rtw_usb_write_port_complete, skb); - if (unlikely(ret)) + if (unlikely(ret)) { rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + dev_kfree_skb_any(skb); + } return ret; } @@ -610,8 +619,8 @@ static void rtw_usb_rx_handler(struct work_struct *work) u32 max_skb_len = pkt_desc_sz + PHY_STATUS_SIZE * 8 + IEEE80211_MAX_MPDU_LEN_VHT_11454; u32 pkt_offset, next_pkt, skb_len; + int limit, ret; u8 *rx_desc; - int limit; for (limit = 0; limit < 200; limit++) { rx_skb = skb_dequeue(&rtwusb->rx_queue); @@ -627,8 +636,11 @@ static void rtw_usb_rx_handler(struct work_struct *work) rx_desc = rx_skb->data; do { - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, - &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) + break; + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 44d8a7f32bf2..43e3b0ef44da 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -157,6 +157,18 @@ config RTW89_8922AE The variant 8922AE-VS has the same features except 1024-QAM. +config RTW89_8922AU + tristate "Realtek 8922AU USB wireless network (Wi-Fi 7) adapter" + depends on USB + select RTW89_CORE + select RTW89_USB + select RTW89_8922A + help + Select this option will enable support for 8922AU chipset + + 802.11be USB wireless network (Wi-Fi 7) adapter + supporting 2x2 2GHz/5GHz/6GHz 4096-QAM 160MHz channels. + config RTW89_DEBUG bool diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 1be81f254fca..475bad743d75 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -88,6 +88,9 @@ rtw89_8922a-objs := rtw8922a.o \ obj-$(CONFIG_RTW89_8922AE) += rtw89_8922ae.o rtw89_8922ae-objs := rtw8922ae.o +obj-$(CONFIG_RTW89_8922AU) += rtw89_8922au.o +rtw89_8922au-objs := rtw8922au.o + rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index ceb399fc2b94..6f11335b4968 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -150,6 +150,8 @@ void rtw89_chan_create(struct rtw89_chan *chan, u8 center_chan, u8 primary_chan, primary_freq); chan->pri_sb_idx = rtw89_get_primary_sb_idx(center_chan, primary_chan, bandwidth); + chan->rfsi_band = band == RTW89_BAND_2G ? RFSI_CTRL_BAND_2GHZ : + RFSI_CTRL_BAND_5_6GHZ; } static void _rtw89_chan_update_punctured(struct rtw89_dev *rtwdev, @@ -398,43 +400,6 @@ static u8 rtw89_entity_role_get_index(struct rtw89_dev *rtwdev) } } -const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, - const char *caller_message, - u8 link_index, bool nullchk) -{ - struct rtw89_hal *hal = &rtwdev->hal; - struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; - enum rtw89_chanctx_idx chanctx_idx; - u8 role_index; - - lockdep_assert_wiphy(rtwdev->hw->wiphy); - - if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) { - WARN(1, "link index %u is invalid (max link inst num: %d)\n", - link_index, __RTW89_MLD_MAX_LINK_NUM); - goto dflt; - } - - role_index = rtw89_entity_role_get_index(rtwdev); - - chanctx_idx = mgnt->chanctx_tbl[role_index][link_index]; - if (chanctx_idx == RTW89_CHANCTX_IDLE) - goto dflt; - - return rtw89_chan_get(rtwdev, chanctx_idx); - -dflt: - if (unlikely(nullchk)) - return NULL; - - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "%s (%s): prefetch NULL on link index %u\n", - __func__, caller_message ?: "", link_index); - - return rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); -} -EXPORT_SYMBOL(__rtw89_mgnt_chan_get); - bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { switch (rtwdev->mlo_dbcc_mode) { @@ -457,6 +422,59 @@ void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) rtw89_debug(rtwdev, RTW89_DBG_CHAN, "%s: (none)\n", __func__); } +void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf) +{ + struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; + enum rtw89_chanctx_idx idxes[ARRAY_SIZE(conf->chans)]; + struct rtw89_vif *role; + u8 ridx; + int i; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + memset(conf, 0, sizeof(*conf)); + + ridx = rtw89_entity_role_get_index(rtwdev); + role = mgnt->active_roles[ridx]; + if (role) { + struct ieee80211_vif *vif = rtwvif_to_vif(role); + + conf->is_mld = ieee80211_vif_is_mld(vif); + conf->en_emlsr = role->mlo_mode == RTW89_MLO_MODE_EMLSR; + } + + for (i = 0; i < ARRAY_SIZE(idxes); i++) + idxes[i] = RTW89_CHANCTX_IDLE; + + switch (rtwdev->mlo_dbcc_mode) { + default: + case MLO_2_PLUS_0_1RF: + set_bit(0, conf->hw_bitmap); + idxes[0] = mgnt->chanctx_tbl[ridx][0]; + idxes[1] = idxes[0]; + break; + case MLO_0_PLUS_2_1RF: + set_bit(1, conf->hw_bitmap); + idxes[1] = mgnt->chanctx_tbl[ridx][1]; + idxes[0] = idxes[1]; + break; + case MLO_1_PLUS_1_1RF: + set_bit(0, conf->hw_bitmap); + set_bit(1, conf->hw_bitmap); + idxes[0] = mgnt->chanctx_tbl[ridx][0]; + idxes[1] = mgnt->chanctx_tbl[ridx][1]; + break; + } + + for (i = 0; i < ARRAY_SIZE(idxes); i++) { + if (idxes[i] == RTW89_CHANCTX_IDLE) + idxes[i] = RTW89_CHANCTX_0; + + conf->chans[i] = rtw89_chan_get(rtwdev, idxes[i]); + } +} +EXPORT_SYMBOL(rtw89_entity_get_conf); + static enum rtw89_mlo_dbcc_mode rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) { diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index c797cda2e763..a9a5f1b307a2 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -116,6 +116,13 @@ struct rtw89_entity_weight { unsigned int active_roles; }; +struct rtw89_entity_conf { + bool is_mld; + bool en_emlsr; + DECLARE_BITMAP(hw_bitmap, __RTW89_MLD_MAX_LINK_NUM); + const struct rtw89_chan *chans[__RTW89_MLD_MAX_LINK_NUM]; +}; + static inline bool rtw89_get_entity_state(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -168,6 +175,7 @@ void rtw89_entity_init(struct rtw89_dev *rtwdev); enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev); bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf); void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev); void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, @@ -180,19 +188,6 @@ void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, const struct rtw89_chanctx_cb_parm *cb_parm); -const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, - const char *caller_message, - u8 link_index, bool nullchk); - -#define rtw89_mgnt_chan_get(rtwdev, link_index) \ - __rtw89_mgnt_chan_get(rtwdev, __func__, link_index, false) - -static inline const struct rtw89_chan * -rtw89_mgnt_chan_get_or_null(struct rtw89_dev *rtwdev, u8 link_index) -{ - return __rtw89_mgnt_chan_get(rtwdev, NULL, link_index, true); -} - struct rtw89_mcc_links_info { struct rtw89_vif_link *links[NUM_OF_RTW89_MCC_ROLES]; }; diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 70feab97dccb..68dad6090f87 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -341,10 +341,26 @@ static const struct rtw89_hw_rate_def { }, }; +static void rtw89_core_tx_skb_proto_stats(struct rtw89_traffic_stats *stats, + struct sk_buff *skb) +{ + switch (ip_hdr(skb)->protocol) { + case IPPROTO_TCP: + stats->tcp++; + break; + case IPPROTO_UDP: + stats->udp++; + break; + default: + break; + } +} + static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats, struct sk_buff *skb, bool tx) { if (tx) { + rtw89_core_tx_skb_proto_stats(stats, skb); stats->tx_cnt++; stats->tx_unicast += skb->len; } else { @@ -458,16 +474,16 @@ static void __rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev, void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan; + struct rtw89_entity_conf conf; - chan = rtw89_mgnt_chan_get(rtwdev, 0); - __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_0); + rtw89_entity_get_conf(rtwdev, &conf); + + __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[0], RTW89_PHY_0); if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return; - chan = rtw89_mgnt_chan_get(rtwdev, 1); - __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_1); + __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[1], RTW89_PHY_1); } void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev, @@ -546,7 +562,7 @@ static void __rtw89_set_channel(struct rtw89_dev *rtwdev, int rtw89_set_channel(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan; + struct rtw89_entity_conf conf; enum rtw89_entity_mode mode; mode = rtw89_entity_recalc(rtwdev); @@ -555,14 +571,14 @@ int rtw89_set_channel(struct rtw89_dev *rtwdev) return -EINVAL; } - chan = rtw89_mgnt_chan_get(rtwdev, 0); - __rtw89_set_channel(rtwdev, chan, RTW89_MAC_0, RTW89_PHY_0); + rtw89_entity_get_conf(rtwdev, &conf); + + __rtw89_set_channel(rtwdev, conf.chans[0], RTW89_MAC_0, RTW89_PHY_0); if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return 0; - chan = rtw89_mgnt_chan_get(rtwdev, 1); - __rtw89_set_channel(rtwdev, chan, RTW89_MAC_1, RTW89_PHY_1); + __rtw89_set_channel(rtwdev, conf.chans[1], RTW89_MAC_1, RTW89_PHY_1); return 0; } @@ -838,6 +854,9 @@ u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel) case RTW89_TX_QSEL_B0_MGMT: case RTW89_TX_QSEL_B0_HI: return RTW89_TXCH_CH8; + case RTW89_TX_QSEL_B1_MGMT: + case RTW89_TX_QSEL_B1_HI: + return RTW89_TXCH_CH10; } } EXPORT_SYMBOL(rtw89_core_get_ch_dma_v2); @@ -931,6 +950,7 @@ __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev, enum btc_pkt_type pkt_type) { struct rtw89_sta_link *rtwsta_link = tx_req->rtwsta_link; + struct ieee80211_vif *vif = tx_req->vif; struct sk_buff *skb = tx_req->skb; struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_link_sta *link_sta; @@ -962,6 +982,9 @@ __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev, if (rtwsta_link && rtwsta_link->ra_report.might_fallback_legacy) return false; + if (vif->type == NL80211_IFTYPE_AP) + return false; + return true; } @@ -2020,29 +2043,14 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data, } } -#define VAR_LEN 0xff -#define VAR_LEN_UNIT 8 static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr) { - static const u8 physts_ie_len_tabs[RTW89_CHIP_GEN_NUM][32] = { - [RTW89_CHIP_AX] = { - 16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, - VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, - VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32 - }, - [RTW89_CHIP_BE] = { - 32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, - VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN, - VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32 - }, - }; - const u8 *physts_ie_len_tab; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const u8 *physts_ie_len_tab = phy->physt_ie_len; u16 ie_len; u8 ie; - physts_ie_len_tab = physts_ie_len_tabs[rtwdev->chip->chip_gen]; - ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE); if (physts_ie_len_tab[ie] != VAR_LEN) ie_len = physts_ie_len_tab[ie]; @@ -2077,11 +2085,9 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u32 t; phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX); - - if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { - phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); - phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); - } + phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); + phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); + phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); if (!phy_ppdu->hdr_2_en) phy_ppdu->rx_path_en = @@ -2094,6 +2100,7 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, return; phy_ppdu->rpl_avg = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_RSSI_AVG_FD); + phy_ppdu->su = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_SU); phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR); phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX); phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN); @@ -2148,14 +2155,50 @@ static void rtw89_core_parse_phy_status_ie00_v2(struct rtw89_dev *rtwdev, rpl_path[i] = tmp_rpl[i] >> 1; } +static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_iehdr *iehdr, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const union rtw89_phy_sts_ie09 *ie09; + u16 ie_len; + + ie09 = (const void *)iehdr; + + if (phy->physt_gen >= 2) { + ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)iehdr); + if (ie_len < sizeof(ie09->gen2)) + return; + } + + phy_ppdu->ie09 = ie09; +} + +static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_iehdr *iehdr, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + phy_ppdu->ie10 = (const void *)iehdr; +} + static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr, struct rtw89_rx_phy_ppdu *phy_ppdu) { + bool accept; u8 ie; ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE); + /* + * For normal mode, only parse ppdu_sts that are A1-matched, except for + * scanning that needs to get chan_idx in IE01. + */ + accept = phy_ppdu->to_self || ie == RTW89_PHYSTS_IE01_CMN_OFDM || + rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR; + if (!accept) + return 0; + switch (ie) { case RTW89_PHYSTS_IE00_CMN_CCK: rtw89_core_parse_phy_status_ie00(rtwdev, iehdr, phy_ppdu); @@ -2165,6 +2208,12 @@ static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, case RTW89_PHYSTS_IE01_CMN_OFDM: rtw89_core_parse_phy_status_ie01(rtwdev, iehdr, phy_ppdu); break; + case RTW89_PHYSTS_IE09_FTR_0: + rtw89_core_parse_phy_status_ie09(rtwdev, iehdr, phy_ppdu); + break; + case RTW89_PHYSTS_IE10_FTR_PLCP_EXT: + rtw89_core_parse_phy_status_ie10(rtwdev, iehdr, phy_ppdu); + break; default: break; } @@ -2224,11 +2273,14 @@ static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev, static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu) { - u16 ie_len; void *pos, *end; + bool accept; + u16 ie_len; - /* mark invalid reports and bypass them */ - if (phy_ppdu->ie < RTW89_CCK_PKT) + /* for normal mode, mark invalid reports and bypass them */ + accept = phy_ppdu->ie >= RTW89_CCK_PKT || + rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR; + if (!accept) return -EINVAL; pos = phy_ppdu->buf + PHY_STS_HDR_LEN; @@ -2238,14 +2290,27 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, while (pos < end) { const struct rtw89_phy_sts_iehdr *iehdr = pos; + /* + * RTL8922D might reports 8 bytes zeros at end if IE09 presents. + * Check and ignore the zeros. + */ + if (unlikely(phy_ppdu->ie09 && end - pos == 8 && iehdr->w0 == 0)) + break; + ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr); - rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); pos += ie_len; if (pos > end || ie_len == 0) { + /* clear pointers to prevent accessing out of IE */ + phy_ppdu->ie09 = NULL; + phy_ppdu->ie10 = NULL; + rtw89_debug(rtwdev, RTW89_DBG_TXRX, "phy status parse failed\n"); + return -EINVAL; } + + rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); } rtw89_chip_convert_rpl_to_rssi(rtwdev, phy_ppdu); @@ -2716,12 +2781,14 @@ static u16 rtw89_bcn_get_histogram_bound(struct rtw89_dev *rtwdev, u8 target) } static u16 rtw89_bcn_get_rx_time(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) + struct rtw89_vif_link *rtwvif_link) { #define RTW89_SYMBOL_TIME_2GHZ 192 #define RTW89_SYMBOL_TIME_5GHZ 20 #define RTW89_SYMBOL_TIME_6GHZ 20 - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + struct rtw89_pkt_stat *pkt_stat = &bb->cur_pkt_stat; u16 bitrate, val; if (!rtw89_legacy_rate_to_bitrate(rtwdev, pkt_stat->beacon_rate, &bitrate)) @@ -2752,15 +2819,15 @@ static void rtw89_bcn_calc_timeout(struct rtw89_dev *rtwdev, #define RTW89_BCN_TRACK_EXTEND_TIMEOUT 5 #define RTW89_BCN_TRACK_COVERAGE_TH 0 /* unit: TU */ #define RTW89_BCN_TRACK_STRONG_RSSI 80 - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; + struct rtw89_pkt_stat *pkt_stat = &bb->cur_pkt_stat; struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; u16 outlier_high_bcn_th = bcn_track->outlier_high_bcn_th; u16 outlier_low_bcn_th = bcn_track->outlier_low_bcn_th; - u8 rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); u16 target_bcn_th = bcn_track->target_bcn_th; + u8 rssi = ewma_rssi_read(&bb->bcn_rssi); u16 low_bcn_th = bcn_track->low_bcn_th; u16 med_bcn_th = bcn_track->med_bcn_th; u16 beacon_int = bcn_track->beacon_int; @@ -2806,7 +2873,7 @@ static void rtw89_bcn_calc_timeout(struct rtw89_dev *rtwdev, bcn_timeout = bcn_stat->drift[target_bcn_th]; out: - bcn_track->bcn_timeout = bcn_timeout + rtw89_bcn_get_rx_time(rtwdev, chan); + bcn_track->bcn_timeout = bcn_timeout + rtw89_bcn_get_rx_time(rtwdev, rtwvif_link); } static void rtw89_bcn_update_timeout(struct rtw89_dev *rtwdev, @@ -2902,6 +2969,23 @@ static void rtw89_core_bcn_track_reset(struct rtw89_dev *rtwdev) memset(&rtwdev->bcn_track, 0, sizeof(rtwdev->bcn_track)); } +static bool rtw89_core_proto_stats_can_lps(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + enum rtw89_tfc_interval interval) +{ + if (rtwdev->chip->chip_id != RTL8852A) + return true; + + if (rtwvif->burst_active) + return false; + + if (interval == RTW89_TFC_INTERVAL_100MS && + hweight8(rtwvif->stats_ps.active_histogram) < 3) + return false; + + return true; +} + static void rtw89_vif_rx_bcn_stat(struct rtw89_dev *rtwdev, struct sk_buff *skb) { #define RTW89_APPEND_TSF_2GHZ 384 @@ -2950,7 +3034,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct rtw89_vif_rx_stats_iter_data *iter_data = data; struct rtw89_dev *rtwdev = iter_data->rtwdev; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; struct sk_buff *skb = iter_data->skb; struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); @@ -2960,6 +3043,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_bss_conf *bss_conf; struct rtw89_vif_link *rtwvif_link; const u8 *bssid = iter_data->bssid; + struct rtw89_pkt_stat *pkt_stat; + struct rtw89_bb_ctx *bb; const u8 *target_bssid; if (rtwdev->scanning && @@ -2993,6 +3078,9 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, rx_status->link_id = rtwvif_link->link_id; } + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + pkt_stat = &bb->cur_pkt_stat; + if (ieee80211_is_beacon(hdr->frame_control)) { if (vif->type == NL80211_IFTYPE_STATION && !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) { @@ -3001,7 +3089,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, } if (phy_ppdu) { - ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg); + ewma_rssi_add(&bb->bcn_rssi, phy_ppdu->rssi_avg); if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx; } @@ -3019,6 +3107,23 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, if (desc_info->data_rate < RTW89_HW_RATE_NR) pkt_stat->rx_rate_cnt[desc_info->data_rate]++; + if (phy_ppdu && phy_ppdu->ofdm.has) { + if (phy_ppdu->ldpc) + pkt_stat->rx.ldpc++; + else + pkt_stat->rx.bcc++; + + if (phy_ppdu->stbc) + pkt_stat->rx.stbc++; + + if (!phy_ppdu->su) + pkt_stat->rx.mu++; + else if (phy_ppdu->bf) + pkt_stat->rx.su_bf++; + else + pkt_stat->rx.su_non_bf++; + } + rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, false, false); out: @@ -3087,12 +3192,496 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, if (!phy_ppdu) return; + if (phy_ppdu->bf) + rx_status->enc_flags |= RX_ENC_FLAG_BF; if (phy_ppdu->ldpc) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; if (phy_ppdu->stbc) rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK); } +static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const union rtw89_phy_sts_ie09 *ie09; + struct ieee80211_radiotap_vht *vht; + u8 group_id; + u32 sig_a1; + u16 paid; + u8 nss; + + if (!phy_ppdu) + return; + + ie09 = phy_ppdu->ie09; + if (!ie09) + return; + + vht = skb_push(skb, sizeof(*vht)); + memset(vht, 0, sizeof(*vht)); + rx_status->flag |= RX_FLAG_RADIOTAP_VHT; + + if (phy->physt_gen >= 2) + sig_a1 = le64_get_bits(ie09->gen2.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2); + else + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); + + group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID); + vht->group_id = group_id; + + if (group_id == 0 || group_id == 63) { + paid = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID); + vht->partial_aid = cpu_to_le16(paid); + } else { + /* let mac80211 fill vht->mcs_nss[0] */ +#define WITH_MCS_IS_NOT_KNOWN(nss) ((nss) ? (15 << 4) | (nss) : 0) + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1); + vht->mcs_nss[1] = WITH_MCS_IS_NOT_KNOWN(nss); + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2); + vht->mcs_nss[2] = WITH_MCS_IS_NOT_KNOWN(nss); + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3); + vht->mcs_nss[3] = WITH_MCS_IS_NOT_KNOWN(nss); +#undef WITH_MCS_IS_NOT_KNOWN + } +} + +static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + const union rtw89_phy_sts_ie09 *ie09) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + u32 sig_a1, sig_a2; + u16 t; + + if (!ie09) + return; + + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(ie09->gen2.qw1, + RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6) << 6; + } else { + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + } + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + 0); + + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + 0); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + rx_status->he_dcm = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_TXBF); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); +} + +static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + const union rtw89_phy_sts_ie09 *sig) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + u32 sig_a1, sig_a2; + u16 t; + + if (!sig) + return; + + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(sig->gen2.qw0, + RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(sig->gen2.qw0, + RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(sig->gen2.qw1, + RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6); + } else { + sig_a1 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); + sig_a2 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + } + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN); + + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); +} + +static u8 rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(struct rtw89_dev *rtwdev, + u8 ru_alloc) +{ + u8 mu_users; + + if (ru_alloc <= 15) { + static const u8 entries_0_15[16] = { + 9, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 5, + }; + + return entries_0_15[ru_alloc]; + } + + if (ru_alloc >= 16 && ru_alloc <= 95) { + mu_users = ru_alloc & 0x07; + + switch (ru_alloc & 0xF8) { + case 16: case 24: return mu_users + 3; + case 32: case 64: return mu_users + 6; + case 40: case 48: case 72: case 80: return mu_users + 5; + case 56: case 88: return mu_users + 4; + } + } + + if (ru_alloc >= 96 && ru_alloc <= 111) { + mu_users = ru_alloc & 0x03; + mu_users += ru_alloc >> 2 & 0x03; + + return mu_users + 2; + } + + if (ru_alloc == 112) + return 4; + + if (ru_alloc >= 113 && ru_alloc <= 115) + return 0; + + if (ru_alloc >= 128 && ru_alloc <= 191) { + mu_users = ru_alloc & 0x07; + mu_users += ru_alloc >> 3 & 0x07; + + return mu_users + 3; + } + + if (ru_alloc >= 192 && ru_alloc <= 215) + return (ru_alloc & 0x07) + 1; + + return 0; +} + +static int rtw89_core_he_mu_get_n_ru_alloc(struct rtw89_dev *rtwdev, u8 bw) +{ + switch (bw) { + case 0: + case 1: + return 1; + case 2: + case 4: + case 5: + return 2; + case 3: + case 6: + case 7: + return 3; + } + + return 0; +} + +static void rtw89_core_he_mu_get_cc_ptr(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_ie10 *ie10, + u8 bw, int n_ru, int n_center_26tone, + const u8 **c1, const u8 **c2) +{ + const u8 *sigb, *end_sigb; + int total_bits, c2_offset; + int n_user_fields = 0; + const u8 *ptr; + int i; + + sigb = &ie10->sigb[0]; + end_sigb = (const void *)ie10 + + rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)ie10); + + ptr = sigb; + if (ptr + n_ru + n_center_26tone >= end_sigb) + return; + *c1 = ptr; + + if (bw == 0) /* 20MHz doesn't have content channel 2 */ + return; + + for (i = 0; i < n_ru; i++) + n_user_fields += + rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(rtwdev, (*c1)[i]); + + /* hardware report max 15 recordes, align 8 */ + n_user_fields = min(n_user_fields, 15); + + total_bits = n_ru * 8 + n_center_26tone + 10 + + n_user_fields * 21 + DIV_ROUND_UP(n_user_fields, 2) * 10; + c2_offset = ALIGN(DIV_ROUND_UP(total_bits, 8), 8); + + ptr = &sigb[c2_offset]; + if (ptr + n_ru + n_center_26tone >= end_sigb) + return; + + *c2 = ptr; +} + +static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + const union rtw89_phy_sts_ie09 *ie09, + const struct rtw89_phy_sts_ie10 *ie10) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const u8 *c1 = NULL, *c2 = NULL; + int n_center_26tone, n_ru, i; + bool doppler, comp; + u32 sig_a1, sig_a2; + u16 t; + u8 bw; + + if (!ie09) + return; + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN); + + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(ie09->gen2.qw1, + RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6) << 6; + } else { + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + } + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + + doppler = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER); + he->data6 |= le16_encode_bits(doppler, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); + + if (doppler) { + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY); + } + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + + he_mu->flags1 |= cpu_to_le16(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_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN); + he_mu->flags2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS); + he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM); + he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR); + he_mu->flags2 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); + + comp = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM); + he_mu->flags2 |= le16_encode_bits(comp, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); + + bw = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW); + he_mu->flags2 |= le16_encode_bits(bw, IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); + + if (comp == 0 && bw <= 5) { + u8 punc = clamp_t(int, bw - 3, 0, 2); + + he_mu->flags2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN); + he_mu->flags2 |= + le16_encode_bits(punc, IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); + } + + /* the Common field in the HE-SIG-B field is not present */ + if (comp || !ie10) + return; + + n_ru = rtw89_core_he_mu_get_n_ru_alloc(rtwdev, bw); + n_center_26tone = bw >= 1; + + rtw89_core_he_mu_get_cc_ptr(rtwdev, ie10, bw, n_ru, n_center_26tone, &c1, &c2); + if (!c1) + return; + + he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN); + + for (i = 0; i < n_ru; i++) { + he_mu->ru_ch1[i] = c1[i]; + if (c2) + he_mu->ru_ch2[i] = c2[i]; + } + + if (n_center_26tone) { + u8 ru; + + ru = c1[n_ru] & BIT(0); + he_mu->flags1 |= + le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU); + + ru = c2 ? c2[n_ru] & BIT(0) : 0; + he_mu->flags1 |= + le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU); + } +} + +static u16 rtw89_core_get_radiotap_he_format(struct rtw89_rx_desc_info *desc_info) +{ + switch (desc_info->ppdu_type) { + case RTW89_RX_PPDU_T_HE_MU: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU; + case RTW89_RX_PPDU_T_HE_ERSU: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU; + case RTW89_RX_PPDU_T_HE_TB: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG; + default: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU; + } +} + +static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + static const struct ieee80211_radiotap_he known_he = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + const union rtw89_phy_sts_ie09 *ie09; + const struct rtw89_phy_sts_ie10 *ie10; + struct ieee80211_radiotap_he *he; + u16 he_format; + + he_format = rtw89_core_get_radiotap_he_format(desc_info); + + /* Radiotap of HE-MU must be placed after HE (skb_push ahead) */ + if (he_format == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU) { + rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; + he_mu = skb_push(skb, sizeof(*he_mu)); + memset(he_mu, 0, sizeof(*he_mu)); + } + + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + he = skb_push(skb, sizeof(*he)); + *he = known_he; + + he->data1 |= le16_encode_bits(he_format, IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK); + + if (!phy_ppdu) + return; + + ie09 = phy_ppdu->ie09; + ie10 = phy_ppdu->ie10; + + switch (he_format) { + case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU: + rtw89_core_update_radiotap_he_mu(rtwdev, skb, rx_status, he, he_mu, + ie09, ie10); + break; + case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG: + rtw89_core_update_radiotap_he_tb(rtwdev, skb, rx_status, he, ie09); + break; + default: + rtw89_core_update_radiotap_he_su(rtwdev, skb, rx_status, he, ie09); + break; + } +} + static const u8 rx_status_bw_to_radiotap_eht_usig[] = { [RATE_INFO_BW_20] = IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_20MHZ, [RATE_INFO_BW_5] = U8_MAX, @@ -3164,28 +3753,27 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev, } static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) { - static const struct ieee80211_radiotap_he known_he = { - .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), - .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), - }; - struct ieee80211_radiotap_he *he; - if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; - if (rx_status->encoding == RX_ENC_HE) { - rx_status->flag |= RX_FLAG_RADIOTAP_HE; - he = skb_push(skb, sizeof(*he)); - *he = known_he; - } else if (rx_status->encoding == RX_ENC_EHT) { + /* + * At transient adding a monitor vif from a station vif, the headroom + * might not include radiotap. + */ + if (unlikely(skb_headroom(skb) < RTW89_RADIOTAP_ROOM + NET_SKB_PAD)) + return; + + if (rx_status->encoding == RX_ENC_VHT) + rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); + else if (rx_status->encoding == RX_ENC_HE) + rtw89_core_update_radiotap_he(rtwdev, desc_info, skb, rx_status, phy_ppdu); + else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); - } } static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status) @@ -3363,7 +3951,8 @@ static bool rtw89_core_skb_pn_valid(struct rtw89_dev *rtwdev, last_pn = tid_stats->last_pn; if (pn > last_pn) { - if (ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { + if (last_pn != -1LL && + ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { dev_kfree_skb_any(skb); return false; @@ -3395,7 +3984,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); - rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); + rtw89_core_update_radiotap(rtwdev, desc_info, skb_ppdu, rx_status, phy_ppdu); rtw89_core_validate_rx_signal(rx_status); rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu); @@ -3509,6 +4098,7 @@ void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, AX_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, AX_RXD_AMPDU); shift_len = desc_info->shift << 1; /* 2-byte unit */ drv_info_len = desc_info->drv_info_size << 3; /* 8-byte unit */ @@ -3565,6 +4155,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU); desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK); desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK); @@ -3639,6 +4230,7 @@ void rtw89_core_query_rxdesc_v3(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU); desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK); desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK); @@ -3771,6 +4363,11 @@ 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->ampdu) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->ampdu_reference = desc_info->ppdu_cnt; + } + rx_status->bw = rtw89_hw_to_rate_info_bw(desc_info->bw); data_rate = desc_info->data_rate; @@ -4441,6 +5038,7 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH); + rtw89_phy_dig_suspend(rtwdev); ieee80211_ready_on_channel(hw); wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work, @@ -4485,6 +5083,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_core_handle_sta_pending_tx(rtwdev, rtwvif_link); queue_work(rtwdev->txq_wq, &rtwdev->txq_work); + rtw89_phy_dig_resume(rtwdev, true); if (hw->conf.flags & IEEE80211_CONF_IDLE) wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work, @@ -4540,9 +5139,42 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev, return RTW89_TFC_ULTRA_LOW; } +static void rtw89_calc_vif_active_histogram(struct rtw89_dev *rtwdev, + struct rtw89_traffic_stats *stats, + enum rtw89_tfc_interval interval) +{ + struct rtw89_vif *rtwvif; + + stats->udp_ratio = stats->tx_cnt ? + DIV_ROUND_DOWN_ULL(stats->udp * 100, stats->tx_cnt) : 0; + stats->active_histogram <<= 1; + + switch (interval) { + case RTW89_TFC_INTERVAL_2SEC: + rtwvif = container_of(stats, struct rtw89_vif, stats); + + if (stats->tcp >= RTW89_TCP_TH && stats->tx_cnt >= stats->rx_cnt) + stats->active_histogram |= BIT(0); + + if (stats->active_histogram & RTW89_RECENT_ACTIVE_HIST) + rtwvif->burst_active = true; + else + rtwvif->burst_active = false; + + break; + case RTW89_TFC_INTERVAL_100MS: + if (stats->udp_ratio >= RTW89_UDP_RATIO_TH) + stats->active_histogram |= BIT(0); + break; + } + + stats->tcp = 0; + stats->udp = 0; +} + static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats, - enum rtw89_tfc_interval interval) + enum rtw89_tfc_interval interval, bool by_vif) { enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv; enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv; @@ -4564,6 +5196,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, stats->rx_avg_len = stats->rx_cnt ? DIV_ROUND_DOWN_ULL(stats->rx_unicast, stats->rx_cnt) : 0; + if (by_vif) + rtw89_calc_vif_active_histogram(rtwdev, stats, interval); + stats->tx_unicast = 0; stats->rx_unicast = 0; stats->tx_cnt = 0; @@ -4585,11 +5220,11 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) bool tfc_changed; tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats, - RTW89_TFC_INTERVAL_2SEC); + RTW89_TFC_INTERVAL_2SEC, false); rtw89_for_each_rtwvif(rtwdev, rtwvif) { rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats, - RTW89_TFC_INTERVAL_2SEC); + RTW89_TFC_INTERVAL_2SEC, true); rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link); @@ -4598,7 +5233,8 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) return tfc_changed; } -static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev) +static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev, + enum rtw89_tfc_interval interval) { struct ieee80211_vif *vif; struct rtw89_vif *rtwvif; @@ -4622,6 +5258,9 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev) if (!rtw89_core_bcn_track_can_lps(rtwdev)) continue; + if (!rtw89_core_proto_stats_can_lps(rtwdev, rtwvif, interval)) + continue; + rtw89_enter_lps(rtwdev, rtwvif, true); } } @@ -4667,14 +5306,22 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, { unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS; struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + u8 decided_bands = BIT(RTW89_BAND_NUM) - 1; struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; unsigned long usable_links; + struct rtw89_bb_ctx *bb; unsigned int link_id; - u8 decided_bands; u8 rssi; - rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + usable_links = ieee80211_vif_usable_links(vif); + + rtwvif_link = rtw89_get_designated_link(rtwvif); + if (unlikely(!rtwvif_link)) + goto select; + + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + rssi = ewma_rssi_read(&bb->bcn_rssi); if (unlikely(!rssi)) return; @@ -4685,12 +5332,6 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, else return; - usable_links = ieee80211_vif_usable_links(vif); - - rtwvif_link = rtw89_get_designated_link(rtwvif); - if (unlikely(!rtwvif_link)) - goto select; - chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); if (decided_bands & BIT(chan->band_type)) return; @@ -4771,13 +5412,13 @@ static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_for_each_rtwvif(rtwdev, rtwvif) rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps, - RTW89_TFC_INTERVAL_100MS); + RTW89_TFC_INTERVAL_100MS, true); if (rtwdev->scanning) return; if (rtwdev->lps_enabled && !rtwdev->btc.lps) - rtw89_enter_lps_track(rtwdev); + rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_100MS); } static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -4825,7 +5466,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_core_mlo_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) - rtw89_enter_lps_track(rtwdev); + rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_2SEC); } void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new) @@ -6229,6 +6870,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) struct rtw89_btc *btc = &rtwdev->btc; u8 band; + rtwdev->io = rtw89_fw_cmd_ofld_alloc_and_get_io_ops(rtwdev); + bitmap_or(rtwdev->quirks, rtwdev->quirks, &rtwdev->chip->default_quirks, NUM_OF_RTW89_QUIRKS); @@ -6524,6 +7167,8 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) rtw89_chip_rfk_channel(rtwdev, target); + rtw89_fw_h2c_tx_history(rtwdev, target->mac_id); + rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; wake_queue: @@ -6671,6 +7316,10 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev) if (ret) goto out; + ret = rtw89_chip_data_setup(rtwdev); + if (ret) + goto out; + rtw89_core_setup_rfe_parms(rtwdev); rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev); @@ -6733,7 +7382,8 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_STBC; - hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC; + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, HAS_RATE_CONTROL); @@ -6882,9 +7532,11 @@ EXPORT_SYMBOL(rtw89_core_unregister); struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, - const struct rtw89_chip_info *chip, - const struct rtw89_chip_variant *variant) + const struct rtw89_driver_info *info) { + const unsigned long *dev_id_quirks = &info->dev_id_quirks; + const struct rtw89_chip_variant *variant = info->variant; + const struct rtw89_chip_info *chip = info->chip; struct rtw89_fw_info early_fw = {}; const struct firmware *firmware; struct ieee80211_hw *hw; @@ -6950,6 +7602,9 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, rtwdev->fw.fw_format = fw_format; rtwdev->support_mlo = support_mlo; + bitmap_or(rtwdev->quirks, rtwdev->quirks, dev_id_quirks, + NUM_OF_RTW89_QUIRKS); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, "probe driver %s chanctx\n", no_chanctx ? "without" : "with"); rtw89_debug(rtwdev, RTW89_DBG_CHAN, "probe driver %s MLO cap\n", diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fd29dbbb120d..5547888d7e67 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -24,9 +24,13 @@ struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_phy_calc_efuse_gain; +union rtw89_phy_sts_ie09; +struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; +struct rtw89_bb_wrap_data; +struct rtw89_fw_cmd_ofld_info; extern const struct ieee80211_ops rtw89_ops; @@ -55,14 +59,17 @@ extern const struct ieee80211_ops rtw89_ops; #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR) #define DELTA_SWINGIDX_SIZE 30 -#define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he) +#define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht) +#define RTW89_RADIOTAP_ROOM_HE (sizeof(struct ieee80211_radiotap_he) + \ + sizeof(struct ieee80211_radiotap_he_mu)) #define RTW89_RADIOTAP_ROOM_EHT \ (sizeof(struct ieee80211_radiotap_tlv) + \ ALIGN(struct_size((struct ieee80211_radiotap_eht *)0, user_info, 1), 4) + \ sizeof(struct ieee80211_radiotap_tlv) + \ ALIGN(sizeof(struct ieee80211_radiotap_eht_usig), 4)) #define RTW89_RADIOTAP_ROOM \ - ALIGN(max(RTW89_RADIOTAP_ROOM_HE, RTW89_RADIOTAP_ROOM_EHT), 64) + ALIGN(max3(RTW89_RADIOTAP_ROOM_VHT, RTW89_RADIOTAP_ROOM_HE, \ + RTW89_RADIOTAP_ROOM_EHT), 64) #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0) #define RTW89_HTC_VARIANT_HE 3 @@ -839,7 +846,7 @@ struct rtw89_rx_phy_ppdu { u8 mac_id; u8 chan_idx; u8 phy_idx; - u8 ie; + u8 ie; /* enum rtw89_phy_status_bitmap */ u16 rate; u8 rpl_avg; u8 rpl_path[RF_PATH_MAX]; @@ -854,11 +861,15 @@ struct rtw89_rx_phy_ppdu { } ofdm; bool has_data; bool has_bcn; + bool su; bool ldpc; bool stbc; + bool bf; bool to_self; bool valid; bool hdr_2_en; + const union rtw89_phy_sts_ie09 *ie09; /* SIG-A */ + const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */ }; enum rtw89_mac_idx { @@ -946,6 +957,21 @@ enum rtw89_bandwidth { RTW89_CHANNEL_WIDTH_10 = 7, }; +enum rtw89_rx_ppdu_type { + RTW89_RX_PPDU_T_LCCK = 0, + RTW89_RX_PPDU_T_SCCK = 1, + RTW89_RX_PPDU_T_OFDM = 2, + RTW89_RX_PPDU_T_HT = 3, + RTW89_RX_PPDU_T_HTGF = 4, + RTW89_RX_PPDU_T_VHT_SU = 5, + RTW89_RX_PPDU_T_VHT_MU = 6, + RTW89_RX_PPDU_T_HE_SU = 7, + RTW89_RX_PPDU_T_HE_ERSU = 8, + RTW89_RX_PPDU_T_HE_MU = 9, + RTW89_RX_PPDU_T_HE_TB = 10, + RTW89_RX_PPDU_T_UNKNOWN = 15, +}; + enum rtw89_ps_mode { RTW89_PS_MODE_NONE = 0, RTW89_PS_MODE_RFOFF = 1, @@ -972,6 +998,9 @@ enum rtw89_ru_bandwidth { RTW89_RU106 = 2, RTW89_RU52_26 = 3, RTW89_RU106_26 = 4, + RTW89_RU484_242 = 5, + RTW89_RU996_484 = 6, + RTW89_RU996_484_242 = 7, RTW89_RU_NUM, }; @@ -989,6 +1018,13 @@ enum rtw89_sc_offset { RTW89_SC_40_LOWER = 10, }; +enum rtw89_rfsi_ctrl_band { + RFSI_CTRL_BAND_5_6GHZ, + RFSI_CTRL_BAND_2GHZ, + + RFSI_CTRL_BAND_NUM, +}; + /* only mgd features can be added to the enum */ enum rtw89_wow_flags { RTW89_WOW_FLAG_EN_MAGIC_PKT, @@ -1012,6 +1048,7 @@ struct rtw89_chan { enum rtw89_tx_comp_band tx_comp_band; enum rtw89_sc_offset pri_ch_idx; u8 pri_sb_idx; + enum rtw89_rfsi_ctrl_band rfsi_band; }; struct rtw89_chan_rcd { @@ -1127,6 +1164,7 @@ struct rtw89_rx_desc_info { bool hw_dec; bool sw_dec; bool addr1_match; + bool ampdu; u8 frag; u16 seq; u8 frame_type; @@ -1477,6 +1515,10 @@ enum rtw89_tfc_lv { DECLARE_EWMA(tp, 10, 2); +#define RTW89_TCP_TH 40 +#define RTW89_UDP_RATIO_TH 70 +#define RTW89_RECENT_ACTIVE_HIST GENMASK(4, 0) + struct rtw89_traffic_stats { /* units in bytes */ u64 tx_unicast; @@ -1504,6 +1546,10 @@ struct rtw89_traffic_stats { u16 tx_rate; u16 rx_rate; + + /* used by rtwvif only */ + u64 tcp, udp, udp_ratio; + u8 active_histogram; }; struct rtw89_btc_chdef { @@ -3479,6 +3525,7 @@ struct rtw89_ra_report { struct rate_info txrate; u32 bit_rate; u16 hw_rate; + u8 retry_ratio; bool might_fallback_legacy; }; @@ -3571,6 +3618,9 @@ struct rtw89_sta_link { u32 data_tx_cnt_lmt:6; }; +#define RTW89_EFUSE_SN_LEN 5 +#define RTW89_EFUSE_UUID_LEN 16 + struct rtw89_efuse { bool valid; bool power_k_valid; @@ -3581,6 +3631,8 @@ struct rtw89_efuse { u8 adc_td; u8 bt_setting_2; u8 bt_setting_3; + u8 sn[RTW89_EFUSE_SN_LEN]; + u8 uuid[RTW89_EFUSE_UUID_LEN]; }; struct rtw89_phy_rate_pattern { @@ -3816,6 +3868,7 @@ struct rtw89_chip_ops { enum rtw89_efuse_block block); int (*read_phycap)(struct rtw89_dev *rtwdev, u8 *phycap_map); void (*fem_setup)(struct rtw89_dev *rtwdev); + int (*data_setup)(struct rtw89_dev *rtwdev); void (*rfe_gpio)(struct rtw89_dev *rtwdev); void (*rfk_hw_init)(struct rtw89_dev *rtwdev); void (*rfk_init)(struct rtw89_dev *rtwdev); @@ -4122,6 +4175,7 @@ struct rtw89_dle_input { u32 h2c_max_size; u32 rx_amsdu_size; /* unit: KB */ u32 c2h_max_size; + u32 rls_rpt_max_size; u32 mpdu_info_tbl_b0; u32 mpdu_info_tbl_b1; }; @@ -4166,6 +4220,12 @@ struct rtw89_reg5_def { u32 data; }; +#define RTW89_REGS_DEF(x) {x, ARRAY_SIZE(x)} +struct rtw89_regs_def { + const u32 *regs; + u32 reg_nr; +}; + struct rtw89_reg_imr { u32 addr; u32 clr; @@ -4187,6 +4247,11 @@ struct rtw89_fw_def { u16 fw_b_aid; }; +struct rtw89_qta_def { + const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; + const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; +}; + struct rtw89_phy_table { const struct rtw89_reg2_def *regs; u32 n_regs; @@ -4478,6 +4543,54 @@ struct rtw89_edcca_regs { u32 tx_collision_t2r_st_mask; }; +struct rtw89_pmac_regs { + struct rtw89_reg_def cck_txon; + struct rtw89_reg_def cck_txen; + struct rtw89_reg_def cck_cca; + struct rtw89_reg_def cck_sfd_gg; + struct rtw89_reg_def cck_sig_gg; + struct rtw89_reg_def cck_spoofing; + struct rtw89_reg_def cck_brk; + struct rtw89_reg_def brk; + struct rtw89_reg_def brk_option; + struct rtw89_reg_def search_fail; + struct rtw89_reg_def lsig_brk_s_th; + struct rtw89_reg_def lsig_brk_l_th; + struct rtw89_reg_def rxl_err_parity; + struct rtw89_reg_def rxl_err_rate; + struct rtw89_reg_def ofdm_cca; + struct rtw89_reg_def cca_spoofing; + struct rtw89_reg_def ampdu_miss; + struct rtw89_reg_def r1b_rx_rpt_rst; + struct rtw89_reg_def r1b_rr_sel; + struct rtw89_reg_def enable_all_cnt; + struct rtw89_reg_def rst_all_cnt; + u32 cck_crc32; + u32 cck_crc32_ok_mask; + u32 cck_crc32_fail_mask; + u32 ofdm_txon; + u32 ofdm_txon_mask; + u32 ofdm_txen_mask; + u32 l_crc; + u32 l_crc_ok_mask; + u32 l_crc_err_mask; + u32 ht_crc; + u32 ht_crc_ok_mask; + u32 ht_crc_err_mask; + u32 vht_crc; + u32 vht_crc_ok_mask; + u32 vht_crc_err_mask; + u32 he_crc; + u32 he_crc_ok_mask; + u32 he_crc_err_mask; + u32 eht_crc; + u32 eht_crc_ok_mask; + u32 eht_crc_err_mask; + u32 ampdu_crc; + u32 ampdu_crc_ok_mask; + u32 ampdu_crc_err_mask; +}; + struct rtw89_phy_ul_tb_info { bool dyn_tb_tri_en; u8 def_if_bandedge; @@ -4502,6 +4615,16 @@ struct rtw89_antdiv_info { bool get_stats; }; +struct rtw89_bb_stat_cfg { + bool enable; + u16 mac_id; +}; + +struct rtw89_phy_info { + const struct rtw89_bb_wrap_data *bb_wrap_data; + struct rtw89_bb_stat_cfg bb_stat_cfg; +}; + enum rtw89_chanctx_state { RTW89_CHANCTX_STATE_MCC_START, RTW89_CHANCTX_STATE_MCC_STOP, @@ -4544,8 +4667,7 @@ struct rtw89_chip_info { u16 max_rx_agg_num; bool dis_2g_40m_ul_ofdma; u32 rsvd_ple_ofst; - const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_TYPE_NUM]; - const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; + struct rtw89_qta_def qta_def; u8 wde_qempty_acq_grpnum; u8 wde_qempty_mgq_grpsel; u32 rf_base_addr[2]; @@ -4561,6 +4683,7 @@ struct rtw89_chip_info { bool support_tas; bool support_sar_by_ant; bool support_noise; + bool support_fw_cmd_ofld; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; bool rx_freq_from_ie; @@ -4663,6 +4786,7 @@ struct rtw89_chip_info { struct rtw89_sb_regs btc_sb; u32 dma_ch_mask; const struct rtw89_edcca_regs *edcca_regs; + const struct rtw89_pmac_regs *pmac_regs; const struct wiphy_wowlan_support *wowlan_stub; const struct rtw89_xtal_info *xtal_info; unsigned long default_quirks; /* bitmap of rtw89_quirks */ @@ -4672,6 +4796,7 @@ struct rtw89_chip_variant { bool no_mcs_12_13: 1; u32 fw_min_ver_code; const struct rtw89_fw_def *fw_def_override; + const struct rtw89_qta_def *qta_def_override; }; union rtw89_bus_info { @@ -4683,6 +4808,7 @@ struct rtw89_driver_info { const struct rtw89_chip_info *chip; const struct rtw89_chip_variant *variant; const struct dmi_system_id *quirks; + unsigned long dev_id_quirks; /* bitmap of rtw89_quirks */ union rtw89_bus_info bus; }; @@ -4821,6 +4947,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_SIM_SER_L0L1_BY_HALT_H2C, RTW89_FW_FEATURE_LPS_ML_INFO_V1, RTW89_FW_FEATURE_SER_POST_RECOVER_DMAC, + RTW89_FW_FEATURE_TX_HISTORY_V1, NUM_OF_RTW89_FW_FEATURES, }; @@ -5170,6 +5297,9 @@ enum rtw89_dm_type { RTW89_DM_THERMAL_PROTECT, RTW89_DM_TAS, RTW89_DM_MLO, + RTW89_DM_HW_SCAN, + RTW89_DM_INACTIVE_PS, + RTW89_DM_DIG_PD, }; #define RTW89_THERMAL_PROT_LV_MAX 5 @@ -5211,6 +5341,9 @@ struct rtw89_hal { u8 thermal_prot_th; u8 thermal_prot_lv; /* 0 ~ RTW89_THERMAL_PROT_LV_MAX */ + + u8 fixed_dig_pd_th; /* v = (X(dBm) + 102)/2 */ + s8 fixed_dig_cck_pd_th; /* dBm */ }; #define RTW89_MAX_MAC_ID_NUM 128 @@ -5248,6 +5381,7 @@ enum rtw89_quirks { RTW89_QUIRK_PCI_BER, RTW89_QUIRK_THERMAL_PROT_120C, RTW89_QUIRK_THERMAL_PROT_110C, + RTW89_QUIRK_HW_INFO_SYSFS, NUM_OF_RTW89_QUIRKS, }; @@ -5296,6 +5430,14 @@ struct rtw89_pkt_stat { u8 beacon_rate; u32 beacon_len; u32 rx_rate_cnt[RTW89_HW_RATE_NR]; + struct { + u32 ldpc; + u32 bcc; + u32 stbc; + u32 su_bf; + u32 su_non_bf; + u32 mu; + } rx; }; #define RTW89_BCN_TRACK_STAT_NR 32 @@ -5326,12 +5468,11 @@ struct rtw89_beacon_stat { DECLARE_EWMA(thermal, 4, 4); +#define RTW89_TX_RATE_NR 40 struct rtw89_phy_stat { struct ewma_thermal avg_thermal[RF_PATH_MAX]; u8 last_thermal_max; - struct ewma_rssi bcn_rssi; - struct rtw89_pkt_stat cur_pkt_stat; - struct rtw89_pkt_stat last_pkt_stat; + u32 tx_rate_cnt[RTW89_TX_RATE_NR]; struct rtw89_beacon_stat bcn_stat; }; @@ -5480,6 +5621,78 @@ struct rtw89_phy_ch_info { u8 is_noisy; }; +struct rtw89_pmac_stat_info { + u32 cck_phy_txon; + u32 cck_mac_txen; + u32 ofdm_mac_txen; + u32 ofdm_phy_txon; + u32 cnt_ofdm_cca; + u32 cnt_cck_cca; + u32 cnt_cca_all; + u32 cnt_cck_spoofing; + u32 cnt_ofdm_spoofing; + u32 cnt_ampdu_miss; + u32 cnt_ampdu_crc_error; + u32 cnt_ampdu_crc_ok; + u32 cnt_cck_crc32_error; + u32 cnt_cck_crc32_ok; + u32 cnt_ofdm_crc32_error; + u32 cnt_ofdm_crc32_ok; + u32 cnt_ht_crc32_error; + u32 cnt_ht_crc32_ok; + u32 cnt_vht_crc32_error; + u32 cnt_vht_crc32_ok; + u32 cnt_he_crc32_ok; + u32 cnt_he_crc32_error; + u32 cnt_eht_crc32_ok; + u32 cnt_eht_crc32_error; + u32 cnt_crc32_error_all; + u32 cnt_crc32_ok_all; + u32 cnt_sfd_gg; + u32 cnt_sig_gg; + u32 cnt_cck_fail; + u32 cnt_ofdm_fail; + u32 cnt_fail_all; + u32 cnt_lsig_brk_s_th; + u32 cnt_lsig_brk_l_th; + u32 cnt_parity_fail; + u32 cnt_rate_illegal; + u32 cnt_sb_search_fail; +}; + +struct rtw89_tx_stat_info { + u32 info[6]; + u32 common_ctrl[2]; + u32 txpwr[2]; + u8 type; + u8 subtype; + u8 txcmd; + u8 txsc; + u8 bw; + u16 tmac_txpwr; + u8 tx_path_en; + u8 path_map; + u8 max_mcs; + bool stbc; +}; + +enum rtw89_diag_bb_type { + RTW89_DIAG_BB_HANG, + RTW89_DIAG_BB_PD, + RTW89_DIAG_BB_NO_RX, + RTW89_DIAG_BB_FA, + RTW89_DIAG_BB_EDCCA, + + RTW89_DIAG_BB_NR, +}; + +struct rtw89_diag_bb { + u32 diag_bb_bitmap; /* bitmap of enum rtw89_diag_bb_type */ + u32 diag_bb_cnt[RTW89_DIAG_BB_NR]; + u16 consecutive_no_tx_cnt; + u16 consecutive_no_rx_cnt; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -5495,6 +5708,7 @@ struct rtw89_dig_info { struct rtw89_agc_gaincode_set cur_gaincode; bool force_gaincode_idx_en; struct rtw89_agc_gaincode_set force_gaincode; + enum rtw89_dig_noisy_level noisy_lv; u8 igi_rssi_th[IGI_RSSI_TH_NUM]; u16 fa_th[FA_TH_NUM]; u8 igi_rssi; @@ -5504,6 +5718,7 @@ struct rtw89_dig_info { u8 dyn_igi_min; bool dyn_pd_th_en; u8 dyn_pd_th_max; + u8 dyn_pd_max_cnt; u8 pd_low_th_ofst; u8 ib_pbk; s8 ib_pkpwr; @@ -5768,6 +5983,7 @@ struct rtw89_env_monitor_info { u8 nhm_th[RTW89_NHM_RPT_NUM]; struct rtw89_nhm_report *nhm_his[RTW89_BAND_NUM]; struct list_head nhm_rpt_list; + u8 edcca_clm_ratio; }; enum rtw89_ser_rcvy_step { @@ -6188,10 +6404,26 @@ struct rtw89_tid_stats { bool started; }; +struct rtw89_io_ops { + int (*pack)(struct rtw89_dev *rtwdev); + int (*unpack)(struct rtw89_dev *rtwdev); + void (*do_udelay)(struct rtw89_dev *rtwdev, u32 us); + void (*do_mdelay)(struct rtw89_dev *rtwdev, u32 ms); + void (*write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data); + void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); + void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + void (*phy_write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data); + void (*phy_write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); + void (*phy_write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + void (*write_rf)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data); +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; const struct ieee80211_ops *ops; + const struct rtw89_io_ops *io; bool dbcc_en; bool support_mlo; @@ -6213,6 +6445,8 @@ struct rtw89_dev { struct rtw89_rfe_data *rfe_data; enum rtw89_custid custid; + struct rtw89_fw_cmd_ofld_info *fw_cmd_ofld_info; + struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM]; refcount_t refcount_ap_info; @@ -6277,6 +6511,7 @@ struct rtw89_dev { struct rtw89_phy_efuse_gain efuse_gain; struct rtw89_phy_ul_tb_info ul_tb_info; struct rtw89_antdiv_info antdiv; + struct rtw89_phy_info phy_info; struct rtw89_bb_ctx { enum rtw89_phy_idx phy_idx; @@ -6284,6 +6519,12 @@ struct rtw89_dev { struct rtw89_dig_info dig; struct rtw89_phy_ch_info ch_info; struct rtw89_edcca_bak edcca_bak; + struct ewma_rssi bcn_rssi; + struct rtw89_pkt_stat cur_pkt_stat; + struct rtw89_pkt_stat last_pkt_stat; + struct rtw89_pmac_stat_info pmac_stat; + struct rtw89_tx_stat_info tx_stat; + struct rtw89_diag_bb diag; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; @@ -6354,6 +6595,7 @@ struct rtw89_vif { struct rtw89_roc roc; bool offchan; + bool burst_active; enum rtw89_mlo_mode mlo_mode; struct rtw89_vif_ml_trans ml_trans; @@ -6492,6 +6734,9 @@ static inline void rtw89_assoc_link_clr(struct rtw89_sta_link *rtwsta_link) static inline struct rtw89_sta_link * rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid) { + if (unlikely(macid >= RTW89_MAX_MAC_ID_NUM)) + return NULL; + return rcu_dereference(rtwdev->assoc_link_on_macid[macid]); } @@ -6673,6 +6918,38 @@ struct rtw89_tx_skb_data *RTW89_TX_SKB_CB(struct sk_buff *skb) return (struct rtw89_tx_skb_data *)info->driver_data; } +static inline int rtw89_io_pack(struct rtw89_dev *rtwdev) +{ + if (rtwdev->io->pack) + return rtwdev->io->pack(rtwdev); + + return 0; +} + +static inline int rtw89_io_unpack(struct rtw89_dev *rtwdev) +{ + if (rtwdev->io->unpack) + return rtwdev->io->unpack(rtwdev); + + return 0; +} + +static inline void rtw89_io_udelay(struct rtw89_dev *rtwdev, u32 us) +{ + if (rtwdev->io->do_udelay) + rtwdev->io->do_udelay(rtwdev, us); + else + udelay(us); +} + +static inline void rtw89_io_mdelay(struct rtw89_dev *rtwdev, u32 ms) +{ + if (rtwdev->io->do_mdelay) + rtwdev->io->do_mdelay(rtwdev, ms); + else + mdelay(ms); +} + static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read8(rtwdev, addr); @@ -6688,21 +6965,36 @@ static inline u32 rtw89_read32(struct rtw89_dev *rtwdev, u32 addr) return rtwdev->hci.ops->read32(rtwdev, addr); } -static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +static inline void rtw89_raw_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) { rtwdev->hci.ops->write8(rtwdev, addr, data); } -static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +static inline void rtw89_raw_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) { rtwdev->hci.ops->write16(rtwdev, addr, data); } -static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +static inline void rtw89_raw_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) { rtwdev->hci.ops->write32(rtwdev, addr, data); } +static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + rtwdev->io->write8(rtwdev, addr, data); +} + +static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + rtwdev->io->write16(rtwdev, addr, data); +} + +static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + rtwdev->io->write32(rtwdev, addr, data); +} + static inline void rtw89_write8_set(struct rtw89_dev *rtwdev, u32 addr, u8 bit) { @@ -6847,13 +7139,20 @@ rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, return rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); } +static inline void +rtw89_raw_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data) +{ + rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); +} + static inline void rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data) { lockdep_assert_wiphy(rtwdev->hw->wiphy); - rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); + rtwdev->io->write_rf(rtwdev, rf_path, addr, mask, data); } static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr) @@ -7212,6 +7511,16 @@ static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev) chip->ops->fem_setup(rtwdev); } +static inline int rtw89_chip_data_setup(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (!chip->ops->data_setup) + return 0; + + return chip->ops->data_setup(rtwdev); +} + static inline void rtw89_chip_rfe_gpio(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; @@ -7449,6 +7758,17 @@ const struct rtw89_fw_def *rtw89_chip_get_fw_def(struct rtw89_dev *rtwdev) return __rtw89_chip_get_fw_def(rtwdev->chip, rtwdev->variant); } +static inline +const struct rtw89_qta_def *rtw89_chip_get_qta_def(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_variant *variant = rtwdev->variant; + + if (variant && variant->qta_def_override) + return variant->qta_def_override; + + return &rtwdev->chip->qta_def; +} + static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl) { @@ -7816,8 +8136,7 @@ int rtw89_core_register(struct rtw89_dev *rtwdev); void rtw89_core_unregister(struct rtw89_dev *rtwdev); struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, - const struct rtw89_chip_info *chip, - const struct rtw89_chip_variant *variant); + const struct rtw89_driver_info *info); void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev); u8 rtw89_acquire_mac_id(struct rtw89_dev *rtwdev); void rtw89_release_mac_id(struct rtw89_dev *rtwdev, u8 mac_id); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 7d8d22311018..8f5af873e09f 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -85,11 +85,15 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv btc_manual; struct rtw89_debugfs_priv fw_log_manual; struct rtw89_debugfs_priv phy_info; + struct rtw89_debugfs_priv bb_info; struct rtw89_debugfs_priv stations; struct rtw89_debugfs_priv disable_dm; + struct rtw89_debugfs_priv static_pd_th; struct rtw89_debugfs_priv mlo_mode; struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; + struct rtw89_debugfs_priv diag_bb; + struct rtw89_debugfs_priv monitor_opts; }; struct rtw89_debugfs_iter_data { @@ -763,6 +767,45 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = { .addr_to_1ss = 0, /* not support */ }; +static const struct txpwr_ent __txpwr_ent_lmt_ru484_242_be[] = { + __GEN_TXPWR_ENT4("RU484_242 1TX ", "IDX_0 ", "IDX_1 ", "IDX_2 ", "IDX_3 "), + __GEN_TXPWR_ENT4("RU484_242 2TX ", "IDX_0 ", "IDX_1 ", "IDX_2 ", "IDX_3 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru484_242_be = { + .ent = __txpwr_ent_lmt_ru484_242_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru484_242_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU484_242_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU484_242_0 + 4, + .addr_to_1ss = 0, /* not support */ +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru996_484_be[] = { + __GEN_TXPWR_ENT2("RU996_484 1TX ", "IDX_0 ", "IDX_1 "), + __GEN_TXPWR_ENT2("RU996_484 2TX ", "IDX_0 ", "IDX_1 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru996_484_be = { + .ent = __txpwr_ent_lmt_ru996_484_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru996_484_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU996_484_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU996_484_0, + .addr_to_1ss = 0, /* not support */ +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru996_484_242_be[] = { + __GEN_TXPWR_ENT2("RU996_484_242 1TX ", "IDX_0 ", "IDX_1 "), + __GEN_TXPWR_ENT2("RU996_484_242 2TX ", "IDX_0 ", "IDX_1 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru996_484_242_be = { + .ent = __txpwr_ent_lmt_ru996_484_242_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru996_484_242_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU996_484_242_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU996_484_242_0, + .addr_to_1ss = 0, /* not support */ +}; + static unsigned int __print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent, const s8 *bufp, const unsigned int cur, unsigned int *ate) @@ -878,6 +921,9 @@ struct dbgfs_txpwr_table { const struct txpwr_map *byr; const struct txpwr_map *lmt; const struct txpwr_map *lmt_ru; + const struct txpwr_map *lmt_ru484_242; + const struct txpwr_map *lmt_ru996_484; + const struct txpwr_map *lmt_ru996_484_242; }; static const struct dbgfs_txpwr_table dbgfs_txpwr_table_ax = { @@ -890,6 +936,9 @@ static const struct dbgfs_txpwr_table dbgfs_txpwr_table_be = { .byr = &__txpwr_map_byr_be, .lmt = &__txpwr_map_lmt_be, .lmt_ru = &__txpwr_map_lmt_ru_be, + .lmt_ru484_242 = &__txpwr_map_lmt_ru484_242_be, + .lmt_ru996_484 = &__txpwr_map_lmt_ru996_484_be, + .lmt_ru996_484_242 = &__txpwr_map_lmt_ru996_484_242_be, }; static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] = { @@ -930,6 +979,8 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_sar_parm sar_parm = {}; const struct dbgfs_txpwr_table *tbl; const struct rtw89_chan *chan; @@ -975,6 +1026,35 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, return n; p += n; + switch (chip_gen) { + case RTW89_CHIP_AX: + goto out; + case RTW89_CHIP_BE: + if (!(chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + goto out; + break; + default: + return -EOPNOTSUPP; + } + + p += scnprintf(p, end - p, "\n[TX power limit_large_mru]\n"); + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru484_242); + if (n < 0) + return n; + p += n; + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru996_484); + if (n < 0) + return n; + p += n; + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru996_484_242); + if (n < 0) + return n; + p += n; + +out: return p - buf; } @@ -3539,53 +3619,13 @@ rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, return count; } -static int rtw89_dbg_trigger_l1_error_by_halt_h2c_ax(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_FORCE); -} - -static int rtw89_dbg_trigger_l1_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - rtw89_leave_ps_mode(rtwdev); - - rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, - B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); - - return 0; -} - -static int rtw89_dbg_trigger_l1_error_by_halt_h2c(struct rtw89_dev *rtwdev) -{ - const struct rtw89_chip_info *chip = rtwdev->chip; - - switch (chip->chip_gen) { - case RTW89_CHIP_AX: - return rtw89_dbg_trigger_l1_error_by_halt_h2c_ax(rtwdev); - case RTW89_CHIP_BE: - return rtw89_dbg_trigger_l1_error_by_halt_h2c_be(rtwdev); - default: - return -EOPNOTSUPP; - } -} - -static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) +static int rtw89_dbg_trigger_l1_error_ax(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - struct rtw89_cpuio_ctrl ctrl_para = {0}; + struct rtw89_cpuio_ctrl ctrl_para = {}; u16 pkt_id; int ret; - if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) - return rtw89_dbg_trigger_l1_error_by_halt_h2c(rtwdev); - - rtw89_leave_ps_mode(rtwdev); - ret = mac->dle_buf_req(rtwdev, 0x20, true, &pkt_id); if (ret) return ret; @@ -3604,6 +3644,52 @@ static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) return 0; } +static int rtw89_dbg_trigger_l1_error_be(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, + B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); + + return 0; +} + +static int rtw89_dbg_trigger_l1_error_by_halt_h2c(struct rtw89_dev *rtwdev) +{ + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_FORCE); +} + +static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + int (*sim_l1)(struct rtw89_dev *rtwdev); + + switch (chip->chip_gen) { + case RTW89_CHIP_AX: + sim_l1 = rtw89_dbg_trigger_l1_error_ax; + break; + case RTW89_CHIP_BE: + sim_l1 = rtw89_dbg_trigger_l1_error_be; + break; + default: + return -EOPNOTSUPP; + } + + if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) + return rtw89_dbg_trigger_l1_error_by_halt_h2c(rtwdev); + + rtw89_leave_ps_mode(rtwdev); + + return sim_l1(rtwdev); +} + static int rtw89_dbg_trigger_l0_error_ax(struct rtw89_dev *rtwdev) { u16 val16; @@ -3628,55 +3714,36 @@ static int rtw89_dbg_trigger_l0_error_ax(struct rtw89_dev *rtwdev) static int rtw89_dbg_trigger_l0_error_be(struct rtw89_dev *rtwdev) { - u8 val8; int ret; ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL); if (ret) return ret; - val8 = rtw89_read8(rtwdev, R_BE_CMAC_FUNC_EN); - rtw89_write8(rtwdev, R_BE_CMAC_FUNC_EN, val8 & ~B_BE_TMAC_EN); - mdelay(1); - rtw89_write8(rtwdev, R_BE_CMAC_FUNC_EN, val8); - - return 0; -} - -static int rtw89_dbg_trigger_l0_error_by_halt_h2c_ax(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L0_RESET_FORCE); -} - -static int rtw89_dbg_trigger_l0_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - rtw89_leave_ps_mode(rtwdev); - rtw89_write32_set(rtwdev, R_BE_CMAC_FW_TRIGGER_IDCT_ISR, B_BE_CMAC_FW_TRIG_IDCT | B_BE_CMAC_FW_ERR_IDCT_IMR); return 0; } +static int rtw89_dbg_trigger_l0_error_by_halt_h2c(struct rtw89_dev *rtwdev) +{ + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L0_RESET_FORCE); +} + static int rtw89_dbg_trigger_l0_error(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; - int (*sim_l0_by_halt_h2c)(struct rtw89_dev *rtwdev); int (*sim_l0)(struct rtw89_dev *rtwdev); switch (chip->chip_gen) { case RTW89_CHIP_AX: - sim_l0_by_halt_h2c = rtw89_dbg_trigger_l0_error_by_halt_h2c_ax; sim_l0 = rtw89_dbg_trigger_l0_error_ax; break; case RTW89_CHIP_BE: - sim_l0_by_halt_h2c = rtw89_dbg_trigger_l0_error_by_halt_h2c_be; sim_l0 = rtw89_dbg_trigger_l0_error_be; break; default: @@ -3684,7 +3751,7 @@ static int rtw89_dbg_trigger_l0_error(struct rtw89_dev *rtwdev) } if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) - return sim_l0_by_halt_h2c(rtwdev); + return rtw89_dbg_trigger_l0_error_by_halt_h2c(rtwdev); rtw89_leave_ps_mode(rtwdev); @@ -3756,6 +3823,7 @@ rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, } struct rtw89_dbg_ser_counters { + unsigned int l0_sim; unsigned int l0; unsigned int l1; unsigned int l0_to_l1; @@ -3776,6 +3844,7 @@ static void rtw89_dbg_get_ser_counters_be(struct rtw89_dev *rtwdev, { const u32 val = rtw89_read32(rtwdev, R_BE_SER_DBG_INFO); + cnt->l0_sim = rtw89_read8(rtwdev, R_BE_SER_L0_DBG_CNT1 + 3); cnt->l0 = u32_get_bits(val, B_BE_SER_L0_COUNTER_MASK); cnt->l1 = u32_get_bits(val, B_BE_SER_L1_COUNTER_MASK); cnt->l0_to_l1 = u32_get_bits(val, B_BE_SER_L0_PROMOTE_L1_EVENT_MASK); @@ -3806,6 +3875,10 @@ static ssize_t rtw89_debug_priv_ser_counters_get(struct rtw89_dev *rtwdev, p += scnprintf(p, end - p, "SER L1 SW Count: %u\n", sw_cnt->l1); p += scnprintf(p, end - p, "SER L2 SW Count: %u\n", sw_cnt->l2); + /* Some chipsets don't have dedicated cnt for SER simulation. */ + p += scnprintf(p, end - p, "---\n"); + p += scnprintf(p, end - p, "SER L0 Simulation Count: %d\n", cnt.l0_sim); + /* Some chipsets won't record SER simulation in HW cnt. */ p += scnprintf(p, end - p, "---\n"); p += scnprintf(p, end - p, "SER L0 Count: %d\n", cnt.l0); @@ -3924,6 +3997,7 @@ static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev, rtw89_rate_info_bw_to_mhz(rate->bw)); p += scnprintf(p, end - p, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate); + p += scnprintf(p, end - p, " PER:%d", rtwsta_link->ra_report.retry_ratio); p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait, max_rc_amsdu_len); @@ -4015,15 +4089,15 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) } static int -rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat, - enum rtw89_hw_rate first_rate, int len) +rtw89_debug_append_rate(char *buf, size_t bufsz, const u32 *rate_cnt, + int first_rate, int len) { char *p = buf, *end = buf + bufsz; int i; for (i = 0; i < len; i++) p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ", - pkt_stat->rx_rate_cnt[first_rate + i]); + rate_cnt[first_rate + i]); return p - buf; } @@ -4050,38 +4124,31 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; -static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, - struct rtw89_debugfs_priv *debugfs_priv, - char *buf, size_t bufsz) +static const struct rtw89_tx_rate_cnt_info { + int first_rate; + int len; + const char *rate_mode; +} rtw89_tx_rate_cnt_infos[] = { + {0, 4, "Legacy:"}, + {4, 8, "OFDM:"}, + {12, 14, "MCS 1SS:"}, + {26, 14, "MCS 2SS:"}, +}; + +static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) { - struct rtw89_traffic_stats *stats = &rtwdev->stats; - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_debugfs_iter_data iter_data; const struct rtw89_rx_rate_cnt_info *info; - struct rtw89_hal *hal = &rtwdev->hal; + u8 rssi = ewma_rssi_read(&bb->bcn_rssi); char *p = buf, *end = buf + bufsz; enum rtw89_hw_rate first_rate; - u8 rssi; int i; - rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); - - p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", - stats->tx_throughput, stats->tx_throughput_raw, - stats->tx_tfc_lv); - if (hal->thermal_prot_lv) - p += scnprintf(p, end - p, ", duty: %d%%", - 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); - p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", - stats->rx_throughput, stats->rx_throughput_raw, - stats->rx_tfc_lv); - p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n", + p += scnprintf(p, end - p, "Beacon: %u (%d dBm)\n", pkt_stat->beacon_nr, - RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); - p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", - stats->tx_avg_len, - stats->rx_avg_len); + RTW89_RSSI_RAW_TO_DBM(rssi)); p += scnprintf(p, end - p, "RX count:\n"); @@ -4092,16 +4159,50 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, continue; p += scnprintf(p, end - p, "%10s [", info->rate_mode); - p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, - first_rate, info->len); + p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt, + first_rate, info->len); if (info->ext) { p += scnprintf(p, end - p, "]["); - p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, - first_rate + info->len, info->ext); + p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt, + first_rate + info->len, + info->ext); } p += scnprintf(p, end - p, "]\n"); } + return p - buf; +} + +static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct rtw89_debugfs_iter_data iter_data; + struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", + stats->tx_throughput, stats->tx_throughput_raw, + stats->tx_tfc_lv); + if (hal->thermal_prot_lv) + p += scnprintf(p, end - p, ", duty: %d%%", + 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); + p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", + stats->rx_throughput, stats->rx_throughput_raw, + stats->rx_tfc_lv); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic); + + rtw89_for_each_active_bb(rtwdev, bb) { + p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); + } + p += scnprintf(p, end - p, "\n"); + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); p += iter_data.written_sz; @@ -4109,6 +4210,333 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, return p - buf; } +static const char *const lcck[] = {"L_CCK"}; +static const char *const scck[] = {"S_CCK"}; +static const char *const ht_gf[] = {"HT_GF"}; +static const char *const vht_mu[] = {"VHT_MU"}; +static const char *const he_er_su[] = {"HE_ER_SU"}; +static const char *const eht_tb[] = {"EHT_TB"}; +static const char *const legacy_ax[] = {"LEGACY"}; +static const char *const ht_ax[] = {"HT"}; +static const char *const vht_su_ax[] = {"VHT_SU"}; +static const char *const he_su_ax[] = {"HE_SU"}; +static const char *const he_mu_ax[] = {"HE_MU"}; +static const char *const he_tb_ax[] = {"HE_TB"}; +static const char *const legacy_be[] = { + "LEGACY", "LEGACY_DUP", "LEGACY_DUP_PUNC" +}; + +static const char *const ht_be[] = { + "HT_MF", "HT_SND_NDP" +}; + +static const char *const vht_su_be[] = { + "VHT_SU", "VHT_SND_NDP" +}; + +static const char *const he_su_be[] = { + "HE_SU", "HE_SND_NDP", "HE_SND_NDP_PUNC", "HE_RANG_NDP" +}; + +static const char *const he_mu_be[] = { + "HE_MU_RU", "HE_MU_MU", "HE_MU_RU_PUNC" +}; + +static const char *const he_tb_be[] = { + "HE_TB", "HE_TB_FB_NDP", "HE_MU_RANG_NDP" +}; + +static const char *const eht_mu[] = { + "EHT_MU_SU", "EHT_MU_ER", "EHT_MU_RU", "EHT_MU_MU", + "EHT_MU_SND_NDP", "EHT_MU_SU_PUNC", "EHT_MU_RU_PUNC", + "EHT_SND_NDP_PUNC", "EHT_MU_MU_PUNC" +}; + +#define PPDU_SAME(ppdu) \ + {.str = {ppdu, ppdu}, \ + .cnt = {ARRAY_SIZE(ppdu), ARRAY_SIZE(ppdu)} } +#define PPDU_VARIANT(ppdu) \ + {.str = {ppdu##_ax, ppdu##_be}, \ + .cnt = {ARRAY_SIZE(ppdu##_ax), ARRAY_SIZE(ppdu##_be)} } +#define PPDU_GEV1(ppdu) \ + {.str = {NULL, ppdu}, \ + .cnt = {0, ARRAY_SIZE(ppdu)} } + +static const struct rtw89_ppdu_info { + const char *const *str[RTW89_CHIP_GEN_NUM]; + u8 cnt[RTW89_CHIP_GEN_NUM]; +} rtw89_ppdu_infos[] = { + [0] = PPDU_SAME(lcck), + [1] = PPDU_SAME(scck), + [2] = PPDU_VARIANT(legacy), + [3] = PPDU_VARIANT(ht), + [4] = PPDU_SAME(ht_gf), + [5] = PPDU_VARIANT(vht_su), + [6] = PPDU_SAME(vht_mu), + [7] = PPDU_VARIANT(he_su), + [8] = PPDU_SAME(he_er_su), + [9] = PPDU_VARIANT(he_mu), + [10] = PPDU_VARIANT(he_tb), + [11] = PPDU_GEV1(eht_mu), + [12] = PPDU_GEV1(eht_tb), +}; + +#define TXCMD_SAME(txcmd) {txcmd, txcmd} +#define TXCMD_DIFF(txcmd, txcmd_v1) {txcmd, txcmd_v1} +#define TXCMD_GEV1(txcmd) {"RSVD", txcmd} + +static const struct rtw89_txcmd_info { + const char *str[RTW89_CHIP_GEN_NUM]; +} rtw89_txcmd_infos[] = { + [0] = {TXCMD_SAME("DATA")}, + [1] = {TXCMD_SAME("BCN")}, + [2] = {TXCMD_SAME("HT_NDPA")}, + [3] = {TXCMD_SAME("VHT_NDPA")}, + [4] = {TXCMD_SAME("HE_NDPA")}, + [5] = {TXCMD_GEV1("EHT_NDPA")}, + [6] = {TXCMD_GEV1("11MC_FTM")}, + [7] = {TXCMD_GEV1("11MC_FTM_ACK")}, + [8] = {TXCMD_SAME("RTS")}, + [9] = {TXCMD_SAME("CTS2S")}, + [10] = {TXCMD_SAME("CF_END")}, + [11] = {TXCMD_SAME("CMP_BAR")}, + [12] = {TXCMD_SAME("BFRP")}, + [13] = {TXCMD_SAME("NDP")}, + [14] = {TXCMD_SAME("QoS_NULL")}, + [15] = {TXCMD_GEV1("CTS_2_MURTS")}, + [16] = {TXCMD_SAME("ACK")}, + [17] = {TXCMD_SAME("CTS")}, + [18] = {TXCMD_SAME("CMP_BA")}, + [19] = {TXCMD_SAME("MSTA_BA")}, + [20] = {TXCMD_SAME("HT_CSI")}, + [21] = {TXCMD_SAME("VHT_CSI")}, + [22] = {TXCMD_SAME("HE_CSI")}, + [23] = {TXCMD_GEV1("EHT_CSI")}, + [24] = {TXCMD_GEV1("NTB_I2R_NDPA")}, + [25] = {TXCMD_GEV1("NTB_I2R_NDP")}, + [26] = {TXCMD_GEV1("NTB_I2R_LMR")}, + [27] = {TXCMD_GEV1("NTB_I2R_NDP")}, + [28] = {TXCMD_GEV1("NTB_I2R_LMR")}, + [29] = {TXCMD_GEV1("NTB_R2I_RANG_NDPA")}, + [30] = {TXCMD_GEV1("NTB_R2I_NDP")}, + [31] = {TXCMD_DIFF("TB_PPDU", "NTB_R2I_LMR")}, + [32] = {TXCMD_SAME("TRIG_BASIC")}, + [33] = {TXCMD_SAME("TRIG_BFRP")}, + [34] = {TXCMD_SAME("TRIG_MUBAR")}, + [35] = {TXCMD_SAME("TRIG_MURTS")}, + [36] = {TXCMD_SAME("TRIG_BSRP")}, + [37] = {TXCMD_SAME("TRIG_BQRP")}, + [38] = {TXCMD_SAME("TRIG_NFRP")}, + [39] = {TXCMD_GEV1("TRIG_BASIC_DATA")}, + [40] = {TXCMD_GEV1("TRIG_RANG_POLL")}, + [41] = {TXCMD_GEV1("TRIG_RANG_SNR")}, + [42] = {TXCMD_GEV1("TRIG_RANG_LMR")}, + [48] = {TXCMD_DIFF("TRIG_BASIC_DATA", "TRIG_TB_CSI")}, + [49] = {TXCMD_GEV1("TRIG_TB_CBA")}, + [50] = {TXCMD_GEV1("TRIG_TB_MBA")}, + [51] = {TXCMD_GEV1("TRIG_TB_BSR")}, + [52] = {TXCMD_GEV1("TRIG_TB_BQR")}, + [53] = {TXCMD_GEV1("TRIG_TB_ACK")}, + [54] = {TXCMD_GEV1("TRIG_TB_PPDU")}, + [55] = {TXCMD_GEV1("TRIG_TB_I2R_CTS2S")}, + [56] = {TXCMD_GEV1("TRIG_TB_I2R_NDP")}, + [57] = {TXCMD_GEV1("TRIG_TB_I2R_LMR")}, +}; + +static const char *rtw89_ppdu_str(struct rtw89_dev *rtwdev, u8 type, u8 subtype) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_ppdu_info *ppdu_info; + + if (type > ARRAY_SIZE(rtw89_ppdu_infos)) + return "RSVD"; + + ppdu_info = &rtw89_ppdu_infos[type]; + + if (!ppdu_info->str[chip->chip_gen] || + subtype >= ppdu_info->cnt[chip->chip_gen]) + return "RSVD"; + + return ppdu_info->str[chip->chip_gen][subtype]; +} + +static const char *rtw89_txcmd_str(struct rtw89_dev *rtwdev, u8 txcmd) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (txcmd < ARRAY_SIZE(rtw89_txcmd_infos)) + return rtw89_txcmd_infos[txcmd].str[chip->chip_gen] ?: "RSVD"; + + return "RSVD"; +} + +static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; + const struct rtw89_physts_regs *physts = phy->physts; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; + u8 factor = chip->txpwr_factor_rf; + u32 reg_nr; + s32 val; + int i; + + p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + + p += scnprintf(p, end - p, "== PMAC\n"); + p += scnprintf(p, end - p, + "TX [CCK_TXEN, CCK_TXON, OFDM_TXEN, OFDM_TXON]: [%d, %d, %d, %d]\n", + pmac->cck_mac_txen, pmac->cck_phy_txon, + pmac->ofdm_mac_txen, pmac->ofdm_phy_txon); + p += scnprintf(p, end - p, "CRC [CCK, OFDM, HT, VHT, HE, EHT, ALL, MPDU]\n"); + p += scnprintf(p, end - p, " ok: [%d, %d, %d, %d, %d, %d, %d, %d]\n", + pmac->cnt_cck_crc32_ok, pmac->cnt_ofdm_crc32_ok, + pmac->cnt_ht_crc32_ok, pmac->cnt_vht_crc32_ok, + pmac->cnt_he_crc32_ok, pmac->cnt_eht_crc32_ok, + pmac->cnt_crc32_ok_all, pmac->cnt_ampdu_crc_ok); + p += scnprintf(p, end - p, "err: [%d, %d, %d, %d, %d, %d, %d, %d]\n", + pmac->cnt_cck_crc32_error, pmac->cnt_ofdm_crc32_error, + pmac->cnt_ht_crc32_error, pmac->cnt_vht_crc32_error, + pmac->cnt_he_crc32_error, pmac->cnt_eht_crc32_error, + pmac->cnt_crc32_error_all, pmac->cnt_ampdu_crc_error); + p += scnprintf(p, end - p, "CCA [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_cca, pmac->cnt_ofdm_cca); + p += scnprintf(p, end - p, "FA [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_fail, pmac->cnt_ofdm_fail); + + p += scnprintf(p, end - p, "CCA spoofing [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_spoofing, pmac->cnt_ofdm_spoofing); + p += scnprintf(p, end - p, "CCK SFD: %d, SIG_GG: %d\n", + pmac->cnt_sfd_gg, pmac->cnt_sig_gg); + p += scnprintf(p, end - p, + "OFDM Parity: %d, Rate: %d, LSIG_BRK_S: %d, LSIG_BRK_L: %d, SBD: %d\n", + pmac->cnt_parity_fail, pmac->cnt_rate_illegal, + pmac->cnt_lsig_brk_s_th, pmac->cnt_lsig_brk_l_th, + pmac->cnt_sb_search_fail); + p += scnprintf(p, end - p, "AMPDU miss: %d\n\n", pmac->cnt_ampdu_miss); + + p += scnprintf(p, end - p, "== TX General\n"); + p += scnprintf(p, end - p, "%s %s\n", + rtw89_ppdu_str(rtwdev, tx_stat->type, tx_stat->subtype), + rtw89_txcmd_str(rtwdev, tx_stat->txcmd)); + + p += scnprintf(p, end - p, "BW: %d, TX_SC: %d, TX_PATH_EN: %d, PATH_MAP: 0x%x\n", + 20 << tx_stat->bw, tx_stat->txsc, + tx_stat->tx_path_en, tx_stat->path_map); + + val = sign_extend32(tx_stat->tmac_txpwr, 8); + p += scnprintf(p, end - p, "TXPWR TMAC: %d,", val >> factor); + + reg_nr = min(chip->rf_path_num, ARRAY_SIZE(tx_stat->txpwr)); + for (i = 0; i < reg_nr; i++) { + val = sign_extend32(tx_stat->txpwr[i], 8); + p += scnprintf(p, end - p, " P%d: %d%s", + i, val >> factor, (i < reg_nr - 1) ? "," : ""); + } + p += scnprintf(p, end - p, " dBm\n"); + + p += scnprintf(p, end - p, "MCS: %d, STBC: %d\n", + tx_stat->max_mcs, tx_stat->stbc); + + p += scnprintf(p, end - p, "Info: ["); + reg_nr = min(physts->tx_info.reg_nr, ARRAY_SIZE(tx_stat->info)); + for (i = 0; i < reg_nr; i++) + p += scnprintf(p, end - p, "0x%08x%s", + tx_stat->info[i], (i < reg_nr - 1) ? ", " : ""); + p += scnprintf(p, end - p, "]\n"); + + p += scnprintf(p, end - p, "Common ctrl: ["); + reg_nr = min(physts->tx_common_ctrl.reg_nr, ARRAY_SIZE(tx_stat->common_ctrl)); + for (i = 0; i < reg_nr; i++) + p += scnprintf(p, end - p, "0x%08x%s", + tx_stat->common_ctrl[i], (i < reg_nr - 1) ? ", " : ""); + p += scnprintf(p, end - p, "]\n\n"); + + p += scnprintf(p, end - p, "== RX General\n"); + p += scnprintf(p, end - p, + "LDPC: %d, BCC: %d, STBC: %d, SU_NON_BF: %d, SU_BF: %d, MU: %d\n\n", + pkt_stat->rx.ldpc, pkt_stat->rx.bcc, + pkt_stat->rx.stbc, pkt_stat->rx.su_non_bf, + pkt_stat->rx.su_bf, pkt_stat->rx.mu); + + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); + p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_bb_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + struct rtw89_traffic_stats *stats = &rtwdev->stats; + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_tx_rate_cnt_info *info; + struct rtw89_debugfs_iter_data iter_data; + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + int i; + + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps, RX: %u [%u] Mbps\n", + stats->tx_throughput, stats->tx_throughput_raw, + stats->rx_throughput, stats->rx_throughput_raw); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic); + + if (chip->chip_gen != RTW89_CHIP_AX) { + p += scnprintf(p, end - p, + "TX count [0x%x]:\n", bb_stat->mac_id); + + for (i = 0; i < ARRAY_SIZE(rtw89_tx_rate_cnt_infos); i++) { + info = &rtw89_tx_rate_cnt_infos[i]; + + p += scnprintf(p, end - p, "%10s [", info->rate_mode); + p += rtw89_debug_append_rate(p, end - p, + rtwdev->phystat.tx_rate_cnt, + info->first_rate, info->len); + p += scnprintf(p, end - p, "]\n"); + } + } + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_bb_stat(rtwdev, bb, p, end - p); + p += scnprintf(p, end - p, "\n"); + + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); + p += iter_data.written_sz; + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_bb_info_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + int val; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (sscanf(buf, "enable %d", &val) == 1) + bb_stat->enable = !!val; + else if (sscanf(buf, "mac_id %x", &val) == 1) + rtw89_fw_h2c_tx_history(rtwdev, val); + else + return -EINVAL; + + return count; +} + static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, struct rtw89_addr_cam_entry *addr_cam) @@ -4347,6 +4775,9 @@ static const struct rtw89_disabled_dm_info { DM_INFO(THERMAL_PROTECT), DM_INFO(TAS), DM_INFO(MLO), + DM_INFO(HW_SCAN), + DM_INFO(INACTIVE_PS), + DM_INFO(DIG_PD), }; static ssize_t @@ -4392,6 +4823,96 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, return count; } +#define RTW89_DIG_PD_TH_MIN_DBM -102 +#define RTW89_DIG_PD_TH_MAX_DBM -40 +#define RTW89_DIG_PD_TH_STEP 2 + +static s8 rtw89_dig_pd_th_to_dbm(u8 reg_val) +{ + return RTW89_DIG_PD_TH_MIN_DBM + RTW89_DIG_PD_TH_STEP * reg_val; +} + +static u8 rtw89_dig_pd_th_dbm_to_reg(s8 dbm) +{ + return (dbm - RTW89_DIG_PD_TH_MIN_DBM) / RTW89_DIG_PD_TH_STEP; +} + +static ssize_t +rtw89_debug_priv_static_pd_th_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; + bool disabled; + s8 pd_th_dbm; + s8 cck_pd_th; + + disabled = hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD); + + if (disabled) { + pd_th_dbm = rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th); + cck_pd_th = hal->fixed_dig_cck_pd_th; + + p += scnprintf(p, end - p, "DIG: static\n"); + p += scnprintf(p, end - p, "OFDM PD threshold: %d dBm\n", pd_th_dbm); + p += scnprintf(p, end - p, "CCK PD threshold: %d dBm\n", cck_pd_th); + } else { + p += scnprintf(p, end - p, "DIG: dynamic\n"); + } + + p += scnprintf(p, end - p, "\nUsage: echo [pd_th] > static_pd_th\n"); + p += scnprintf(p, end - p, " mode: 0 = dynamic, 1 = static\n"); + p += scnprintf(p, end - p, " pd_th: PD threshold in dBm (-102 ~ -40)\n"); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_static_pd_th_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + struct rtw89_hal *hal = &rtwdev->hal; + int ret; + u32 mode; + s32 pd_th_dbm; + + ret = sscanf(buf, "%u %d", &mode, &pd_th_dbm); + if (ret < 1) + return -EINVAL; + + if (mode > 1) + return -EINVAL; + + if (mode == 0) { + rtw89_core_dm_disable_clr(rtwdev, RTW89_DM_DIG_PD); + hal->fixed_dig_pd_th = 0; + hal->fixed_dig_cck_pd_th = 0; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "DIG static mode disabled\n"); + } else { + if (ret < 2 || pd_th_dbm < RTW89_DIG_PD_TH_MIN_DBM || + pd_th_dbm > RTW89_DIG_PD_TH_MAX_DBM) + return -EINVAL; + + rtw89_core_dm_disable_set(rtwdev, RTW89_DM_DIG_PD); + hal->fixed_dig_pd_th = clamp(rtw89_dig_pd_th_dbm_to_reg(pd_th_dbm), + 0, 0x1f); + /* CCK uses dBm directly */ + hal->fixed_dig_cck_pd_th = pd_th_dbm; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "DIG static mode: PD=0x%02x (%d dBm), CCK=%d dBm\n", + hal->fixed_dig_pd_th, + rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th), + hal->fixed_dig_cck_pd_th); + } + + return count; +} + static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev, unsigned int link_id) { @@ -4468,6 +4989,77 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, return count; } +static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); + p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); + p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); + p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; + struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; + struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; + u16 upper, lower = bcn_stat->tbtt_tu_min; + char *p = buf, *end = buf + bufsz; + u16 *drift = bcn_stat->drift; + u8 bcn_num = bcn_stat->num; + struct rtw89_bb_ctx *bb; + u8 count; + u8 i; + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); + + p += scnprintf(p, end - p, "[Beacon info]\n"); + p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); + p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); + + p += scnprintf(p, end - p, "\n[Distribution]\n"); + p += scnprintf(p, end - p, "tbtt\n"); + for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) { + upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1; + if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1) + upper = max(upper, bcn_stat->tbtt_tu_max); + + p += scnprintf(p, end - p, "%02u - %02u: %u\n", + lower, upper, bcn_dist->bins[i]); + + lower = upper + 1; + } + + p += scnprintf(p, end - p, "\ndrift\n"); + + for (i = 0; i < bcn_num; i += count) { + count = 1; + while (i + count < bcn_num && drift[i] == drift[i + count]) + count++; + + p += scnprintf(p, end - p, "%u: %u\n", drift[i], count); + } + p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound); + p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound); + p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count); + + p += scnprintf(p, end - p, "\n[Tracking]\n"); + p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset); + p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout); + + return p - buf; +} + enum __diag_mac_cmd { __CMD_EQUALV, __CMD_EQUALO, @@ -4764,64 +5356,94 @@ rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev, return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz); } -static ssize_t -rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, - struct rtw89_debugfs_priv *debugfs_priv, - char *buf, size_t bufsz) +static int rtw89_get_diag_bb(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) { - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; - struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; - struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; - struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; - u16 upper, lower = bcn_stat->tbtt_tu_min; + struct rtw89_diag_bb *diag = &bb->diag; char *p = buf, *end = buf + bufsz; - u16 *drift = bcn_stat->drift; - u8 bcn_num = bcn_stat->num; - u8 count; - u8 i; - p += scnprintf(p, end - p, "[Beacon info]\n"); - p += scnprintf(p, end - p, "count: %u\n", pkt_stat->beacon_nr); - p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); - p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); - p += scnprintf(p, end - p, "raw rssi: %lu\n", - ewma_rssi_read(&rtwdev->phystat.bcn_rssi)); - p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); - p += scnprintf(p, end - p, "length: %u\n", pkt_stat->beacon_len); - - p += scnprintf(p, end - p, "\n[Distribution]\n"); - p += scnprintf(p, end - p, "tbtt\n"); - for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) { - upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1; - if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1) - upper = max(upper, bcn_stat->tbtt_tu_max); - - p += scnprintf(p, end - p, "%02u - %02u: %u\n", - lower, upper, bcn_dist->bins[i]); - - lower = upper + 1; - } - - p += scnprintf(p, end - p, "\ndrift\n"); - - for (i = 0; i < bcn_num; i += count) { - count = 1; - while (i + count < bcn_num && drift[i] == drift[i + count]) - count++; - - p += scnprintf(p, end - p, "%u: %u\n", drift[i], count); - } - p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound); - p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound); - p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count); - - p += scnprintf(p, end - p, "\n[Tracking]\n"); - p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset); - p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout); + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Diag bitmap = 0x%x\n", diag->diag_bb_bitmap); + p += scnprintf(p, end - p, + "Event{Hang, PD MAX, No RX, High FA, High EDCCA Ratio} = "); + p += scnprintf(p, end - p, "{%d, %d, %d, %d, %d}\n", + diag->diag_bb_cnt[RTW89_DIAG_BB_HANG], + diag->diag_bb_cnt[RTW89_DIAG_BB_PD], + diag->diag_bb_cnt[RTW89_DIAG_BB_NO_RX], + diag->diag_bb_cnt[RTW89_DIAG_BB_FA], + diag->diag_bb_cnt[RTW89_DIAG_BB_EDCCA]); + p += scnprintf(p, end - p, + "consecutive_no_tx_cnt=%d, consecutive_no_rx_cnt=%d\n\n", + diag->consecutive_no_tx_cnt, + diag->consecutive_no_rx_cnt); return p - buf; } +static ssize_t +rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_diag_bb(rtwdev, bb, p, end - p); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; + u32 bss_color; + u32 aid; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_leave_ps_mode(rtwdev); + + bss_color = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg, + B_BSS_CLR_MAP_TGT, RTW89_PHY_0); + aid = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg, + B_BSS_CLR_MAP_STAID, RTW89_PHY_0); + + p += scnprintf(p, end - p, "bss_color=0x%x aid=0x%x\n", bss_color, aid); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_monitor_opts_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + u32 bss_color; + u32 aid; + int num; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + num = sscanf(buf, "%x %x", &bss_color, &aid); + if (num != 2) { + rtw89_info(rtwdev, "valid format: \n"); + return -EINVAL; + } + + rtw89_leave_ps_mode(rtwdev); + + __rtw89_phy_set_bss_color(rtwdev, bss_color, aid, RTW89_PHY_0); + + return count; +} + #define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ @@ -4878,11 +5500,15 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .btc_manual = rtw89_debug_priv_set(btc_manual), .fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK), .phy_info = rtw89_debug_priv_get(phy_info), + .bb_info = rtw89_debug_priv_set_and_get(bb_info, RWLOCK), .stations = rtw89_debug_priv_get(stations, RLOCK), .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), + .static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, RWLOCK), .mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK), .beacon_info = rtw89_debug_priv_get(beacon_info), .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK), + .diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK), + .monitor_opts = rtw89_debug_priv_set_and_get(monitor_opts, RWLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -4926,11 +5552,20 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_w(btc_manual); rtw89_debugfs_add_w(fw_log_manual); rtw89_debugfs_add_r(phy_info); + rtw89_debugfs_add_rw(bb_info); rtw89_debugfs_add_r(stations); +} + +static +void rtw89_debugfs_add_sec2(struct rtw89_dev *rtwdev, struct dentry *debugfs_topdir) +{ rtw89_debugfs_add_rw(disable_dm); + rtw89_debugfs_add_rw(static_pd_th); rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); + rtw89_debugfs_add_r(diag_bb); + rtw89_debugfs_add_rw(monitor_opts); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) @@ -4947,6 +5582,7 @@ void rtw89_debugfs_init(struct rtw89_dev *rtwdev) rtw89_debugfs_add_sec0(rtwdev, debugfs_topdir); rtw89_debugfs_add_sec1(rtwdev, debugfs_topdir); + rtw89_debugfs_add_sec2(rtwdev, debugfs_topdir); } void rtw89_debugfs_deinit(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 17704f054727..d6a594b75ab2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -930,8 +930,9 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __DIS_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, WITH_RFK_PRE_NOTIFY, G), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), - __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -1208,12 +1209,17 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev, { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; + u16 aid = le16_to_cpu(elm->aid); u32 needed_bitmap = 0; u32 offset = 0; int subband; u32 bitmap; int type; + if (aid && aid != hal->aid) + return 1; + if (chip->support_bands & BIT(NL80211_BAND_6GHZ)) needed_bitmap |= RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_6GHZ; if (chip->support_bands & BIT(NL80211_BAND_5GHZ)) @@ -1417,12 +1423,18 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev, const union rtw89_fw_element_arg arg) { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_hal *hal = &rtwdev->hal; + u8 rfe_type; u16 aid; aid = le16_to_cpu(elm->aid); + rfe_type = elm->u.tx_comp.rfe_type; + if (aid && aid != hal->aid) return 1; /* ignore if aid not matched */ + else if (rfe_type && rfe_type != efuse->rfe_type) + return 1; /* ignore if rfe_type not matched */ else if (elm_info->tx_comp) return 1; /* ignore if an element is existing */ @@ -2957,6 +2969,8 @@ static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev, ether_addr_copy(arp_skb->sender_hw, rtwvif_link->mac_addr); arp_skb->sender_ip = rtwvif->ip_addr; + ether_addr_copy(arp_skb->target_hw, rtwvif_link->mac_addr); + arp_skb->target_ip = rtwvif->ip_addr; return skb; } @@ -3223,14 +3237,15 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12}; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_h2c_lps_ml_cmn_info *h2c; struct rtw89_vif_link *rtwvif_link; + struct rtw89_pkt_stat *pkt_stat; const struct rtw89_chan *chan; u8 bw_idx = RTW89_BB_BW_20_40; + struct rtw89_bb_ctx *bb; u32 len = sizeof(*h2c); unsigned int link_id; struct sk_buff *skb; @@ -3261,11 +3276,14 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A; chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); gain_band = rtw89_subband_to_gain_band_be(chan->subband_type); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); h2c->central_ch[rtwvif_link->phy_idx] = chan->channel; h2c->pri_ch[rtwvif_link->phy_idx] = chan->primary_channel; h2c->band[rtwvif_link->phy_idx] = chan->band_type; h2c->bw[rtwvif_link->phy_idx] = chan->band_width; + + pkt_stat = &bb->cur_pkt_stat; if (pkt_stat->beacon_rate < RTW89_HW_RATE_OFDM6) h2c->bcn_rate_type[rtwvif_link->phy_idx] = 0x1; else @@ -5050,7 +5068,7 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv le32_encode_bits(0, RTW89_H2C_JOININFO_W1_EMLSR_CAB) | le32_encode_bits(0, RTW89_H2C_JOININFO_W1_NSTR_EN) | le32_encode_bits(init_ps, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) | - le32_encode_bits(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US, + le32_encode_bits(IEEE80211_EML_CAP_EML_PADDING_DELAY_256US, RTW89_H2C_JOININFO_W1_EMLSR_PADDING) | le32_encode_bits(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US, RTW89_H2C_JOININFO_W1_EMLSR_TRANS_DELAY) | @@ -5584,6 +5602,127 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi return ret; } +int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_tx_history *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c tx history\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_tx_history *)skb->data; + + h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_RA_TX_HISTORY_W0_MACID) | + le32_encode_bits(0, RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_TX_HISTORY, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + bb_stat->mac_id = mac_id; + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_phy_ch_rpt *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c phy ch rpt\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_phy_ch_rpt *)skb->data; + + h2c->w1 = le32_encode_bits(1, RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_PHY_CH_RPT, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_drv_ctrl_fw *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c drv ctrl fw\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_drv_ctrl_fw *)skb->data; + + h2c->w0 = le32_encode_bits(1, RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; @@ -8167,6 +8306,12 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev) rtw89_info(rtwdev, "FW MISC = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM3)); rtw89_info(rtwdev, "R_AX_HALT_C2H = 0x%x\n", rtw89_read32(rtwdev, R_AX_HALT_C2H)); + rtw89_info(rtwdev, "R_AX_HALT_C2H_CTRL = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_C2H_CTRL)); + rtw89_info(rtwdev, "R_AX_HALT_H2C = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_H2C)); + rtw89_info(rtwdev, "R_AX_HALT_H2C_CTRL = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_H2C_CTRL)); rtw89_info(rtwdev, "R_AX_SER_DBG_INFO = 0x%x\n", rtw89_read32(rtwdev, R_AX_SER_DBG_INFO)); @@ -8318,7 +8463,7 @@ static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, struct sk_buff *skb; bool found; int ret = 0; - u8 i; + u32 i; if (!req->n_6ghz_params) return 0; @@ -9485,7 +9630,7 @@ int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtw if (enable) { ret = rtw89_fw_h2c_add_general_pkt(rtwdev, rtwvif_link, - RTW89_PKT_OFLD_TYPE_NULL_DATA, + RTW89_PKT_OFLD_TYPE_ARP_RSP, &pkt_id); if (ret) return -EPERM; @@ -11344,3 +11489,350 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, return parms; } + +static int rtw89_fw_cmd_ofld_pack(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + info->pack_level++; + + return 0; +} + +static void rtw89_fw_cmd_ofld_flush(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + struct sk_buff *skb; + int ret; + u32 len; + + len = info->cnt * sizeof(info->cmds[0]); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "alloc skb fail\n"); + return; + } + + skb_put_data(skb, info->cmds, len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_FW_OFLD, + H2C_FUNC_CMD_OFLD_PKT, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send cmd ofld\n"); + dev_kfree_skb_any(skb); + return; + } + + if (info->accu_delay) + fsleep(info->accu_delay); + + info->cnt = 0; + info->accu_delay = 0; +} + +static int rtw89_fw_cmd_ofld_unpack(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (info->pack_level == 0) + return -EFAULT; + + if (--info->pack_level != 0) + return 0; + + if (info->cnt == 0) + return 0; + + rtw89_fw_cmd_ofld_flush(rtwdev); + + return 0; +} + +static int rtw89_fw_cmd_ofld_enqueue(struct rtw89_dev *rtwdev, + const struct rtw89_fw_cmd_ofld_arg *cmd) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + struct rtw89_h2c_cmd_ofld *h2c; + u32 base_off; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (info->pack_level == 0) + return 1; + + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + if (cmd->type != RTW89_FW_CMD_OFLD_DELAY && + cmd->src != RTW89_FW_CMD_OFLD_SRC_RF && + cmd->src != RTW89_FW_CMD_OFLD_SRC_RF_DDIE && + !IS_ALIGNED(cmd->offset, 4)) + return -EFAULT; + + if (info->cnt >= ARRAY_SIZE(info->cmds)) + rtw89_fw_cmd_ofld_flush(rtwdev); + + h2c = &info->cmds[info->cnt++]; + + h2c->w0 = le32_encode_bits(cmd->src, RTW89_H2C_CMD_OFLD_W0_SRC) | + le32_encode_bits(cmd->type, RTW89_H2C_CMD_OFLD_W0_TYPE) | + le32_encode_bits(cmd->rf_path, RTW89_H2C_CMD_OFLD_W0_PATH) | + le32_encode_bits(info->cnt, RTW89_H2C_CMD_OFLD_W0_CMD_NUM) | + le32_encode_bits(cmd->offset, RTW89_H2C_CMD_OFLD_W0_OFFSET); + + if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF_DDIE) + base_off = 1; + else if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF) + base_off = 0; + else + base_off = cmd->offset >> 16; + + h2c->w1 = le32_encode_bits(cmd->id, RTW89_H2C_CMD_OFLD_W1_ID) | + le32_encode_bits(base_off, RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET); + h2c->w2 = le32_encode_bits(cmd->value, RTW89_H2C_CMD_OFLD_W2_VALUE); + h2c->w3 = le32_encode_bits(cmd->mask, RTW89_H2C_CMD_OFLD_W3_MASK); + + if (cmd->type == RTW89_FW_CMD_OFLD_DELAY) + info->accu_delay += cmd->value; + + return 0; +} + +static const struct rtw89_io_ops rtw89_raw_io = { + .pack = NULL, + .unpack = NULL, + .write8 = rtw89_raw_write8, + .write16 = rtw89_raw_write16, + .write32 = rtw89_raw_write32, + .phy_write8 = rtw89_raw_phy_write8, + .phy_write16 = rtw89_raw_phy_write16, + .phy_write32 = rtw89_raw_phy_write32, + .write_rf = rtw89_raw_write_rf, +}; + +static +void rtw89_fw_cmd_ofld_phy_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 4), + .mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write8(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_phy_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 2), + .mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write16(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_phy_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = addr, + .mask = MASKDWORD, + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write32(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 4), + .mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write8(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 2), + .mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write16(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = addr, + .mask = MASKDWORD, + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write32(rtwdev, addr, data); +} + +static void rtw89_fw_cmd_ofld_write_rf_ddv(struct rtw89_dev *rtwdev, + struct rtw89_fw_cmd_ofld_arg *cmd, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + *cmd = (typeof(*cmd)){ + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .rf_path = 0, /* Set rf_path to zero in rf_ddv */ + .offset = chip->rf_base_addr[rf_path] + ((addr & 0xff) << 2), + .mask = mask, + .value = data, + }; +} + +static void rtw89_fw_cmd_ofld_write_rf_dav(struct rtw89_dev *rtwdev, + struct rtw89_fw_cmd_ofld_arg *cmd, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + *cmd = (typeof(*cmd)){ + .src = RTW89_FW_CMD_OFLD_SRC_RF, + .type = RTW89_FW_CMD_OFLD_WRITE, + .rf_path = rf_path, + .offset = addr, + .mask = mask, + .value = data, + }; +} + +static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + bool ad_sel = u32_get_bits(addr, RTW89_RF_ADDR_ADSEL_MASK); + struct rtw89_fw_cmd_ofld_arg cmd; + int ret; + + if (unlikely(rf_path >= chip->rf_path_num)) { + rtw89_warn(rtwdev, "unsupported rf path (%d) in fw offload\n", rf_path); + return; + } + + if (ad_sel) + rtw89_fw_cmd_ofld_write_rf_ddv(rtwdev, &cmd, rf_path, addr, mask, data); + else + rtw89_fw_cmd_ofld_write_rf_dav(rtwdev, &cmd, rf_path, addr, mask, data); + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write_rf(rtwdev, rf_path, addr, mask, data); +} + +static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .type = RTW89_FW_CMD_OFLD_DELAY, + .value = us, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + udelay(us); +} + +static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .type = RTW89_FW_CMD_OFLD_DELAY, + .value = ms * 1000, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + mdelay(ms); +} + +static const struct rtw89_io_ops rtw89_fw_cmd_ofld_io = { + .pack = rtw89_fw_cmd_ofld_pack, + .unpack = rtw89_fw_cmd_ofld_unpack, + .do_udelay = rtw89_fw_cmd_ofld_udelay, + .do_mdelay = rtw89_fw_cmd_ofld_mdelay, + .write8 = rtw89_fw_cmd_ofld_write8, + .write16 = rtw89_fw_cmd_ofld_write16, + .write32 = rtw89_fw_cmd_ofld_write32, + .phy_write8 = rtw89_fw_cmd_ofld_phy_write8, + .phy_write16 = rtw89_fw_cmd_ofld_phy_write16, + .phy_write32 = rtw89_fw_cmd_ofld_phy_write32, + .write_rf = rtw89_fw_cmd_ofld_write_rf, +}; + +const struct rtw89_io_ops * +rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_fw_cmd_ofld_info *info; + + if (!(rtwdev->hci.type == RTW89_HCI_TYPE_USB && chip->support_fw_cmd_ofld)) + return &rtw89_raw_io; + + info = devm_kzalloc(rtwdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return &rtw89_raw_io; + + rtwdev->fw_cmd_ofld_info = info; + + return &rtw89_fw_cmd_ofld_io; +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index db252d45e498..20721d5209aa 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -471,6 +471,28 @@ struct rtw89_h2c_ra_v1 { #define RTW89_H2C_RA_V1_W4_RAMASK_UHL16 GENMASK(31, 16) #define RTW89_H2C_RA_V1_W5_RAMASK_UHH16 GENMASK(15, 0) +struct rtw89_h2c_ra_tx_history { + __le32 w0; +} __packed; + +#define RTW89_H2C_RA_TX_HISTORY_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU GENMASK(23, 16) + +struct rtw89_h2c_ra_phy_ch_rpt { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT BIT(10) + +struct rtw89_h2c_ra_drv_ctrl_fw { + __le32 w0; +} __packed; + +#define RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT BIT(13) + static inline void RTW89_SET_FWCMD_SEC_IDX(void *cmd, u32 val) { le32p_replace_bits((__le32 *)(cmd) + 0x00, val, GENMASK(7, 0)); @@ -3115,6 +3137,59 @@ struct rtw89_h2c_trx_protect { #define RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN BIT(0) #define RTW89_H2C_TRX_PROTECT_W1_DFS_EN BIT(1) +enum rtw89_fw_cmd_ofld_arg_src { + RTW89_FW_CMD_OFLD_SRC_BB, + RTW89_FW_CMD_OFLD_SRC_RF, + RTW89_FW_CMD_OFLD_SRC_MAC, + RTW89_FW_CMD_OFLD_SRC_RF_DDIE, + RTW89_FW_CMD_OFLD_SRC_OTHER, +}; + +enum rtw89_fw_cmd_ofld_arg_type { + RTW89_FW_CMD_OFLD_WRITE, + RTW89_FW_CMD_OFLD_COMPARE, + RTW89_FW_CMD_OFLD_DELAY, + RTW89_FW_CMD_OFLD_MOVE, +}; + +struct rtw89_fw_cmd_ofld_arg { + enum rtw89_fw_cmd_ofld_arg_src src; + enum rtw89_fw_cmd_ofld_arg_type type; + enum rtw89_rf_path rf_path; + u32 value; + u32 mask; + u32 offset; + u16 id; +}; + +struct rtw89_h2c_cmd_ofld { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_CMD_OFLD_W0_SRC GENMASK(1, 0) +#define RTW89_H2C_CMD_OFLD_W0_TYPE GENMASK(3, 2) +#define RTW89_H2C_CMD_OFLD_W0_LC BIT(4) +#define RTW89_H2C_CMD_OFLD_W0_PATH GENMASK(6, 5) +#define RTW89_H2C_CMD_OFLD_W0_CMD_NUM GENMASK(14, 8) +#define RTW89_H2C_CMD_OFLD_W0_OFFSET GENMASK(31, 16) +#define RTW89_H2C_CMD_OFLD_W1_ID GENMASK(15, 0) +#define RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET GENMASK(31, 16) +#define RTW89_H2C_CMD_OFLD_W2_VALUE GENMASK(31, 0) +#define RTW89_H2C_CMD_OFLD_W3_MASK GENMASK(31, 0) +#define RTW89_W8_MASK_OF_ALIGNED_ADDR(offset) (0xff << (((offset) & 0x3) << 3)) +#define RTW89_W16_MASK_OF_ALIGNED_ADDR(offset) (0xffff << (((offset) & 0x2) * 8)) + +#define RTW89_FW_CMD_OFLD_NR 125 +struct rtw89_fw_cmd_ofld_info { + unsigned int pack_level; + u32 cnt; + u32 accu_delay; + struct rtw89_h2c_cmd_ofld cmds[RTW89_FW_CMD_OFLD_NR]; +}; + struct rtw89_h2c_fwips { __le32 w0; } __packed; @@ -3833,14 +3908,16 @@ struct rtw89_c2h_done_ack { #define RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0) #define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24) -#define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0)) -#define RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 2)) -#define RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8)) -#define RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(23, 16)) +struct rtw89_c2h_rev_ack { + __le32 w0; + __le32 w1; + __le32 w2; +} __packed; + +#define RTW89_C2H_REV_ACK_W2_CAT GENMASK(1, 0) +#define RTW89_C2H_REV_ACK_W2_CLASS GENMASK(7, 2) +#define RTW89_C2H_REV_ACK_W2_FUNC GENMASK(15, 8) +#define RTW89_C2H_REV_ACK_W2_H2C_SEQ GENMASK(23, 16) struct rtw89_fw_c2h_log_fmt { __le16 signature; @@ -3918,6 +3995,13 @@ struct rtw89_c2h_lps_rpt { */ } __packed; +struct rtw89_c2h_ra_tx_history { + struct rtw89_c2h_hdr hdr; + __le32 ra_tbtt_cnt; + __le32 tx_rate_tot_cnt_hist[RTW89_TX_RATE_NR]; + __le32 tx_cat_cnt[3]; +} __packed; + struct rtw89_c2h_fw_scan_rpt { struct rtw89_c2h_hdr hdr; u8 phy_idx; @@ -4464,6 +4548,11 @@ struct rtw89_fw_element_hdr { u8 rsvd[4]; u8 rules_and_msgs[]; } __packed diag_mac; + struct { + u8 rfe_type; + u8 priv[7]; + u8 contents[]; + } __packed tx_comp; struct __rtw89_fw_txpwr_element txpwr; struct __rtw89_fw_regd_element regd; } __packed u; @@ -4606,6 +4695,7 @@ enum rtw89_fw_ofld_h2c_func { H2C_FUNC_MAC_MACID_PAUSE = 0x8, H2C_FUNC_USR_EDCA = 0xF, H2C_FUNC_TSF32_TOGL = 0x10, + H2C_FUNC_CMD_OFLD_PKT = 0x13, H2C_FUNC_OFLD_CFG = 0x14, H2C_FUNC_ADD_SCANOFLD_CH = 0x16, H2C_FUNC_SCANOFLD = 0x17, @@ -4715,6 +4805,9 @@ enum rtw89_mrc_h2c_func { #define H2C_CL_OUTSRC_RA 0x1 #define H2C_FUNC_OUTSRC_RA_MACIDCFG 0x0 +#define H2C_FUNC_OUTSRC_RA_TX_HISTORY 0x9 +#define H2C_FUNC_OUTSRC_RA_PHY_CH_RPT 0xe +#define H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW 0xf #define H2C_CL_OUTSRC_DM 0x2 #define H2C_FUNC_FW_MCC_DIG 0x6 @@ -5293,6 +5386,9 @@ int rtw89_fw_h2c_rssi_offload(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu); int rtw89_fw_h2c_tp_offload(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi); +int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id); +int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type); @@ -5737,4 +5833,7 @@ enum rtw89_wow_wakeup_ver { RTW89_WOW_REASON_NUM, }; +const struct rtw89_io_ops * +rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev); + #endif diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 54aad37485d6..8c395517bd2f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -894,11 +894,12 @@ EXPORT_SYMBOL(rtw89_mac_set_err_status); static int hfc_reset_param(struct rtw89_dev *rtwdev) { + const struct rtw89_qta_def *qta_def = rtw89_chip_get_qta_def(rtwdev); const struct rtw89_hfc_param_ini *param_ini, *param_inis; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; u8 qta_mode = rtwdev->mac.dle_info.qta_mode; - param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.type]; + param_inis = qta_def->hfc_param_ini[rtwdev->hci.dle_type]; if (!param_inis) return -EINVAL; @@ -1716,18 +1717,27 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .hfc_preccfg_pcie = {2, 40, 0, 0, 1, 0, 0, 0}, .hfc_prec_cfg_c0 = {2, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0}, .hfc_prec_cfg_c2 = {0, 256, 0, 0, 0, 0, 0, 0, 0, 256, 0, 0}, + .hfc_prec_cfg_c3 = {18, 32, 148, 148, 1, 1, 1, 1}, + .hfc_prec_cfg_c5 = {18, 32, 148, 148, 1, 1, 0, 1}, + .hfc_prec_cfg_c6 = {8, 32, 148, 148, 1, 1, 0, 1}, /* PCIE 64 */ .wde_size0 = {RTW89_WDE_PG_64, 4095, 1,}, .wde_size0_v1 = {RTW89_WDE_PG_64, 3328, 0, 0,}, /* 8852A USB */ .wde_size1 = {RTW89_WDE_PG_64, 768, 0,}, /* DLFW */ + .wde_size3_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, .wde_size4 = {RTW89_WDE_PG_64, 0, 4096,}, - .wde_size4_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, + /* 8922A USB3.0 */ + .wde_size4_v1 = {RTW89_WDE_PG_64, 892, 4, 0,}, + .wde_size5_v1 = {RTW89_WDE_PG_64, 864, 32, 0,}, /* PCIE 64 */ .wde_size6 = {RTW89_WDE_PG_64, 512, 0,}, /* 8852B PCIE SCC */ .wde_size7 = {RTW89_WDE_PG_64, 510, 2,}, + /* 8922A USB2.0 */ + .wde_size7_v1 = {RTW89_WDE_PG_64, 508, 4,}, + .wde_size8_v1 = {RTW89_WDE_PG_64, 634, 6,}, /* DLFW */ .wde_size9 = {RTW89_WDE_PG_64, 0, 1024,}, .wde_size16_v1 = {RTW89_WDE_PG_64, 639, 1, 0,}, @@ -1738,6 +1748,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_size18_v1 = {RTW89_WDE_PG_64, 0, 640, 0,}, /* 8852C PCIE SCC */ .wde_size19 = {RTW89_WDE_PG_64, 3328, 0,}, + .wde_size22_v1 = {RTW89_WDE_PG_128, 384, 0, 0,}, .wde_size23 = {RTW89_WDE_PG_64, 1022, 2,}, /* 8852B USB2.0/USB3.0 SCC turbo */ .wde_size30 = {RTW89_WDE_PG_64, 220, 36,}, @@ -1751,8 +1762,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size3_v1 = {RTW89_PLE_PG_128, 2928, 0, 212992,}, /* DLFW */ .ple_size4 = {RTW89_PLE_PG_128, 64, 1472,}, + /* 8922A USB3.0 */ + .ple_size4_v1 = {RTW89_PLE_PG_256, 1963, 109, 57344,}, /* PCIE 64 */ .ple_size6 = {RTW89_PLE_PG_128, 496, 16,}, + /* 8922A USB2.0 */ + .ple_size6_v1 = {RTW89_PLE_PG_256, 2059, 109, 32768,}, + .ple_size7_v1 = {RTW89_PLE_PG_256, 2027, 109, 40960,}, /* DLFW */ .ple_size8 = {RTW89_PLE_PG_128, 64, 960,}, .ple_size9 = {RTW89_PLE_PG_128, 2288, 16,}, @@ -1766,6 +1782,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size22_v1 = {RTW89_PLE_PG_128, 2736, 0, 40960,}, /* 8851B USB2.0 SCC turbo */ .ple_size27 = {RTW89_PLE_PG_128, 1396, 12,}, + .ple_size29_v1 = {RTW89_PLE_PG_128, 1895, 182, 49152,}, /* 8852B USB3.0 SCC turbo */ .ple_size31 = {RTW89_PLE_PG_128, 1392, 16,}, /* 8852C USB2.0 */ @@ -1778,10 +1795,16 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt3 = {0, 0, 0, 0,}, /* DLFW */ .wde_qt4 = {0, 0, 0, 0,}, + /* 8922A USB3.0 */ + .wde_qt4_v1 = {876, 6, 0, 10,}, + .wde_qt5_v1 = {838, 6, 0, 20,}, /* PCIE 64 */ .wde_qt6 = {448, 48, 0, 16,}, /* 8852B PCIE SCC */ .wde_qt7 = {446, 48, 0, 16,}, + /* 8922A USB2.0 */ + .wde_qt7_v1 = {492, 6, 0, 10,}, + .wde_qt8_v1 = {608, 6, 0, 20,}, /* 8852C USB3.0 */ .wde_qt16 = {344, 2, 0, 8,}, /* 8852C DLFW */ @@ -1790,6 +1813,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt18 = {3228, 60, 0, 40,}, .wde_qt19_v1 = {613, 6, 0, 20,}, .wde_qt23 = {958, 48, 0, 16,}, + .wde_qt23_v1 = {371, 3, 0, 10,}, /* 8852B USB2.0/USB3.0 SCC turbo */ .wde_qt30 = {210, 2, 0, 8,}, /* 8852C USB2.0 */ @@ -1801,9 +1825,22 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE SCC */ .ple_qt5 = {264, 0, 32, 20, 64, 13, 1101, 0, 64, 128, 120,}, .ple_qt5_v2 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,}, + /* 8922A USB3.0 */ + .ple_qt6_v1 = {1806, 0, 16, 24, 7, 14, 57, 0, 12, 9, 1, 4, 0,}, + .ple_qt7_v1 = {1806, 0, 16, 24, 20, 27, 70, 0, 25, 9, 1, 17, 0,}, + .ple_qt8_v1 = {1638, 0, 16, 24, 7, 14, 99, 99, 24, 9, 1, 4, 0,}, + /* PCIE SCC */ .ple_qt9 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 1, 0, 0,}, + /* 8922A USB3.0 */ + .ple_qt9_v1 = {1638, 0, 16, 24, 35, 42, 127, 127, 52, 9, 1, 32, 0,}, + /* 8922A USB2.0 */ + .ple_qt12_v1 = {463, 0, 16, 24, 7, 14, 57, 0, 12, 9, 1, 4, 0,}, /* DLFW */ .ple_qt13 = {0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0,}, + /* 8922A USB2.0 */ + .ple_qt13_v1 = {463, 0, 16, 24, 1459, 1466, 1509, 0, 1464, 9, 1, 1456, 0,}, + .ple_qt14_v1 = {939, 0, 16, 24, 7, 14, 57, 57, 24, 9, 1, 4, 0,}, + .ple_qt15_v1 = {939, 0, 16, 24, 882, 889, 932, 932, 899, 9, 1, 879, 0,}, /* PCIE 64 */ .ple_qt18 = {147, 0, 16, 20, 17, 13, 89, 0, 32, 14, 8, 0,}, /* 8852A USB SCC */ @@ -1814,18 +1851,19 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt28 = {1040, 0, 32, 48, 43, 13, 208, 0, 62, 14, 24, 0,}, /* USB 52C USB3.0 */ .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, - .ple_qt42_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, /* USB 52C USB3.0 */ .ple_qt43 = {3068, 0, 32, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, - .ple_qt43_v2 = {645, 645, 32, 16, 2062, 2056, 2134, 2134, 2087, 2061, 1, 2047, 0, 0,}, /* DLFW 52C */ .ple_qt44 = {0, 0, 16, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_qt44_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, /* DLFW 52C */ .ple_qt45 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_qt45_v2 = {645, 645, 32, 2055, 2058, 2052, 2132, 2132, 2083, 2057, 1, 2043, 0, 0,}, /* 8852C PCIE SCC */ .ple_qt46 = {525, 0, 16, 20, 13, 13, 178, 0, 32, 62, 8, 16,}, /* 8852C PCIE SCC */ .ple_qt47 = {525, 0, 32, 20, 1034, 13, 1199, 0, 1053, 62, 160, 1037,}, + .ple_qt47_v2 = {0, 0, 32, 2703, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,}, .ple_qt57 = {147, 0, 16, 20, 13, 13, 178, 0, 32, 14, 8, 0,}, /* PCIE 64 */ .ple_qt58 = {147, 0, 16, 20, 157, 13, 229, 0, 172, 14, 24, 0,}, @@ -1833,6 +1871,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8851B USB2.0 SCC turbo */ .ple_qt61 = {858, 0, 16, 48, 4, 13, 370, 0, 32, 14, 8, 0, 0,}, .ple_qt62 = {858, 0, 32, 48, 37, 13, 403, 0, 65, 14, 24, 0, 0,}, + .ple_qt64_v2 = {91, 91, 32, 16, 19, 13, 93, 93, 44, 34, 1, 4, 0, 0,}, + .ple_qt65_v2 = {645, 645, 32, 1380, 1383, 1377, 1457, 1457, 1408, 1398, 1, 1368,}, /* USB2.0 52C */ .ple_qt78 = {1560, 0, 16, 48, 13, 13, 390, 0, 32, 38, 8, 16, 0,}, /* USB2.0 52C */ @@ -1847,23 +1887,30 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt_51b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, .ple_rsvd_qt0 = {2, 107, 107, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_rsvd_qt2 = {1, 56, 28, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt9 = {1, 44, 44, 6, 6, 6, 6, 69, 0, 0,}, .rsvd0_size0 = {212992, 0,}, + .rsvd0_size3 = {57344, 0,}, + .rsvd0_size5 = {32768, 0,}, .rsvd0_size6 = {40960, 0,}, + .rsvd0_size17 = {49152, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, - .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0,}, - .dle_input18 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, + .rsvd1_size3 = {315008, 78208,}, + .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0, 0,}, + .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 1024, 24, 24,}, + .dle_input28 = {128, 128, 11454, 2048, 0, 2048, 2048, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); static const struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode) { + const struct rtw89_qta_def *qta_def = rtw89_chip_get_qta_def(rtwdev); struct rtw89_mac_info *mac = &rtwdev->mac; const struct rtw89_dle_mem *cfg, *cfgs; - cfgs = rtwdev->chip->dle_mem[rtwdev->hci.dle_type]; + cfgs = qta_def->dle_mem[rtwdev->hci.dle_type]; if (!cfgs) return NULL; @@ -2300,6 +2347,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx, { u32 reg, max_preld_size, min_rsvd_size; + rtw89_io_pack(rtwdev); + 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 ? @@ -2313,6 +2362,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx, 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); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2392,11 +2443,15 @@ static int sta_sch_init_ax(struct rtw89_dev *rtwdev) return ret; } + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_AX_SS_CTRL, B_AX_SS_WARM_INIT_FLG); rtw89_write32_clr(rtwdev, R_AX_SS_CTRL, B_AX_SS_NONEMPTY_SS2FINFO_EN); _patch_ss2f_path(rtwdev); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2408,12 +2463,16 @@ static int mpdu_proc_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + rtw89_write32(rtwdev, R_AX_ACTION_FWD0, TRXCFG_MPDU_PROC_ACT_FRWD); rtw89_write32(rtwdev, R_AX_TF_FWD, TRXCFG_MPDU_PROC_TF_FRWD); rtw89_write32_set(rtwdev, R_AX_MPDU_PROC, B_AX_APPEND_FCS | B_AX_A_ICV_ERR); rtw89_write32(rtwdev, R_AX_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2427,6 +2486,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + val = rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL); /* init clock */ val |= (B_AX_CLK_EN_CGCMP | B_AX_CLK_EN_WAPI | B_AX_CLK_EN_WEP_TKIP); @@ -2452,6 +2513,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) rtw89_write32_mask(rtwdev, R_AX_SEC_DEBUG1, B_AX_TX_TIMEOUT_SEL_MASK, AX_TX_TO_VAL); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2535,6 +2598,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PREBKF_CFG_1, mac_idx); if (rtwdev->chip->chip_id == RTL8852C) rtw89_write32_mask(rtwdev, reg, B_AX_SIFS_MACTXEN_T1_MASK, @@ -2563,6 +2628,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) SCH_PREBKF_24US); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -2631,6 +2698,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + for (i = RTW89_MGNT; i <= RTW89_DATA; i++) { ret = rtw89_mac_typ_fltr_opt_ax(rtwdev, i, RTW89_FWD_TO_HOST, mac_idx); @@ -2647,6 +2716,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write16(rtwdev, rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx), plcp_ftlr); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2693,6 +2764,8 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_CCA_CONTROL, mac_idx); val = rtw89_read32(rtwdev, reg); val |= (B_AX_TB_CHK_BASIC_NAV | B_AX_TB_CHK_BTCCA | @@ -2713,16 +2786,22 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) _patch_dis_resp_chk(rtwdev, mac_idx); + rtw89_io_unpack(rtwdev); + return 0; } static int nav_ctrl_init_ax(struct rtw89_dev *rtwdev) { + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_PLCP_UP_NAV_EN | B_AX_WMAC_TF_UP_NAV_EN | B_AX_WMAC_NAV_UPPER_EN); rtw89_write32_mask(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_NAV_UPPER_MASK, NAV_25MS); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2734,12 +2813,17 @@ static int spatial_reuse_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); if (ret) return ret; + + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_SR_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_BSSID_SRC_CTRL, mac_idx); rtw89_write8_set(rtwdev, reg, B_AX_PLCP_SRC_EN); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2752,6 +2836,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_MAC_LOOPBACK, mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_MACLBK_EN); @@ -2762,6 +2848,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_HIGH_MCS_THRE_MASK, TXDFIFO_HIGH_MCS_THRE); rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_LOW_MCS_THRE_MASK, TXDFIFO_LOW_MCS_THRE); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2776,6 +2864,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TRXPTCL_RESP_0, mac_idx); val = rtw89_read32(rtwdev, reg); val &= ~B_AX_WMAC_SPEC_SIFS_CCK_MASK; @@ -2806,6 +2896,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, rrsr->rsc.addr, mac_idx); rtw89_write32_mask(rtwdev, reg, rrsr->rsc.mask, rrsr->rsc.data); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2840,6 +2932,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (mac_idx == RTW89_MAC_0) rst_bacam(rtwdev); @@ -2881,6 +2975,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_VHT_SU_SIGB_CRC_CHK); + rtw89_io_unpack(rtwdev); + return ret; } @@ -2932,6 +3028,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_SIFS_SETTING, mac_idx); val = rtw89_read32(rtwdev, reg); @@ -2969,6 +3067,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) B_AX_AMPDU_MAX_LEN_VHT_MASK, 0x3FF80); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -4229,6 +4329,8 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) return ret; } + rtw89_mac_fwdl_preconfig(rtwdev); + ret = rtw89_fw_download(rtwdev, RTW89_FW_NORMAL, include_bb); if (ret) return ret; @@ -5353,16 +5455,39 @@ rtw89_mac_c2h_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, } static void -rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len) { /* N.B. This will run in interrupt context. */ + const struct rtw89_c2h_rev_ack *c2h = (const void *)skb_c2h->data; + + u8 h2c_cat = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CAT); + u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS); + u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC); + u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ); + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n", - RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h->data)); + h2c_cat, h2c_class, h2c_func, h2c_seq); + + if (h2c_cat != H2C_CAT_MAC) + return; + + switch (h2c_class) { + default: + return; + case H2C_CL_MAC_FW_OFLD: + switch (h2c_func) { + default: + return; + case H2C_FUNC_SCANOFLD: + case H2C_FUNC_SCANOFLD_BE: + scan_info->seq++; + break; + } + + return; + } } static void @@ -5370,7 +5495,6 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le { /* N.B. This will run in interrupt context. */ struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; - struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_wait_info *ps_wait = &rtwdev->mac.ps_wait; const struct rtw89_c2h_done_ack *c2h = (const struct rtw89_c2h_done_ack *)skb_c2h->data; @@ -5413,11 +5537,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; case H2C_FUNC_SCANOFLD: - scan_info->seq++; cond = RTW89_SCANOFLD_WAIT_COND_START; break; case H2C_FUNC_SCANOFLD_BE: - scan_info->seq++; cond = RTW89_SCANOFLD_BE_WAIT_COND_START; h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; @@ -7368,6 +7490,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .reset_pwr_state = rtw89_mac_reset_pwr_state_ax, .disable_cpu = rtw89_mac_disable_cpu_ax, + .fwdl_preconfig = NULL, .fwdl_enable_wcpu = rtw89_mac_enable_cpu_ax, .fwdl_get_status = rtw89_fw_get_rdy_ax, .fwdl_check_path_ready = rtw89_fwdl_check_path_ready_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 9db9ae219cd6..539061fc15e8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -926,19 +926,27 @@ struct rtw89_mac_size_set { const struct rtw89_hfc_prec_cfg hfc_preccfg_pcie; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c0; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c2; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c3; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c5; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c6; const struct rtw89_dle_size wde_size0; const struct rtw89_dle_size wde_size1; const struct rtw89_dle_size wde_size0_v1; + const struct rtw89_dle_size wde_size3_v1; const struct rtw89_dle_size wde_size4; const struct rtw89_dle_size wde_size4_v1; + const struct rtw89_dle_size wde_size5_v1; const struct rtw89_dle_size wde_size6; const struct rtw89_dle_size wde_size7; + const struct rtw89_dle_size wde_size7_v1; + const struct rtw89_dle_size wde_size8_v1; const struct rtw89_dle_size wde_size9; const struct rtw89_dle_size wde_size16_v1; const struct rtw89_dle_size wde_size17; const struct rtw89_dle_size wde_size18; const struct rtw89_dle_size wde_size18_v1; const struct rtw89_dle_size wde_size19; + const struct rtw89_dle_size wde_size22_v1; const struct rtw89_dle_size wde_size23; const struct rtw89_dle_size wde_size30; const struct rtw89_dle_size wde_size31; @@ -946,8 +954,11 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size1; const struct rtw89_dle_size ple_size0_v1; const struct rtw89_dle_size ple_size3_v1; + const struct rtw89_dle_size ple_size4_v1; const struct rtw89_dle_size ple_size4; const struct rtw89_dle_size ple_size6; + const struct rtw89_dle_size ple_size6_v1; + const struct rtw89_dle_size ple_size7_v1; const struct rtw89_dle_size ple_size8; const struct rtw89_dle_size ple_size9; const struct rtw89_dle_size ple_size17; @@ -956,6 +967,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size20_v1; const struct rtw89_dle_size ple_size22_v1; const struct rtw89_dle_size ple_size27; + const struct rtw89_dle_size ple_size29_v1; const struct rtw89_dle_size ple_size31; const struct rtw89_dle_size ple_size34; const struct rtw89_wde_quota wde_qt0; @@ -963,13 +975,18 @@ struct rtw89_mac_size_set { const struct rtw89_wde_quota wde_qt0_v1; const struct rtw89_wde_quota wde_qt3; const struct rtw89_wde_quota wde_qt4; + const struct rtw89_wde_quota wde_qt4_v1; + const struct rtw89_wde_quota wde_qt5_v1; const struct rtw89_wde_quota wde_qt6; const struct rtw89_wde_quota wde_qt7; + const struct rtw89_wde_quota wde_qt7_v1; + const struct rtw89_wde_quota wde_qt8_v1; const struct rtw89_wde_quota wde_qt16; const struct rtw89_wde_quota wde_qt17; const struct rtw89_wde_quota wde_qt18; const struct rtw89_wde_quota wde_qt19_v1; const struct rtw89_wde_quota wde_qt23; + const struct rtw89_wde_quota wde_qt23_v1; const struct rtw89_wde_quota wde_qt30; const struct rtw89_wde_quota wde_qt31; const struct rtw89_ple_quota ple_qt0; @@ -977,26 +994,37 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt4; const struct rtw89_ple_quota ple_qt5; const struct rtw89_ple_quota ple_qt5_v2; + const struct rtw89_ple_quota ple_qt6_v1; + const struct rtw89_ple_quota ple_qt7_v1; + const struct rtw89_ple_quota ple_qt8_v1; const struct rtw89_ple_quota ple_qt9; + const struct rtw89_ple_quota ple_qt9_v1; + const struct rtw89_ple_quota ple_qt12_v1; const struct rtw89_ple_quota ple_qt13; + const struct rtw89_ple_quota ple_qt13_v1; + const struct rtw89_ple_quota ple_qt14_v1; + const struct rtw89_ple_quota ple_qt15_v1; const struct rtw89_ple_quota ple_qt18; const struct rtw89_ple_quota ple_qt25; const struct rtw89_ple_quota ple_qt26; const struct rtw89_ple_quota ple_qt27; const struct rtw89_ple_quota ple_qt28; const struct rtw89_ple_quota ple_qt42; - const struct rtw89_ple_quota ple_qt42_v2; const struct rtw89_ple_quota ple_qt43; - const struct rtw89_ple_quota ple_qt43_v2; const struct rtw89_ple_quota ple_qt44; + const struct rtw89_ple_quota ple_qt44_v2; const struct rtw89_ple_quota ple_qt45; + const struct rtw89_ple_quota ple_qt45_v2; const struct rtw89_ple_quota ple_qt46; const struct rtw89_ple_quota ple_qt47; + const struct rtw89_ple_quota ple_qt47_v2; const struct rtw89_ple_quota ple_qt57; const struct rtw89_ple_quota ple_qt58; const struct rtw89_ple_quota ple_qt59; const struct rtw89_ple_quota ple_qt61; const struct rtw89_ple_quota ple_qt62; + const struct rtw89_ple_quota ple_qt64_v2; + const struct rtw89_ple_quota ple_qt65_v2; const struct rtw89_ple_quota ple_qt78; const struct rtw89_ple_quota ple_qt79; const struct rtw89_ple_quota ple_qt_52a_wow; @@ -1006,13 +1034,19 @@ struct rtw89_mac_size_set { const struct rtw89_rsvd_quota ple_rsvd_qt0; const struct rtw89_rsvd_quota ple_rsvd_qt1; const struct rtw89_rsvd_quota ple_rsvd_qt1_v1; + const struct rtw89_rsvd_quota ple_rsvd_qt2; const struct rtw89_rsvd_quota ple_rsvd_qt9; const struct rtw89_dle_rsvd_size rsvd0_size0; + const struct rtw89_dle_rsvd_size rsvd0_size3; + const struct rtw89_dle_rsvd_size rsvd0_size5; const struct rtw89_dle_rsvd_size rsvd0_size6; + const struct rtw89_dle_rsvd_size rsvd0_size17; const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; + const struct rtw89_dle_rsvd_size rsvd1_size3; const struct rtw89_dle_input dle_input3; - const struct rtw89_dle_input dle_input18; + const struct rtw89_dle_input dle_input20; + const struct rtw89_dle_input dle_input28; }; extern const struct rtw89_mac_size_set rtw89_mac_size; @@ -1087,6 +1121,7 @@ struct rtw89_mac_gen_def { int (*reset_pwr_state)(struct rtw89_dev *rtwdev); void (*disable_cpu)(struct rtw89_dev *rtwdev); + void (*fwdl_preconfig)(struct rtw89_dev *rtwdev); int (*fwdl_enable_wcpu)(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw, bool include_bb); u8 (*fwdl_get_status)(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type); @@ -1700,6 +1735,17 @@ static inline int rtw89_mac_efuse_read_ecv(struct rtw89_dev *rtwdev) return mac->efuse_read_ecv(rtwdev); } +static inline +void rtw89_mac_fwdl_preconfig(struct rtw89_dev *rtwdev) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (!mac->fwdl_preconfig) + return; + + mac->fwdl_preconfig(rtwdev); +} + static inline void rtw89_fwdl_secure_idmem_share_mode(struct rtw89_dev *rtwdev, u8 mode) { diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 501c3af1da01..aade5c5b79e8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -98,6 +98,9 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) !rtwdev->scanning) rtw89_enter_ips(rtwdev); + if (changed & IEEE80211_CONF_CHANGE_MONITOR) + rtw89_physts_parsing_init(rtwdev); + return 0; } @@ -356,6 +359,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, } } + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) + rtwdev->hal.rx_fltr |= B_AX_SNIFFER_MODE; + else + rtwdev->hal.rx_fltr &= ~B_AX_SNIFFER_MODE; + rx_fltr = rtwdev->hal.rx_fltr; /* mac80211 doesn't configure filter when HW scan, driver need to @@ -721,14 +729,21 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_MLD_VALID_LINKS) { struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif); + u16 mac_id; if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) rtw89_chip_rfk_channel(rtwdev, cur); - if (hweight16(vif->active_links) == 1) + if (hweight16(vif->active_links) == 1) { + mac_id = cur->mac_id; rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; - else + } else { + /* Specify for all MAC ID */ + mac_id = 0xffff; rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR; + } + + rtw89_fw_h2c_tx_history(rtwdev, mac_id); } } @@ -964,7 +979,8 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, rtw89_err(rtwdev, "failed to add key to sec cam\n"); return ret; } - rtw89_core_tid_rx_stats_reset(rtwdev); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + rtw89_core_tid_rx_stats_reset(rtwdev); break; case DISABLE_KEY: flush_work(&rtwdev->txq_work); @@ -1241,6 +1257,7 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; + struct rtw89_hal *hal = &rtwdev->hal; int ret; lockdep_assert_wiphy(hw->wiphy); @@ -1248,6 +1265,12 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return 1; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_HW_SCAN)) { + rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, + "reject hw scan due to disabled_dm\n"); + return -EBUSY; + } + if (rtwdev->scanning || rtwvif->offchan) return -EBUSY; diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 39a28fd27412..f24c119b99f1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -622,6 +622,12 @@ static void rtw89_mac_disable_cpu_be(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_BE_UDM2, 0); } +static void rtw89_mac_fwdl_preconfig_be(struct rtw89_dev *rtwdev) +{ + rtw89_write32_clr(rtwdev, R_BE_FW_AUTO_CAL_DELAY, B_BE_WCPU_FW_DELAY_COUNT_VALID); + rtw89_write32_mask(rtwdev, R_BE_FW_AUTO_CAL_DELAY, B_BE_WCPU_FW_DELAY_COUNT_MASK, 0); +} + static void set_cpu_en(struct rtw89_dev *rtwdev, bool include_bb) { u32 set = B_BE_WLANCPU_FWDL_EN; @@ -1000,6 +1006,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_BE_MPDU_PROC, B_BE_APPEND_FCS); rtw89_write32(rtwdev, R_BE_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL | B_BE_CA_CHK_ADDRCAM_EN); @@ -1019,6 +1027,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev) val32 = u32_replace_bits(val32, 1, B_BE_FWD_WLAN_CPU_TYPE_1_MASK); rtw89_write32(rtwdev, R_BE_DISP_FWD_WLAN_0, val32); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1031,6 +1041,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + val32 = rtw89_read32(rtwdev, R_BE_SEC_ENG_CTRL); val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP | B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC | @@ -1041,6 +1053,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1083,13 +1097,16 @@ static int mlo_init_be(struct rtw89_dev *rtwdev) u32 reg; int ret; - val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL); + rtw89_io_pack(rtwdev); + val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL); val32 |= B_BE_MLO_TABLE_REINIT; rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32); val32 &= ~B_BE_MLO_TABLE_REINIT; rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32); + rtw89_io_unpack(rtwdev); + ret = read_poll_timeout_atomic(rtw89_read32, val32, val32 & B_BE_MLO_TABLE_INIT_DONE, 1, 1000, false, rtwdev, R_BE_MLO_INIT_CTL); @@ -1101,9 +1118,13 @@ static int mlo_init_be(struct rtw89_dev *rtwdev) else reg = R_BE_SS_CTRL_V1; + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, reg, B_BE_MLO_HW_CHGLINK_EN); rtw89_write32_set(rtwdev, R_BE_CMAC_SHARE_ACQCHK_CFG_0, B_BE_R_MACID_ACQ_CHK_EN); + rtw89_io_unpack(rtwdev); + return ret; } @@ -1173,6 +1194,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (chip->chip_id == RTL8922D) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SCH_EXT_CTRL, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_CWCNT_PLUS_MODE); @@ -1216,6 +1239,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_set(rtwdev, reg, B_BE_TX_NAV_RST_EDCA_EN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1292,6 +1317,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) u32 reg; u32 val; + rtw89_io_pack(rtwdev); + rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_MGNT, RTW89_FWD_TO_HOST, mac_idx); rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_CTRL, RTW89_FWD_TO_HOST, mac_idx); rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_DATA, RTW89_FWD_TO_HOST, mac_idx); @@ -1309,6 +1336,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) B_BE_LSIG_PARITY_CHK_EN | B_BE_CCK_SIG_CHK | B_BE_CCK_CRC_CHK; rtw89_write16(rtwdev, reg, val); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1322,6 +1351,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) u32 val32; u32 reg; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_NAV_CTL, mac_idx); val32 = rtw89_read32(rtwdev, reg); @@ -1337,6 +1368,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TRXPTCL_RESP_0, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_WMAC_MBA_DUR_FORCE); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1349,12 +1382,16 @@ static int spatial_reuse_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_BE_SR_EN | B_BE_SR_CTRL_PLCP_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_BSSID_SRC_CTRL, mac_idx); rtw89_write8_set(rtwdev, reg, B_BE_PLCP_SRC_EN); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1364,6 +1401,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) struct rtw89_hal *hal = &rtwdev->hal; u32 reg; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TB_PPDU_CTRL, mac_idx); rtw89_write32_clr(rtwdev, reg, B_BE_QOSNULL_UPD_MUEDCA_EN); @@ -1378,6 +1417,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_clr(rtwdev, reg, CLEAR_DTOP_DIS); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1394,6 +1435,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MAC_LOOPBACK, mac_idx); val32 = rtw89_read32(rtwdev, reg); val32 = u32_replace_bits(val32, S_BE_MACLBK_PLCP_DLY_DEF, @@ -1453,6 +1496,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_mask(rtwdev, reg, B_BE_RSC_MASK, 1); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1495,6 +1540,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_DLK_PROTECT_CTL, mac_idx); val16 = rtw89_read16(rtwdev, reg); val16 = u16_replace_bits(val16, TRXCFG_RMAC_DATA_TO, B_BE_RX_DLK_DATA_TIME_MASK); @@ -1534,6 +1581,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_set(rtwdev, reg, B_BE_DIS_CHK_MIN_LEN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1555,10 +1604,14 @@ static int resp_pktctl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RESP_CSI_RESERVED_PAGE, mac_idx); rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_START_PAGE_MASK, qt_cfg.pktid); rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_PAGE_NUM_MASK, qt_cfg.pg_num + 1); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1602,6 +1655,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (is_qta_poh(rtwdev)) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SIFS_SETTING, mac_idx); val32 = rtw89_read32(rtwdev, reg); @@ -1671,6 +1726,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) B_BE_PRELD_HIQ_P0_EN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1965,6 +2022,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, if (!(chip->chip_id == RTL8922A || rtw89_mac_chk_preload_allow(rtwdev))) return 0; + rtw89_io_pack(rtwdev); + max_preld_size = mac_idx == RTW89_MAC_0 ? PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM; if (chip->chip_id == RTL8922D) @@ -1997,6 +2056,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, val32 |= B_BE_B0_PRELD_FEN; rtw89_write32(rtwdev, reg, val32); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2750,6 +2811,10 @@ static int rtw89_mac_set_csi_para_reg_be(struct rtw89_dev *rtwdev, sound_dim = min(sound_dim, t); } + /* Disable stbc_en in VHT 160MHz to avoid IOT issues with certain APs */ + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) + stbc_en = 0; + nc = min(nc, sound_dim); nr = min(nr, sound_dim); @@ -3229,6 +3294,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .reset_pwr_state = rtw89_mac_reset_pwr_state_be, .disable_cpu = rtw89_mac_disable_cpu_be, + .fwdl_preconfig = rtw89_mac_fwdl_preconfig_be, .fwdl_enable_wcpu = rtw89_mac_fwdl_enable_wcpu_be, .fwdl_get_status = fwdl_get_status_be, .fwdl_check_path_ready = rtw89_fwdl_check_path_ready_be, diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 43c61b3dc969..102bae488180 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3039,6 +3039,17 @@ static int rtw89_pci_mode_op(struct rtw89_dev *rtwdev) return 0; } +static bool rtw89_pci_dev_ltr_enabled(struct rtw89_dev *rtwdev) +{ + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; + u16 cap; + + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &cap); + + return !!(cap & PCI_EXP_DEVCTL2_LTR_EN); +} + static int rtw89_pci_ops_deinit(struct rtw89_dev *rtwdev) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -3143,7 +3154,7 @@ int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en) { u32 val; - if (!en) + if (!en || !rtw89_pci_dev_ltr_enabled(rtwdev)) return 0; val = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0); @@ -3179,6 +3190,9 @@ int rtw89_pci_ltr_set_v1(struct rtw89_dev *rtwdev, bool en) u32 dec_ctrl; u32 val32; + if (!rtw89_pci_dev_ltr_enabled(rtwdev)) + return 0; + val32 = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0); if (rtw89_pci_ltr_is_err_reg_val(val32)) return -EINVAL; @@ -4366,10 +4380,20 @@ static void rtw89_pci_l1ss_cfg(struct rtw89_dev *rtwdev) static void rtw89_pci_cpl_timeout_cfg(struct rtw89_dev *rtwdev) { struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_hal *hal = &rtwdev->hal; struct pci_dev *pdev = rtwpci->pdev; + bool dis = true; - pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); + if (chip_id == RTL8922D && hal->cid == RTL8922D_CID7090) + dis = false; + + if (dis) + pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); + else + pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); } static int rtw89_pci_poll_io_idle_ax(struct rtw89_dev *rtwdev) @@ -4624,9 +4648,9 @@ EXPORT_SYMBOL(rtw89_pm_ops); static pci_ers_result_t rtw89_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -4643,12 +4667,12 @@ static pci_ers_result_t rtw89_pci_io_slot_reset(struct pci_dev *pdev) static void rtw89_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw89_pci_err_handler = { @@ -4748,8 +4772,7 @@ int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) info = (const struct rtw89_driver_info *)id->driver_data; rtwdev = rtw89_alloc_ieee80211_hw(&pdev->dev, - sizeof(struct rtw89_pci), - info->chip, info->variant); + sizeof(struct rtw89_pci), info); if (!rtwdev) { dev_err(&pdev->dev, "failed to allocate hw\n"); return -ENOMEM; diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index e7da37b9da7d..c3f2d0df5846 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -33,6 +33,8 @@ #define RAC_REG_REV2 0x1B #define BAC_CMU_EN_DLY_MASK GENMASK(15, 12) #define PCIE_DPHY_DLY_25US 0x1 +#define RAC_ANA14 0x14 +#define EIEOS_L1SS_WAIT_CLKRDY BIT(2) #define RAC_ANA19 0x19 #define B_PCIE_BIT_RD_SEL BIT(2) #define RAC_REG_FLD_0 0x1D @@ -56,7 +58,7 @@ #define B_AX_DIV GENMASK(15, 14) #define RAC_SET_PPR_V1 0x31 #define RAC_ANA40 0x40 -#define PHY_ERR_IMR_DIS (BIT(9) | BIT(0)) +#define PHY_ERR_IMR_DIS (BIT(9) | BIT(8) | BIT(0)) #define RAC_ANA41 0x41 #define PHY_ERR_FLAG_EN BIT(6) @@ -961,6 +963,8 @@ #define R_BE_PCIE_HRPWM 0x30C0 #define R_BE_PCIE_CRPWM 0x30C4 +#define R_BE_PCIE_HCI2FW_ISR 0x30CC + #define R_BE_L1_2_CTRL_HCILDO 0x3110 #define B_BE_PM_CLKREQ_EXT_RB BIT(11) #define B_BE_PCIE_DIS_RTK_PRST_N_L1_2 BIT(10) diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index dfffec1ff3c7..6390980b8ee0 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -83,6 +83,17 @@ static void _patch_pcie_power_wake_be(struct rtw89_dev *rtwdev, bool power_up) rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, BIT_WAKE_CTRL_V1); } +static void _patch_pre_init_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + if (!(rtwdev->chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + return; + + rtw89_write16_clr(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G2 + + RAC_ANA14 * RAC_MULT, EIEOS_L1SS_WAIT_CLKRDY); +} + static void rtw89_pci_set_io_rcy_be(struct rtw89_dev *rtwdev) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -336,6 +347,7 @@ static void rtw89_pci_pcie_setting_be(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_SYM_PRST_CPHY_RST); rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_USUS_OFFCAPC_EN); + rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF); } static void rtw89_pci_ser_setting_be(struct rtw89_dev *rtwdev) @@ -475,6 +487,7 @@ static int rtw89_pci_ops_mac_pre_init_be(struct rtw89_dev *rtwdev) rtw89_pci_set_io_rcy_be(rtwdev); _patch_pcie_power_wake_be(rtwdev, true); + _patch_pre_init_be(rtwdev); rtw89_pci_ctrl_wpdma_pcie_be(rtwdev, false); rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE); @@ -831,6 +844,8 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev) rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); } + + rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF); } rtw89_pci_basic_cfg(rtwdev, true); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index e70d0e283987..759be4dab42b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1352,17 +1352,17 @@ static void rtw89_phy_config_bb_reg(struct rtw89_dev *rtwdev, u32 addr; if (reg->addr == 0xfe) { - mdelay(50); + rtw89_io_mdelay(rtwdev, 50); } else if (reg->addr == 0xfd) { - mdelay(5); + rtw89_io_mdelay(rtwdev, 5); } else if (reg->addr == 0xfc) { - mdelay(1); + rtw89_io_mdelay(rtwdev, 1); } else if (reg->addr == 0xfb) { - udelay(50); + rtw89_io_udelay(rtwdev, 50); } else if (reg->addr == 0xfa) { - udelay(5); + rtw89_io_udelay(rtwdev, 5); } else if (reg->addr == 0xf9) { - udelay(1); + rtw89_io_udelay(rtwdev, 1); } else if (reg->data == BYPASS_CR_DATA) { rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Bypass CR 0x%x\n", reg->addr); } else { @@ -1692,17 +1692,17 @@ void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, void *extra_data) { if (reg->addr == 0xfe) { - mdelay(50); + rtw89_io_mdelay(rtwdev, 50); } else if (reg->addr == 0xfd) { - mdelay(5); + rtw89_io_mdelay(rtwdev, 5); } else if (reg->addr == 0xfc) { - mdelay(1); + rtw89_io_mdelay(rtwdev, 1); } else if (reg->addr == 0xfb) { - udelay(50); + rtw89_io_udelay(rtwdev, 50); } else if (reg->addr == 0xfa) { - udelay(5); + rtw89_io_udelay(rtwdev, 5); } else if (reg->addr == 0xf9) { - udelay(1); + rtw89_io_udelay(rtwdev, 1); } else { rtw89_write_rf(rtwdev, rf_path, reg->addr, 0xfffff, reg->data); rtw89_phy_cofig_rf_reg_store(rtwdev, reg, rf_path, @@ -1715,6 +1715,11 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, void *extra_data) { + if (reg->addr == 0xfe) { + rtw89_io_mdelay(rtwdev, 50); + return; + } + rtw89_write_rf(rtwdev, rf_path, reg->addr, RFREG_MASK, reg->data); if (reg->addr < 0x100) @@ -1885,11 +1890,16 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev) const struct rtw89_phy_table *bb_gain_table; bb_table = elm_info->bb_tbl ? elm_info->bb_tbl : chip->bb_table; + + rtw89_io_pack(rtwdev); + rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, NULL); if (rtwdev->dbcc_en) rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, (void *)RTW89_PHY_1); + rtw89_io_unpack(rtwdev); + rtw89_chip_init_txpwr_unit(rtwdev); bb_gain_table = elm_info->bb_gain ? elm_info->bb_gain : chip->bb_gain_table; @@ -2016,7 +2026,11 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio) else config = rf_table->config ? rf_table->config : rtw89_phy_config_rf_reg; + + rtw89_io_pack(rtwdev); rtw89_phy_init_reg(rtwdev, rf_table, by_acv, config, (void *)rf_reg_info); + rtw89_io_unpack(rtwdev); + if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info)) rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n", rf_reg_info->rf_path); @@ -3225,6 +3239,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, bool format_v1 = chip->chip_gen == RTW89_CHIP_BE; u8 mode, rate, bw, giltf, mac_id; u16 legacy_bitrate, amsdu_len; + u8 retry_ratio = 0; bool valid; u8 mcs = 0; u8 t; @@ -3245,6 +3260,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, bw |= u8_encode_bits(t, BIT(2)); t = le32_get_bits(c2h->w3, RTW89_C2H_RA_RPT_W3_MD_SEL_B2); mode |= u8_encode_bits(t, BIT(2)); + retry_ratio = le32_get_bits(c2h->w2, RTW89_C2H_RA_RPT_W2_RETRY_RATIO); } if (mode == RTW89_RA_RPT_MODE_LEGACY) { @@ -3320,6 +3336,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, u16_encode_bits(rate, RTW89_HW_RATE_V1_MASK_VAL) : u16_encode_bits(mode, RTW89_HW_RATE_MASK_MOD) | u16_encode_bits(rate, RTW89_HW_RATE_MASK_VAL); + ra_report->retry_ratio = retry_ratio; ra_report->might_fallback_legacy = mcs <= 2; amsdu_len = get_max_amsdu_len(rtwdev, ra_report); @@ -3365,12 +3382,24 @@ rtw89_phy_c2h_ra_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) &ra_data); } +static void +rtw89_phy_c2h_tx_history(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + const struct rtw89_c2h_ra_tx_history *history = (const void *)c2h->data; + u32 *tx_rate_cnt = rtwdev->phystat.tx_rate_cnt; + u32 i; + + for (i = 0; i < RTW89_TX_RATE_NR; i++) + tx_rate_cnt[i] = le32_to_cpu(history->tx_rate_tot_cnt_hist[i]); +} + static void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { [RTW89_PHY_C2H_FUNC_STS_RPT] = rtw89_phy_c2h_ra_rpt, [RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT] = NULL, [RTW89_PHY_C2H_FUNC_TXSTS] = NULL, + [RTW89_PHY_C2H_FUNC_TX_HISTORY] = rtw89_phy_c2h_tx_history, [RTW89_PHY_C2H_FUNC_ACCELERATE_EN] = rtw89_fw_c2h_dummy_handler, }; @@ -3956,6 +3985,14 @@ int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; unsigned long time_left; + /* + * USB transport adds latency to H2C/C2H round-trips, so RF + * calibrations take longer than on PCIe. Apply a 4x multiplier + * to avoid spurious timeouts. + */ + if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + ms *= 4; + /* Since we can't receive C2H event during SER, use a fixed delay. */ if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) { fsleep(1000 * ms / 2); @@ -5792,6 +5829,7 @@ static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev) static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_bb_ctx *bb; int i; for (i = 0; i < rtwdev->chip->rf_path_num; i++) @@ -5799,24 +5837,360 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtw89_phy_stat_thermal_update(rtwdev); - memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); - memset(&phystat->last_pkt_stat, 0, sizeof(phystat->last_pkt_stat)); + rtw89_for_each_capab_bb(rtwdev, bb) { + memset(&bb->cur_pkt_stat, 0, sizeof(bb->cur_pkt_stat)); + memset(&bb->last_pkt_stat, 0, sizeof(bb->last_pkt_stat)); - ewma_rssi_init(&phystat->bcn_rssi); + ewma_rssi_init(&bb->bcn_rssi); + } rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_pmac_diag(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +#define RTW89_DIAG_BB_FA_RATIO_TH 80 +#define RTW89_DIAG_BB_NO_RX_TH 5 +#define RTW89_DIAG_BB_HANG_TH 30 + struct rtw89_pmac_stat_info *pmac_stat = &bb->pmac_stat; + bool is_linked = rtwdev->total_sta_assoc > 0; + struct rtw89_diag_bb *diag = &bb->diag; + u8 fa_ratio = 0; + + if (!(pmac_stat->cck_mac_txen || pmac_stat->ofdm_mac_txen)) + diag->consecutive_no_tx_cnt++; + else + diag->consecutive_no_tx_cnt = 0; + + if (!pmac_stat->cnt_cca_all) + diag->consecutive_no_rx_cnt++; + else + diag->consecutive_no_rx_cnt = 0; + + if (pmac_stat->cnt_cca_all) + fa_ratio = pmac_stat->cnt_fail_all * PERCENT / pmac_stat->cnt_cca_all; + + diag->diag_bb_bitmap &= ~(BIT(RTW89_DIAG_BB_NO_RX) | + BIT(RTW89_DIAG_BB_HANG) | + BIT(RTW89_DIAG_BB_FA)); + + if (diag->consecutive_no_rx_cnt >= RTW89_DIAG_BB_NO_RX_TH && is_linked) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_NO_RX); + diag->diag_bb_cnt[RTW89_DIAG_BB_NO_RX]++; + } + + if (fa_ratio >= RTW89_DIAG_BB_FA_RATIO_TH && is_linked) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_FA); + diag->diag_bb_cnt[RTW89_DIAG_BB_FA]++; + } + + if (diag->consecutive_no_tx_cnt > RTW89_DIAG_BB_HANG_TH && + diag->consecutive_no_rx_cnt > RTW89_DIAG_BB_HANG_TH) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_HANG); + diag->diag_bb_cnt[RTW89_DIAG_BB_HANG]++; + } +} + +static void rtw89_phy_pmac_stat_reset(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool cck) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + + if (cck) { + rtw89_phy_write32_clr(rtwdev, regs->r1b_rx_rpt_rst.addr, + regs->r1b_rx_rpt_rst.mask); + rtw89_phy_write32_set(rtwdev, regs->r1b_rx_rpt_rst.addr, + regs->r1b_rx_rpt_rst.mask); + } + + rtw89_phy_write32_idx_set(rtwdev, regs->enable_all_cnt.addr, + regs->enable_all_cnt.mask, bb->phy_idx); + rtw89_phy_write32_idx_set(rtwdev, regs->rst_all_cnt.addr, + regs->rst_all_cnt.mask, bb->phy_idx); + rtw89_phy_write32_idx_clr(rtwdev, regs->rst_all_cnt.addr, + regs->rst_all_cnt.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_cck(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 val; + + pmac->cck_phy_txon = + rtw89_phy_read32_mask(rtwdev, regs->cck_txon.addr, regs->cck_txon.mask); + pmac->cck_mac_txen = + rtw89_phy_read32_mask(rtwdev, regs->cck_txen.addr, regs->cck_txen.mask); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + rtw89_phy_write32_mask(rtwdev, regs->r1b_rr_sel.addr, + regs->r1b_rr_sel.mask, 0x2); + + if (bb->phy_idx == RTW89_PHY_1) + pmac->cnt_cck_cca = + rtw89_phy_read32_mask(rtwdev, regs->cck_cca.addr + 8, + regs->cck_cca.mask); + else + pmac->cnt_cck_cca = + rtw89_phy_read32_mask(rtwdev, regs->cck_cca.addr, + regs->cck_cca.mask); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + rtw89_phy_write32_mask(rtwdev, regs->r1b_rr_sel.addr, + regs->r1b_rr_sel.mask, 0x1); + + if (bb->phy_idx == RTW89_PHY_1) + val = rtw89_phy_read32(rtwdev, regs->cck_crc32 + 8); + else + val = rtw89_phy_read32(rtwdev, regs->cck_crc32); + pmac->cnt_cck_crc32_ok = field_get(regs->cck_crc32_ok_mask, val); + pmac->cnt_cck_crc32_error = field_get(regs->cck_crc32_fail_mask, val); + + pmac->cnt_sfd_gg = + rtw89_phy_read32_idx(rtwdev, regs->cck_sfd_gg.addr, + regs->cck_sfd_gg.mask, bb->phy_idx); + pmac->cnt_sig_gg = + rtw89_phy_read32_idx(rtwdev, regs->cck_sig_gg.addr, + regs->cck_sig_gg.mask, bb->phy_idx); + pmac->cnt_cck_spoofing = + rtw89_phy_read32_idx(rtwdev, regs->cck_spoofing.addr, + regs->cck_spoofing.mask, bb->phy_idx); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + pmac->cnt_cck_fail = + pmac->cnt_cck_cca - pmac->cnt_cck_crc32_ok - + pmac->cnt_cck_crc32_error - pmac->cnt_cck_spoofing; + else + pmac->cnt_cck_fail = + rtw89_phy_read32_idx(rtwdev, regs->cck_brk.addr, + regs->cck_brk.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_ofdm(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 val; + + val = rtw89_phy_read32_idx(rtwdev, regs->ofdm_txon, MASKDWORD, bb->phy_idx); + pmac->ofdm_phy_txon = field_get(regs->ofdm_txon_mask, val); + pmac->ofdm_mac_txen = field_get(regs->ofdm_txen_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->l_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ofdm_crc32_ok = field_get(regs->l_crc_ok_mask, val); + pmac->cnt_ofdm_crc32_error = field_get(regs->l_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->ht_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ht_crc32_ok = field_get(regs->ht_crc_ok_mask, val); + pmac->cnt_ht_crc32_error = field_get(regs->ht_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->vht_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_vht_crc32_ok = field_get(regs->vht_crc_ok_mask, val); + pmac->cnt_vht_crc32_error = field_get(regs->vht_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->he_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_he_crc32_ok = field_get(regs->he_crc_ok_mask, val); + pmac->cnt_he_crc32_error = field_get(regs->he_crc_err_mask, val); + + if (chip->chip_gen == RTW89_CHIP_BE) { + val = rtw89_phy_read32_idx(rtwdev, regs->eht_crc, + MASKDWORD, bb->phy_idx); + pmac->cnt_eht_crc32_ok = field_get(regs->eht_crc_ok_mask, val); + pmac->cnt_eht_crc32_error = field_get(regs->eht_crc_err_mask, val); + } + + val = rtw89_phy_read32_idx(rtwdev, regs->ampdu_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ampdu_crc_ok = field_get(regs->ampdu_crc_ok_mask, val); + pmac->cnt_ampdu_crc_error = field_get(regs->ampdu_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->brk.addr, regs->brk.mask, bb->phy_idx); + if (chip->chip_id == RTL8852C && + rtw89_phy_read32_idx(rtwdev, regs->brk_option.addr, + regs->brk_option.mask, bb->phy_idx) == 1) { + u32 tmp = pmac->ofdm_phy_txon + pmac->cck_phy_txon; + + val = (val > tmp) ? (val - tmp) : 0; + } + pmac->cnt_ofdm_fail = val; + + pmac->cnt_sb_search_fail = + rtw89_phy_read32_idx(rtwdev, regs->search_fail.addr, + regs->search_fail.mask, bb->phy_idx); + pmac->cnt_lsig_brk_s_th = + rtw89_phy_read32_idx(rtwdev, regs->lsig_brk_s_th.addr, + regs->lsig_brk_s_th.mask, bb->phy_idx); + pmac->cnt_lsig_brk_l_th = + rtw89_phy_read32_idx(rtwdev, regs->lsig_brk_l_th.addr, + regs->lsig_brk_l_th.mask, bb->phy_idx); + pmac->cnt_parity_fail = + rtw89_phy_read32_idx(rtwdev, regs->rxl_err_parity.addr, + regs->rxl_err_parity.mask, bb->phy_idx); + pmac->cnt_rate_illegal = + rtw89_phy_read32_idx(rtwdev, regs->rxl_err_rate.addr, + regs->rxl_err_rate.mask, bb->phy_idx); + pmac->cnt_ofdm_cca = + rtw89_phy_read32_idx(rtwdev, regs->ofdm_cca.addr, + regs->ofdm_cca.mask, bb->phy_idx); + pmac->cnt_ofdm_spoofing = + rtw89_phy_read32_idx(rtwdev, regs->cca_spoofing.addr, + regs->cca_spoofing.mask, bb->phy_idx); + pmac->cnt_ampdu_miss = + rtw89_phy_read32_idx(rtwdev, regs->ampdu_miss.addr, + regs->ampdu_miss.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; + bool cck; + + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + cck = chan->band_type == RTW89_BAND_2G; + + if (cck) + rtw89_phy_pmac_stat_cck(rtwdev, bb); + + rtw89_phy_pmac_stat_ofdm(rtwdev, bb); + + pmac->cnt_fail_all = pmac->cnt_ofdm_fail; + pmac->cnt_cca_all = pmac->cnt_ofdm_cca; + + if (cck) { + pmac->cnt_fail_all += pmac->cnt_cck_fail; + pmac->cnt_cca_all += pmac->cnt_cck_cca; + } + + pmac->cnt_crc32_error_all = pmac->cnt_he_crc32_error + + pmac->cnt_vht_crc32_error + + pmac->cnt_ht_crc32_error + + pmac->cnt_ofdm_crc32_error + + pmac->cnt_cck_crc32_error; + + pmac->cnt_crc32_ok_all = pmac->cnt_he_crc32_ok + + pmac->cnt_vht_crc32_ok + + pmac->cnt_ht_crc32_ok + + pmac->cnt_ofdm_crc32_ok + + pmac->cnt_cck_crc32_ok; + + if (chip->chip_gen == RTW89_CHIP_BE) { + pmac->cnt_crc32_error_all += pmac->cnt_eht_crc32_error; + pmac->cnt_crc32_ok_all += pmac->cnt_eht_crc32_ok; + } + + rtw89_phy_pmac_diag(rtwdev, bb); + rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); +} + +static void rtw89_phy_tx_stat_update(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const struct rtw89_physts_regs *physts = phy->physts; + struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 reg_nr; + u32 val; + u32 i; + + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF && + bb->phy_idx != RTW89_PHY_0) + val = 1; + else + val = 0; + + rtw89_phy_write32_mask(rtwdev, physts->mac_phy_intf_sel.addr, + physts->mac_phy_intf_sel.mask, val); + + reg_nr = min(physts->tx_info.reg_nr, ARRAY_SIZE(tx_stat->info)); + for (i = 0; i < reg_nr; i++) + tx_stat->info[i] = rtw89_phy_read32(rtwdev, physts->tx_info.regs[i]); + + reg_nr = min(physts->tx_common_ctrl.reg_nr, ARRAY_SIZE(tx_stat->common_ctrl)); + for (i = 0; i < reg_nr; i++) + tx_stat->common_ctrl[i] = + rtw89_phy_read32(rtwdev, physts->tx_common_ctrl.regs[i]); + + reg_nr = min(chip->rf_path_num, ARRAY_SIZE(tx_stat->txpwr)); + for (i = 0; i < reg_nr; i++) + tx_stat->txpwr[i] = rtw89_phy_read32_mask(rtwdev, physts->txpwr[i].addr, + physts->txpwr[i].mask); + + tx_stat->type = u32_get_bits(tx_stat->info[0], TX_STATUS_TYPE); + + if (chip->chip_gen == RTW89_CHIP_AX) { + tx_stat->tx_path_en = u32_get_bits(tx_stat->info[0], TX_STATUS_TX_PATH_EN); + tx_stat->path_map = u32_get_bits(tx_stat->info[0], TX_STATUS_PATH_MAP); + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD); + tx_stat->txsc = u32_get_bits(tx_stat->info[1], TX_STATUS_TXSC); + tx_stat->bw = u32_get_bits(tx_stat->info[1], TX_STATUS_BW); + tx_stat->tmac_txpwr = u32_get_bits(tx_stat->info[1], TX_STATUS_TMAC_TXPWR); + + if (!(chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev))) + tx_stat->max_mcs = u32_get_bits(tx_stat->info[3], TX_STATUS_MAX_MCS); + } else { + tx_stat->type = u32_get_bits(tx_stat->info[0], TX_STATUS_TYPE); + tx_stat->subtype = u32_get_bits(tx_stat->info[0], TX_STATUS_SUBTYPE); + + if (chip->chip_id == RTL8922A) + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD_V1); + else + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD_V2); + + tx_stat->txsc = u32_get_bits(tx_stat->info[0], TX_STATUS_TXSC_V1); + tx_stat->bw = u32_get_bits(tx_stat->info[1], TX_STATUS_BW_V1); + tx_stat->tmac_txpwr = u32_get_bits(tx_stat->info[1], TX_STATUS_TMAC_TXPWR_V1); + tx_stat->tx_path_en = u32_get_bits(tx_stat->info[2], TX_STATUS_TX_PATH_EN_V1); + tx_stat->path_map = u32_get_bits(tx_stat->info[2], TX_STATUS_PATH_MAP_V1); + tx_stat->max_mcs = u32_get_bits(tx_stat->info[4], TX_STATUS_MAX_MCS_V1); + } + + tx_stat->stbc = !!(tx_stat->common_ctrl[0] & TX_STATUS_STBC); +} + +static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) +{ + if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) + rtw89_fw_h2c_phy_ch_rpt(rtwdev); + else + rtw89_fw_h2c_drv_ctrl_fw(rtwdev); +} + +static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + if (!rtwdev->phy_info.bb_stat_cfg.enable) + return; + + rtw89_phy_trigger_tx_count(rtwdev); + + rtw89_for_each_active_bb(rtwdev, bb) { + rtw89_phy_pmac_stat_update(rtwdev, bb); + rtw89_phy_tx_stat_update(rtwdev, bb); + } +} + void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) { - struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_bb_ctx *bb; rtw89_phy_stat_thermal_update(rtwdev); rtw89_phy_thermal_protect(rtwdev); rtw89_phy_stat_rssi_update(rtwdev); + rtw89_phy_stat_update(rtwdev); - phystat->last_pkt_stat = phystat->cur_pkt_stat; - memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); + rtw89_for_each_active_bb(rtwdev, bb) { + bb->last_pkt_stat = bb->cur_pkt_stat; + memset(&bb->cur_pkt_stat, 0, sizeof(bb->cur_pkt_stat)); + } } static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, @@ -6401,6 +6775,29 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev, return true; } +static bool rtw89_phy_edcca_clm_get_result(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + struct rtw89_env_monitor_info *env = &bb->env_monitor; + const struct rtw89_ccx_regs *ccx = phy->ccx; + u32 val; + + val = rtw89_phy_read32_idx(rtwdev, ccx->edcca_clm_rdy, + ccx->edcca_clm_rdy_mask, bb->phy_idx); + if (!val) { + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "Get EDCCA_CLM report Fail\n"); + return false; + } + + val = rtw89_phy_read32_idx(rtwdev, ccx->edcca_clm_cnt, + ccx->edcca_clm_cnt_mask, bb->phy_idx); + env->edcca_clm_ratio = rtw89_phy_ccx_get_report(rtwdev, bb, val, PERCENT); + + return true; +} + static void rtw89_phy_nhm_th_update(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -6568,6 +6965,9 @@ static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev, if (rtw89_phy_ifs_clm_get_result(rtwdev, bb)) env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM; + if (rtw89_phy_edcca_clm_get_result(rtwdev, bb)) + env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_EDCCA_CLM; + rtw89_phy_ccx_racing_release(rtwdev, bb); para.mntr_time = 1900; para.rac_lv = RTW89_RAC_LV_1; @@ -6685,10 +7085,21 @@ static void rtw89_physts_enable_hdr_2(struct rtw89_dev *rtwdev, enum rtw89_phy_i static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; const struct rtw89_chip_info *chip = rtwdev->chip; + u32 monitor_mode_mu_ies = 0; + u32 monitor_mode_su_ies = 0; u32 val; u8 i; + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { + monitor_mode_mu_ies = BIT(RTW89_PHYSTS_IE09_FTR_0); + if (phy->physt_gen < 2) + monitor_mode_mu_ies |= BIT(RTW89_PHYSTS_IE10_FTR_PLCP_EXT); + + monitor_mode_su_ies = BIT(RTW89_PHYSTS_IE09_FTR_0); + } + rtw89_physts_enable_fail_report(rtwdev, false, phy_idx); /* enable hdr_2 for 8922D (PHYSTS_BE_GEN2 above) */ @@ -6702,6 +7113,7 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, val = rtw89_physts_get_ie_bitmap(rtwdev, i, phy_idx); if (i == RTW89_HE_MU || i == RTW89_VHT_MU) { val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF); + val |= monitor_mode_mu_ies; } else if (i == RTW89_TRIG_BASE_PPDU) { val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF) | BIT(RTW89_PHYSTS_IE01_CMN_OFDM); @@ -6715,11 +7127,14 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, val |= BIT(RTW89_PHYSTS_IE20_DBG_OFDM_FD_USER_SEG_0); } + if (i == RTW89_HE_PKT || i == RTW89_VHT_PKT) + val |= monitor_mode_su_ies; + rtw89_physts_set_ie_bitmap(rtwdev, i, val, phy_idx); } } -static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) +void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) { __rtw89_physts_parsing_init(rtwdev, RTW89_PHY_0); if (rtwdev->dbcc_en) @@ -6825,11 +7240,15 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; const u16 *fa_th_src = NULL; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + switch (chan->band_type) { case RTW89_BAND_2G: dig->lna_gain = dig->lna_gain_g; @@ -6870,6 +7289,7 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev, dig->dyn_igi_max = igi_max_performance_mode; dig->dyn_igi_min = dynamic_igi_min; dig->dyn_pd_th_max = dynamic_pd_threshold_max; + dig->dyn_pd_max_cnt = 0; dig->pd_low_th_ofst = pd_low_th_offset; dig->is_linked_pre = false; } @@ -6993,6 +7413,7 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev, igi_offset = min_t(u8, igi_offset, IGI_OFFSET_MAX); dig->fa_rssi_ofst = igi_offset; + dig->noisy_lv = noisy_lv; rtw89_debug(rtwdev, RTW89_DBG_DIG, "fa_th: [+6 (%d) +4 (%d) +2 (%d) 0 (%d) -2 ]\n", @@ -7159,14 +7580,26 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, u8 rssi, bool enable) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_hal *hal = &rtwdev->hal; u8 final_rssi, under_region = dig->pd_low_th_ofst; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; s8 cck_cca_th; u32 pd_val; - pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { + pd_val = hal->fixed_dig_pd_th; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "Fixed DIG: PD_low=%d\n", pd_val); + } else { + pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); + } dig->bak_dig = pd_val; rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, @@ -7174,17 +7607,24 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx); - if (!rtwdev->hal.support_cckpd) + if (!hal->support_cckpd) return; - final_rssi = min_t(u8, rssi, dig->igi_rssi); - under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan); - cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI); - pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { + pd_val = hal->fixed_dig_cck_pd_th; - 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_debug(rtwdev, RTW89_DBG_DIG, + "Fixed DIG: cck_PD_low=((%d))dB\n", pd_val); + } else { + final_rssi = min_t(u8, rssi, dig->igi_rssi); + under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan); + 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_idx(rtwdev, dig_regs->bmode_pd_reg, dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx); @@ -7204,10 +7644,33 @@ void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) rtw89_phy_dig_update_para(rtwdev, bb); } +static void rtw89_phy_dig_diag(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool is_linked) +{ +#define RTW89_DIAG_BB_PD_MAX_PERIOD 5 + struct rtw89_diag_bb *diag = &bb->diag; + struct rtw89_dig_info *dig = &bb->dig; + + diag->diag_bb_bitmap &= ~BIT(RTW89_DIAG_BB_PD); + + if (dig->dyn_igi_max != dig->igi_fa_rssi || + dig->noisy_lv < RTW89_DIG_NOISY_LEVEL_MAX) { + dig->dyn_pd_max_cnt = 0; + return; + } + + if (++dig->dyn_pd_max_cnt < RTW89_DIAG_BB_PD_MAX_PERIOD || !is_linked) + return; + + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_PD); + diag->diag_bb_cnt[RTW89_DIAG_BB_PD]++; +} + #define IGI_RSSI_MIN 10 #define ABS_IGI_MIN 0xc static -void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool is_linked) { struct rtw89_dig_info *dig = &bb->dig; u8 igi_min; @@ -7225,6 +7688,8 @@ void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb } else { dig->igi_fa_rssi = dig->dyn_igi_max; } + + rtw89_phy_dig_diag(rtwdev, bb, is_linked); } struct rtw89_phy_iter_mcc_dig { @@ -7253,7 +7718,7 @@ static void rtw89_phy_set_mcc_dig(struct rtw89_dev *rtwdev, } chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); + rtw89_phy_cal_igi_fa_rssi(rtwdev, bb, is_linked); pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, is_linked, chan); rtw89_fw_h2c_mcc_dig(rtwdev, rtwvif_link->chanctx_idx, @@ -7315,6 +7780,7 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_hal *hal = &rtwdev->hal; bool en_dig; u32 pd_val; @@ -7323,10 +7789,16 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb if (pause_dig) { en_dig = false; - pd_val = 0; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) + pd_val = hal->fixed_dig_pd_th; + else + pd_val = 0; } else { en_dig = rtwdev->total_sta_assoc > 0; - pd_val = restore ? dig->bak_dig : 0; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) + pd_val = hal->fixed_dig_pd_th; + else + pd_val = restore ? dig->bak_dig : 0; } rtw89_debug(rtwdev, RTW89_DBG_DIG, "%s <%s> PD_low=%d", __func__, @@ -7391,7 +7863,7 @@ static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) } dig->is_linked_pre = is_linked; - rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); + rtw89_phy_cal_igi_fa_rssi(rtwdev, bb, is_linked); rtw89_debug(rtwdev, RTW89_DBG_DIG, "rssi=%03d, dyn_joint(max,min)=(%d,%d), final_rssi=%d\n", @@ -7646,6 +8118,14 @@ static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) __rtw89_phy_env_monitor_init(rtwdev, bb); } +static void rtw89_phy_diag_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + memset(&bb->diag, 0, sizeof(bb->diag)); +} + static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -7686,6 +8166,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_chip_bb_sethw(rtwdev); + rtw89_phy_diag_init(rtwdev); rtw89_phy_env_monitor_init(rtwdev); rtw89_phy_nhm_setting_init(rtwdev); rtw89_physts_parsing_init(rtwdev); @@ -7750,12 +8231,24 @@ void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev) __rtw89_phy_dm_init_data(rtwdev, bb); } +void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld; + + rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1, + phy_idx); + rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT, + bss_color, phy_idx); + rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID, + aid, phy_idx); +} + void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); - const struct rtw89_chip_info *chip = rtwdev->chip; - const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; struct ieee80211_bss_conf *bss_conf; u8 bss_color; @@ -7772,12 +8265,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, rcu_read_unlock(); - rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1, - phy_idx); - rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT, - bss_color, phy_idx); - rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID, - vif->cfg.aid, phy_idx); + __rtw89_phy_set_bss_color(rtwdev, bss_color, vif->cfg.aid, phy_idx); } static bool rfk_chan_validate_desc(const struct rtw89_rfk_chan_desc *desc) @@ -8262,6 +8750,24 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb edcca_regs->ppdu_mask, th, bb->phy_idx); } +static void rtw89_phy_edcca_diag(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +#define RTW89_DIAG_BB_EDCCA_RATIO_TH 50 +#define RTW89_DIAG_BB_EDCCA_TX_TH 10 + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct rtw89_diag_bb *diag = &bb->diag; + + diag->diag_bb_bitmap &= ~BIT(RTW89_DIAG_BB_EDCCA); + + if (stats->tx_throughput <= RTW89_DIAG_BB_EDCCA_TX_TH || + env->edcca_clm_ratio <= RTW89_DIAG_BB_EDCCA_RATIO_TH) + return; + + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_EDCCA); + diag->diag_bb_cnt[RTW89_DIAG_BB_EDCCA]++; +} + static void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -8269,6 +8775,7 @@ void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) rtw89_phy_edcca_thre_calc(rtwdev, bb); rtw89_phy_edcca_log(rtwdev, bb); + rtw89_phy_edcca_diag(rtwdev, bb); } void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev) @@ -8419,12 +8926,37 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_ax = { .nhm_en_mask = B_NHM_EN_MSK, .nhm_method = R_NHM_TH9, .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, + .edcca_clm_rdy = R_CLM_EDCCA_RDY, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, +}; + +static const u32 rtw89_txinfo_reg_ax[] = { + R_TX_INFO_0_0_COMB, + R_TX_INFO_0_1_COMB, + R_TX_INFO_1_0_COMB, + R_TX_INFO_1_1_COMB +}; + +static const u32 rtw89_tx_common_ctrl_reg_ax[] = { + R_TX_COMMON_CTRL_0_0_COMB, + R_TX_COMMON_CTRL_0_1_COMB +}; + +static const struct rtw89_reg_def rtw89_txpwr_ax[] = { + {.addr = R_PATH0_TXPWR, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR, .mask = B_PATH1_TXPWR} }; static const struct rtw89_physts_regs rtw89_physts_regs_ax = { .setting_addr = R_PLCP_HISTOGRAM, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_ax, + .tx_info = RTW89_REGS_DEF(rtw89_txinfo_reg_ax), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_ax), }; static const struct rtw89_cfo_regs rtw89_cfo_regs_ax = { @@ -8438,6 +8970,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_ax = { .cr_base = 0x10000, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START, .physt_bmp_eht = 0xfc, + .physt_ie_len = {16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, + VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 0, .ccx = &rtw89_ccx_regs_ax, .physts = &rtw89_physts_regs_ax, .cfo = &rtw89_cfo_regs_ax, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index bde419edf744..532232892831 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -57,6 +57,25 @@ #define RA_MASK_EHT_4SS_MCS0_11 GENMASK_ULL(62, 60) #define RA_MASK_EHT_RATES GENMASK_ULL(62, 12) +#define TX_STATUS_TYPE GENMASK(3, 0) +#define TX_STATUS_SUBTYPE GENMASK(7, 4) +#define TX_STATUS_TXCMD GENMASK(29, 24) +#define TX_STATUS_TXCMD_V1 GENMASK(13, 8) +#define TX_STATUS_TXCMD_V2 GENMASK(15, 8) +#define TX_STATUS_TXSC GENMASK(7, 4) +#define TX_STATUS_TXSC_V1 GENMASK(27, 24) +#define TX_STATUS_BW GENMASK(17, 16) +#define TX_STATUS_BW_V1 GENMASK(6, 4) +#define TX_STATUS_TMAC_TXPWR GENMASK(26, 18) +#define TX_STATUS_TMAC_TXPWR_V1 GENMASK(16, 8) +#define TX_STATUS_TX_PATH_EN GENMASK(15, 12) +#define TX_STATUS_TX_PATH_EN_V1 GENMASK(7, 4) +#define TX_STATUS_PATH_MAP GENMASK(23, 16) +#define TX_STATUS_PATH_MAP_V1 GENMASK(15, 8) +#define TX_STATUS_MAX_MCS GENMASK(7, 4) +#define TX_STATUS_MAX_MCS_V1 GENMASK(3, 0) +#define TX_STATUS_STBC BIT(0) + #define CFO_TRK_ENABLE_TH (2 << 2) #define CFO_TRK_STOP_TH_4 (30 << 2) #define CFO_TRK_STOP_TH_3 (20 << 2) @@ -135,10 +154,14 @@ #define EDCCA_UNIT_CONVER 128 #define EDCCA_PWROFST_DEFAULT 18 +#define VAR_LEN 0xff +#define VAR_LEN_UNIT 8 + enum rtw89_phy_c2h_ra_func { RTW89_PHY_C2H_FUNC_STS_RPT, RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT, RTW89_PHY_C2H_FUNC_TXSTS, + RTW89_PHY_C2H_FUNC_TX_HISTORY = 0x4, RTW89_PHY_C2H_FUNC_ACCELERATE_EN = 0x7, RTW89_PHY_C2H_FUNC_RA_NUM, @@ -451,12 +474,20 @@ struct rtw89_ccx_regs { u32 nhm_en_mask; u32 nhm_method; u32 nhm_pwr_method_msk; + u32 edcca_clm_rdy; + u32 edcca_clm_rdy_mask; + u32 edcca_clm_cnt; + u32 edcca_clm_cnt_mask; }; struct rtw89_physts_regs { u32 setting_addr; u32 dis_trigger_fail_mask; u32 dis_trigger_brk_mask; + struct rtw89_reg_def mac_phy_intf_sel; + const struct rtw89_reg_def *txpwr; + struct rtw89_regs_def tx_info; + struct rtw89_regs_def tx_common_ctrl; }; struct rtw89_cfo_regs { @@ -537,14 +568,113 @@ struct rtw89_txpwr_limit_ru_be { s8 ru106_26[RTW89_RU_SEC_NUM_BE]; }; +#define RTW89_RU484_242_SEC_NUM_BE 4 +#define RTW89_RU996_484_SEC_NUM_BE 2 +#define RTW89_RU996_484_242_SEC_NUM_BE 2 + +struct rtw89_txpwr_limit_large_mru_be { + s8 ru484_242[RTW89_NSS_NUM][RTW89_RU484_242_SEC_NUM_BE]; + s8 ru996_484[RTW89_NSS_NUM][RTW89_RU996_484_SEC_NUM_BE]; + s8 ru996_484_242[RTW89_NSS_NUM][RTW89_RU996_484_242_SEC_NUM_BE]; +}; + struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +enum rtw89_mdpd_onoff { + MDPD_ON = 0, + MDPD_OFF = 1, +}; + +enum rtw89_oob_dpd_onoff { + OOB_DPD_OFF = 0, + OOB_DPD_ON = 1, +}; + +enum rtw89_cim3k_onoff { + CIM3K_ON = 1, + CIM3K_OFF = 0, +}; + +enum rtw89_cim3k_en_dis { + CIM3K_ENABLE = 1, + CIM3K_DISABLE = 0, +}; + +enum rtw89_rfsi_ctrl_modulation { + RFSI_BPSK = 0, + RFSI_QPSK = 1, + RFSI_16QAM = 2, + RFSI_64QAM = 3, + RFSI_256QAM = 4, + RFSI_1024QAM = 5, + RFSI_4096QAM = 6, + RFSI_MAX, +}; + +enum rtw89_cfir_onoff { + CFIR_ON = 1, + CFIR_OFF = 0, +}; + +enum rtw89_filter_onoff { + FILTER_A_ON = 1, + FILTER_A_OFF = 0, +}; + +#define MAX_TX_RFSI_CTRL_OPT 10 + +#define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ + ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ + (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) + +#define _qam_comp_code(c) ((((c) & (BIT(15) | BIT(14))) >> 11) | \ + (((c) & BIT(12)) >> 10) | \ + (((c) & (BIT(9) | BIT(8))) >> 8)) + +#define _10qam_comp_code(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) \ + _qam_comp_code(c0), _qam_comp_code(c1), _qam_comp_code(c2), _qam_comp_code(c3), \ + _qam_comp_code(c4), _qam_comp_code(c5), _qam_comp_code(c6), _qam_comp_code(c7), \ + _qam_comp_code(c8), _qam_comp_code(c9) + +struct rtw89_bb_wrap_common_data { + struct { + struct rtw89_bb_wrap_data_cim3k { + u8 th, ow, non_bandedge, bandedge; + } cim3k; + u32 rfsi_ct_opt[2]; + u8 pb_tb; + } bands[RFSI_CTRL_BAND_NUM]; + u8 qam_th[6]; +}; + +struct rtw89_bb_wrap_common_data_gen3 { + struct { + u8 qam_th[6]; + } bands[RFSI_CTRL_BAND_NUM]; + u8 cck_val[2]; +}; + +struct rtw89_bb_wrap_data { + const struct rtw89_bb_wrap_common_data *common; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + struct { + u16 qam_comp_th0[MAX_TX_RFSI_CTRL_OPT]; + u16 qam_comp_th1[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ + u16 qam_comp_th2[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ + u16 qam_comp_ow[MAX_TX_RFSI_CTRL_OPT]; + u8 oob_dpd_by_cbw[8]; + } bands[RFSI_CTRL_BAND_NUM]; + u8 mdpd_by_dbw[4]; +}; + struct rtw89_phy_gen_def { u32 cr_base; u32 physt_bmp_start; u32 physt_bmp_eht; + u8 physt_ie_len[32]; + u8 physt_gen; const struct rtw89_ccx_regs *ccx; const struct rtw89_physts_regs *physts; const struct rtw89_cfo_regs *cfo; @@ -576,30 +706,48 @@ extern const struct rtw89_phy_gen_def rtw89_phy_gen_ax; extern const struct rtw89_phy_gen_def rtw89_phy_gen_be; extern const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1; -static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, - u32 addr, u8 data) +static inline void rtw89_raw_phy_write8(struct rtw89_dev *rtwdev, + u32 addr, u8 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write8(rtwdev, addr + phy->cr_base, data); } -static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev, - u32 addr, u16 data) +static inline void rtw89_raw_phy_write16(struct rtw89_dev *rtwdev, + u32 addr, u16 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write16(rtwdev, addr + phy->cr_base, data); } -static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev, - u32 addr, u32 data) +static inline void rtw89_raw_phy_write32(struct rtw89_dev *rtwdev, + u32 addr, u32 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write32(rtwdev, addr + phy->cr_base, data); } +static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, + u32 addr, u8 data) +{ + rtwdev->io->phy_write8(rtwdev, addr, data); +} + +static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev, + u32 addr, u16 data) +{ + rtwdev->io->phy_write16(rtwdev, addr, data); +} + +static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev, + u32 addr, u32 data) +{ + rtwdev->io->phy_write32(rtwdev, addr, data); +} + static inline void rtw89_phy_write32_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits) { @@ -863,6 +1011,7 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, void rtw89_phy_dm_init(struct rtw89_dev *rtwdev); void rtw89_phy_dm_reinit(struct rtw89_dev *rtwdev); void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev); +void rtw89_physts_parsing_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); void rtw89_phy_write32_idx_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits, @@ -904,6 +1053,7 @@ static inline void rtw89_phy_bb_wrap_init(struct rtw89_dev *rtwdev) } void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, + enum rtw89_rfsi_ctrl_band ctrl_band, enum rtw89_phy_idx phy_idx); void rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, @@ -1076,6 +1226,8 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid, + enum rtw89_phy_idx phy_idx); void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, enum rtw89_tssi_bandedge_cfg bandedge_cfg); diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 929fac1b10d2..99263355e2f1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -74,6 +74,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be = { .nhm_en_mask = B_NHM_EN_MSK, .nhm_method = R_NHM_TH9, .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, + .edcca_clm_rdy = R_CLM_EDCCA_RDY_V1, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT_V1, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { @@ -133,18 +137,68 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { .ifs_total_addr = R_IFS_TOTAL_BE4, .ifs_cnt_done_mask = B_IFS_CNT_DONE_BE4, .ifs_total_mask = B_IFS_TOTAL_BE4, + .edcca_clm_rdy = R_CLM_EDCCA_RDY_BE4, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT_BE4, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, +}; + +static const u32 rtw89_tx_info_reg_be[] = { + R_TX_INFO_0_0_COMB_V1, + R_TX_INFO_0_1_COMB_V1, + R_TX_INFO_1_0_COMB_V1, + R_TX_INFO_1_1_COMB_V1, + R_TX_INFO_2_0_COMB_V1, + R_TX_INFO_2_1_COMB_V1 +}; + +static const u32 rtw89_tx_common_ctrl_reg_be[] = { + R_TX_COMMON_CTRL_0_0_COMB_V1, + R_TX_COMMON_CTRL_0_1_COMB_V1 +}; + +static const struct rtw89_reg_def rtw89_txpwr_be[] = { + {.addr = R_PATH0_TXPWR_V1, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR_V1, .mask = B_PATH1_TXPWR} }; static const struct rtw89_physts_regs rtw89_physts_regs_be = { .setting_addr = R_PLCP_HISTOGRAM, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_be, + .tx_info = RTW89_REGS_DEF(rtw89_tx_info_reg_be), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_be), +}; + +static const u32 rtw89_tx_info_reg_be_v1[] = { + R_TX_INFO_0_0_COMB_BE4, + R_TX_INFO_0_1_COMB_BE4, + R_TX_INFO_1_0_COMB_BE4, + R_TX_INFO_1_1_COMB_BE4, + R_TX_INFO_2_0_COMB_BE4, + R_TX_INFO_2_1_COMB_BE4 +}; + +static const u32 rtw89_tx_common_ctrl_reg_be_v1[] = { + R_TX_COMMON_CTRL_0_0_COMB_BE4, + R_TX_COMMON_CTRL_0_1_COMB_BE4 +}; + +static const struct rtw89_reg_def rtw89_txpwr_be_v1[] = { + {.addr = R_PATH0_TXPWR_BE4, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR_BE4, .mask = B_PATH1_TXPWR} }; static const struct rtw89_physts_regs rtw89_physts_regs_be_v1 = { .setting_addr = R_PLCP_HISTOGRAM_BE_V1, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL_BE4, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_be_v1, + .tx_info = RTW89_REGS_DEF(rtw89_tx_info_reg_be_v1), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_be_v1), }; static const struct rtw89_cfo_regs rtw89_cfo_regs_be = { @@ -533,8 +587,8 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_LMT, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_LMT_ON, 0); - addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RATE_ON, 0); + addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); + rtw89_write32_mask(rtwdev, addr, B_BE_FORCE_PWR_BY_RATE_EN, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_OFST_RULMT, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0); @@ -542,8 +596,8 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0); - addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_FORCE_PWR_BY_RATE_EN, 0); + addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, mac_idx); + rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RATE_ON, 0); } static void rtw89_phy_bb_wrap_ftm_init(struct rtw89_dev *rtwdev, @@ -592,15 +646,23 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, } void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, + enum rtw89_rfsi_ctrl_band rfsi_band, enum rtw89_phy_idx phy_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const u32 *val; u32 reg; + if (!d || !d->common) + return; + + val = d->common->bands[rfsi_band].rfsi_ct_opt; + reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_0_BE4, phy_idx); - rtw89_write32(rtwdev, reg, 0x00010001); + rtw89_write32(rtwdev, reg, val[0]); reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_8_BE4, phy_idx); - rtw89_write32(rtwdev, reg, 0x00010001); + rtw89_write32(rtwdev, reg, val[1]); } EXPORT_SYMBOL(rtw89_phy_bb_wrap_set_rfsi_ct_opt); @@ -630,42 +692,52 @@ EXPORT_SYMBOL(rtw89_phy_bb_wrap_set_rfsi_bandedge_ch); static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th0, th1, th2; + + if (!d || !d->common) + return; + + th0 = d->common->qam_th[0]; + th1 = d->common->qam_th[1]; + th2 = d->common->qam_th[2]; + /* TH0 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_1_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_4_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_7_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_6_BE4, 0x1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_1_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_4_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_7_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_6_BE4, th0, mac_idx); /* TH1 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_1_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_4_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_2_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_5_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_8_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_1_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_4_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_7_BE4, 0x2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_1_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_4_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_2_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_5_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_8_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_1_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_4_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_7_BE4, th1, mac_idx); /* TH2 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_2_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_0_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_3_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_6_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_9_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_2_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_5_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_8_BE4, 0x4, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_2_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_0_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_3_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_6_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_9_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_2_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_5_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_8_BE4, th2, mac_idx); /* DPD 160M */ - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_1_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_2_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_4_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_5_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_6_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_7_BE4, 0x1, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_1_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_2_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_4_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_5_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_6_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_7_BE4, th0, mac_idx); /* DPD 20M */ rtw89_write32_idx(rtwdev, R_DPD_CBW_TH0_BE4, B_DPD_CBW20_TH0_0_BE4, 0x2, mac_idx); rtw89_write32_idx(rtwdev, R_DPD_CBW_TH0_BE4, B_DPD_CBW20_TH0_1_BE4, 0x2, mac_idx); @@ -697,16 +769,91 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH2_BE4, 0x2, mac_idx); } +static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_gen3_init(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + const u8 *ths; + + if (!d || !d->common_gen3) + return; + + common_gen3 = d->common_gen3; + ths = common_gen3->bands[chan->rfsi_band].qam_th; + + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_0_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_1_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_2_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_3_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_4_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_5_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_6_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_7_BE4, ths[0], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH1_0_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH1_1_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_2_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_3_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_4_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_5_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_6_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_7_BE4, ths[1], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_0_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_1_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_2_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_3_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_4_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_5_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_6_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_7_BE4, ths[2], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_0_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_1_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_2_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_3_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_4_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_5_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH3_6_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH3_7_BE4, ths[3], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_0_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_1_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_2_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_3_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_4_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_5_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_6_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_7_BE4, ths[4], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_0_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_1_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_2_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_3_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_4_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_5_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_6_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_7_BE4, ths[5], mac_idx); +} + static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 pb_tb = 0; + + if (d && d->common) + pb_tb = d->common->bands[0].pb_tb; + rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_ER_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_SUBF_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_FTM_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_SENS_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DEF_BE, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_PB_BE4, 0x0, mac_idx); + rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_PB_BE4, pb_tb, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DL_WO_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DL_BF_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_MUMIMO_BE4, 0x0, mac_idx); @@ -717,147 +864,229 @@ static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, 0x4410, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const u16 *th; - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_L, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_M, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2L, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2M, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2M, 0x0, mac_idx); + if (!d || !d->common) + return; - rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, 0x0, mac_idx); + th = d->bands[0].qam_comp_th0; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, th[7], mac_idx); + + th = d->bands[0].qam_comp_th1; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_L, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_M, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_H, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_L, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_M, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_H, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_L, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_M, th[7], mac_idx); + + th = d->bands[0].qam_comp_th2; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2L, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2M, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2H, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2L, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2M, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2H, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2L, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2M, th[7], mac_idx); + + th = d->bands[0].qam_comp_ow; + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, th[7], mac_idx); } static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK7_BE4, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th; - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK7_BE4, 0x0, mac_idx); + if (!d) + return; - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[0]; + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[1]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[2]; + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[3]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[4]; + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[5]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW7_BE4, th, mac_idx); + + th = d->bands[0].oob_dpd_by_cbw[6]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW7_BE4, th, mac_idx); + + th = d->bands[0].oob_dpd_by_cbw[7]; + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW7_BE4, th, mac_idx); } static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH7_BE4, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th; - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW7_BE4, 0x0, mac_idx); + if (!d) + return; + + th = d->mdpd_by_dbw[0]; + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH7_BE4, th, mac_idx); + + th = d->mdpd_by_dbw[2]; + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW7_BE4, th, mac_idx); } static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_OW_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_NONBE_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_BANDEDGE_BE4, 0x1, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_data_cim3k *p; + + if (!d || !d->common) + return; + + p = &d->common->bands[0].cim3k; + + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH_BE4, p->th, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_OW_BE4, p->ow, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_NONBE_BE4, + p->non_bandedge, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_BANDEDGE_BE4, + p->bandedge, mac_idx); + + if (rtwdev->chip->chip_id != RTL8922D) + return; + + rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_EN, 1, mac_idx); + rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_VAL, 0, mac_idx); +} + +static void rtw89_phy_bb_set_cck_cfir_filter_val_gen3(struct rtw89_dev *rtwdev, + enum rtw89_mac_idx mac_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + u8 cck0, cck1; + + if (!d || !d->common_gen3) + return; + + common_gen3 = d->common_gen3; + cck0 = common_gen3->cck_val[0]; + cck1 = common_gen3->cck_val[1]; + + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR0_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR1_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR2_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR3_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR4_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR5_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR6_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR7_BE4, cck0, mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR0_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR1_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR2_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR3_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR4_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR5_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR6_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR7_BE4, cck1, mac_idx); } static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, @@ -865,23 +1094,25 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, { enum rtw89_phy_idx phy_idx = mac_idx != RTW89_MAC_0 ? RTW89_PHY_1 : RTW89_PHY_0; enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_entity_conf conf; const struct rtw89_chan *chan; if (chip_id != RTL8922D) return; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[phy_idx]; + rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(rtwdev, mac_idx); + rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_gen3_init(rtwdev, chan, mac_idx); rtw89_phy_bb_wrap_tx_rfsi_scenario_def(rtwdev, mac_idx); rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_oob_dpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_mdpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_cim3k_val(rtwdev, mac_idx); - - rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); - - chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); - if (chan) - rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); + rtw89_phy_bb_set_cck_cfir_filter_val_gen3(rtwdev, mac_idx); + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, 0, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); } static void rtw89_phy_bb_wrap_ul_pwr(struct rtw89_dev *rtwdev) @@ -1468,11 +1699,168 @@ static void rtw89_phy_fill_limit_ru_be(struct rtw89_dev *rtwdev, } } +static +void rtw89_phy_fill_limit_ru484_242_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU484_242_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_80: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 8); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 8); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 24); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 8); + (*lmt)[2] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 8); + (*lmt)[3] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 24); + break; + } +} + +static +void rtw89_phy_fill_limit_ru996_484_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU996_484_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch - 16); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch + 16); + break; + } +} + +static +void rtw89_phy_fill_limit_ru996_484_242_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU996_484_242_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch - 16); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch + 16); + break; + } +} + +static +void rtw89_phy_fill_limit_large_mru_be(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + struct rtw89_txpwr_limit_large_mru_be *lmt, + bool has_bf) +{ + u8 band = chan->band_type; + u8 ch = chan->channel; + u8 bw = chan->band_width; + int i; + + memset(lmt, 0, sizeof(*lmt)); + + if (has_bf) + return; + + for (i = 0; i <= RTW89_NSS_2; i++) { + rtw89_phy_fill_limit_ru484_242_be(rtwdev, &lmt->ru484_242[i], + i, band, ch, bw); + rtw89_phy_fill_limit_ru996_484_be(rtwdev, &lmt->ru996_484[i], + i, band, ch, bw); + rtw89_phy_fill_limit_ru996_484_242_be(rtwdev, &lmt->ru996_484_242[i], + i, band, ch, bw); + } +} + +static +void rtw89_phy_conf_limit_large_mru_be(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx, + bool has_bf) +{ + struct rtw89_txpwr_limit_large_mru_be lmt_lmru; + u32 addr, val; + + rtw89_phy_fill_limit_large_mru_be(rtwdev, chan, &lmt_lmru, has_bf); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU484_242_0 : + R_BE_TXAGC_MAX_1TX_RU484_242_0; + + val = u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][2], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][3], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + + val = u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][2], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][3], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr + 4, val); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU996_484_0 : + R_BE_TXAGC_MAX_1TX_RU996_484_0; + + val = u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_2][0], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_2][1], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU996_484_242_0 : + R_BE_TXAGC_MAX_1TX_RU996_484_242_0; + + val = u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_2][0], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_2][1], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); +} + static void rtw89_phy_set_txpwr_limit_ru_be(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_txpwr_limit_ru_be lmt_ru; + struct rtw89_hal *hal = &rtwdev->hal; const s8 *ptr; u32 addr, val; u8 i, j; @@ -1499,12 +1887,22 @@ static void rtw89_phy_set_txpwr_limit_ru_be(struct rtw89_dev *rtwdev, rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); } } + + if (!(chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + return; + + rtw89_phy_conf_limit_large_mru_be(rtwdev, chan, phy_idx, false); + rtw89_phy_conf_limit_large_mru_be(rtwdev, chan, phy_idx, true); } const struct rtw89_phy_gen_def rtw89_phy_gen_be = { .cr_base = 0x20000, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START, .physt_bmp_eht = R_PHY_STS_BITMAP_EHT, + .physt_ie_len = {32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN, + VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 1, .ccx = &rtw89_ccx_regs_be, .physts = &rtw89_physts_regs_be, .cfo = &rtw89_cfo_regs_be, @@ -1526,6 +1924,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1 = { .cr_base = 0x0, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START_BE4, .physt_bmp_eht = R_PHY_STS_BITMAP_EHT_BE4, + .physt_ie_len = {32, 40, 24, 24, 16, 16, 16, 16, VAR_LEN, VAR_LEN, VAR_LEN, 168, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 32, 56, + 96, VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 2, .ccx = &rtw89_ccx_regs_be_v1, .physts = &rtw89_physts_regs_be_v1, .cfo = &rtw89_cfo_regs_be_v1, diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index 125cf14fa581..31bb5dcd284a 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -234,8 +234,14 @@ void rtw89_enter_ips(struct rtw89_dev *rtwdev) { struct rtw89_vif_link *rtwvif_link; struct rtw89_vif *rtwvif; + struct rtw89_hal *hal = &rtwdev->hal; unsigned int link_id; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_INACTIVE_PS)) { + rtw89_debug(rtwdev, RTW89_DBG_PS, "skip enter IPS due to disabled_dm\n"); + return; + } + set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags); if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 42ffe83931a3..086ef77ebb9f 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -164,6 +164,19 @@ #define R_AX_DBG_PORT_SEL 0x00C0 #define B_AX_DEBUG_ST_MASK GENMASK(31, 0) +#define R_AX_PAD_CTRL2 0x00C4 +#define B_AX_FORCE_U3_CK BIT(23) +#define B_AX_USB2_FORCE BIT(22) +#define B_AX_USB3_FORCE BIT(21) +#define B_AX_USB3_USB2_TRANSITION BIT(20) +#define B_AX_USB23_SW_MODE_V1 GENMASK(19, 18) +#define USB_MODE_U2 0x1 +#define USB_MODE_U3 0x2 +#define B_AX_NO_PDN_CHIPOFF_V1 BIT(17) +#define B_AX_RSM_EN_V1 BIT(16) +#define B_AX_MATCH_CNT GENMASK(15, 8) +#define USB_SWITCH_DELAY 0xF + #define R_AX_PMC_DBG_CTRL2 0x00CC #define B_AX_SYSON_DIS_PMCR_AX_WRMSK BIT(2) @@ -4235,6 +4248,20 @@ #define B_BE_TOGGLE BIT(31) #define B_BE_DATA_LINE_MASK GENMASK(30, 0) +#define R_BE_PAD_CTRL2 0x00C4 +#define B_BE_USB23_SW_MODE BIT(31) +#define B_BE_USB3_GEN_MODE BIT(30) +#define B_BE_USB3_LANE_MODE BIT(29) +#define B_BE_USB_AUTO_INSTALL_MASK BIT(28) +#define B_BE_FORCE_CLK_U2 BIT(25) +#define B_BE_FORCE_U2_CK BIT(24) +#define B_BE_FORCE_U3_CK BIT(23) +#define B_BE_USB2_FORCE BIT(22) +#define B_BE_USB3_FORCE BIT(21) +#define B_BE_NO_PDN_CHIPOFF_V1 BIT(17) +#define B_BE_RSM_EN_V1 BIT(16) +#define B_BE_MATCH_CNT GENMASK(15, 8) + #define R_BE_PMC_DBG_CTRL2 0x00CC #define B_BE_EFUSE_BURN_GNT_MASK GENMASK(31, 24) #define B_BE_DIS_IOWRAP_TIMEOUT BIT(16) @@ -4329,6 +4356,10 @@ #define R_BE_SECURE_BOOT_MALLOC_INFO 0x0184 +#define R_BE_FW_AUTO_CAL_DELAY 0x0188 +#define B_BE_WCPU_FW_DELAY_COUNT_VALID BIT(15) +#define B_BE_WCPU_FW_DELAY_COUNT_MASK GENMASK(14, 0) + #define R_BE_FWS0IMR 0x0190 #define B_BE_FS_HALT_H2C_INT_EN BIT(31) #define B_BE_FS_FSM_HIOE_TO_EVENT_INT_EN BIT(30) @@ -4806,6 +4837,10 @@ #define R_BE_LTR_LATENCY_IDX2_V1 0x361C #define R_BE_LTR_LATENCY_IDX3_V1 0x3620 +#define R_BE_USB2_WLAN_TRX_OPT_PAR2 0x41BC +#define B_BE_USB2_USBRX_RST BIT(1) +#define B_BE_USB2_USBTX_RST BIT(0) + #define R_BE_HCI_BUF_IMR 0x6018 #define B_BE_HCI_BUF_IMR_CLR 0xC0000303 #define B_BE_HCI_BUF_IMR_SET 0xC0000301 @@ -8354,6 +8389,11 @@ #define R_BE_PWR_LISTEN_PATH 0x11988 #define B_BE_PWR_LISTEN_PATH_EN GENMASK(31, 28) +#define R_BE_TXAGC_MAX_1TX_RU484_242_0 0x11990 +#define R_BE_TXAGC_MAX_1TX_RU996_484_0 0x119A4 +#define R_BE_TXAGC_MAX_1TX_RU996_484_242_0 0x119AC +#define R_BE_TXAGC_MAX_1TX_BF_RU484_242_0 0x119DC + #define R_BE_PWR_REF_CTRL 0x11A20 #define B_BE_PWR_REF_CTRL_OFDM GENMASK(9, 1) #define B_BE_PWR_REF_CTRL_CCK GENMASK(18, 10) @@ -8391,6 +8431,8 @@ B_BE_PWR_FORCE_MACID_EN_VAL | \ B_BE_PWR_FORCE_MACID_EN_ON) +#define R_BE_TXAGC_MAX_1TX_BF_RU996_484_0 0x11A4C + #define R_BE_PWR_REG_CTRL 0x11A50 #define B_BE_PWR_BT_EN BIT(23) @@ -8404,6 +8446,8 @@ #define R_BE_PWR_TH 0x11A78 #define R_BE_PWR_RSSI_TARGET_LMT 0x11A84 +#define R_BE_TXAGC_MAX_1TX_BF_RU996_484_242_0 0x11ADC + #define R_BE_PWR_OFST_SW 0x11AE8 #define B_BE_PWR_OFST_SW_DB GENMASK(27, 24) @@ -8462,6 +8506,8 @@ #define RR_MODOPT_V1 0x10001 #define RR_SW_SEL BIT(19) #define RR_TXG_SEL GENMASK(19, 17) +#define RR_TBL_SEL_IDX1 GENMASK(9, 8) +#define RR_TBL_SEL_IDX0 GENMASK(7, 6) #define RR_MODOPT_M_TXPWR GENMASK(5, 0) #define RR_WLSEL 0x02 #define RR_WLSEL_AG GENMASK(18, 16) @@ -8735,6 +8781,9 @@ #define B_DFS_FFT_EN BIT(0) #define R_CHINFO_DATA 0x00C0 #define B_CHINFO_DATA_BITMAP GENMASK(22, 0) +#define R_INTF_R_INTF_RPT_SEL 0x0200 +#define R_INTF_R_INTF_RPT_SEL_BE4 0x20200 +#define B_INTF_R_INTF_RPT_SEL BIT(12) #define R_ANAPAR_PW15 0x030C #define B_ANAPAR_PW15 GENMASK(31, 24) #define B_ANAPAR_PW15_H GENMASK(27, 24) @@ -8793,6 +8842,12 @@ #define R_P0_ANT_SW 0x0728 #define B_P0_HW_ANTSW_DIS_BY_GNT_BT BIT(12) #define B_P0_TRSW_TX_EXTEND GENMASK(3, 0) +#define R_RST_ALL_CNT 0x0730 +#define R_RST_ALL_CNT_BE4 0x20730 +#define B_RST_ALL_CNT BIT(0) +#define R_ENABLE_ALL_CNT 0x0730 +#define R_ENABLE_ALL_CNT_BE4 0x20730 +#define B_ENABLE_ALL_CNT BIT(1) #define R_MAC_PIN_SEL 0x0734 #define R_MAC_PIN_SEL_BE4 0x20734 #define B_CH_IDX_SEG0 GENMASK(23, 16) @@ -8943,6 +8998,8 @@ #define B_RXHT_MCS_LIMIT GENMASK(9, 8) #define R_RXVHT_MCS_LIMIT 0x0D18 #define B_RXVHT_MCS_LIMIT GENMASK(22, 21) +#define R_BRK_OPT 0x0D44 +#define B_BRK_OPT BIT(31) #define R_P0_EN_SOUND_WO_NDP 0x0D7C #define B_P0_EN_SOUND_WO_NDP BIT(1) #define R_RXHE 0x0D80 @@ -8998,6 +9055,21 @@ #define R_CFO_COMP_SEG0_L 0x1384 #define R_CFO_COMP_SEG0_H 0x1388 #define R_CFO_COMP_SEG0_CTRL 0x138C +#define R_CNT_CCKTXEN 0x1700 +#define R_CNT_CCKTXEN_V1 0x2E00 +#define R_CNT_CCKTXEN_BE4 0x2CF00 +#define B_CNT_CCKTXEN GENMASK(31, 16) +#define R_CNT_CCKTXON 0x1704 +#define R_CNT_CCKTXON_V1 0x2E04 +#define R_CNT_CCKTXON_BE4 0x2CF04 +#define B_CNT_CCKTXON GENMASK(15, 0) +#define R_CNT_CCK_CCA_P0 0x1710 +#define R_CNT_CCK_CCA_P0_V1 0x2E10 +#define B_CNT_CCK_CCA_P0 GENMASK(15, 0) +#define R_CNT_CCK_CRC32_P0 0x1714 +#define R_CNT_CCK_CRC32_P0_V1 0x2E14 +#define B_CNT_CCK_CRC32OK_P0 GENMASK(15, 0) +#define B_CNT_CCK_CRC32FAIL_P0 GENMASK(31, 16) #define R_DBG32_D 0x1730 #define R_EDCCA_RPT_A 0x1738 #define R_EDCCA_RPT_B 0x173c @@ -9013,7 +9085,87 @@ #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_INFO_0_0_COMB 0x1800 +#define R_TX_INFO_0_0_COMB_V1 0x3E00 +#define R_TX_INFO_0_1_COMB 0x1804 +#define R_TX_INFO_0_1_COMB_V1 0x3E04 +#define R_TX_INFO_1_0_COMB 0x1808 +#define R_TX_INFO_1_0_COMB_V1 0x3E08 +#define R_TX_INFO_1_1_COMB 0x180C +#define R_TX_INFO_1_1_COMB_V1 0x3E0C +#define R_TX_INFO_2_0_COMB_V1 0x3E10 +#define R_TX_INFO_2_1_COMB_V1 0x3E14 +#define R_TX_COMMON_CTRL_0_0_COMB 0x1810 +#define R_TX_COMMON_CTRL_0_0_COMB_V1 0x3E20 +#define R_TX_COMMON_CTRL_0_1_COMB 0x1814 +#define R_TX_COMMON_CTRL_0_1_COMB_V1 0x3E24 +#define R_CNT_LSIG_BRK_S_TH 0x1A00 +#define R_CNT_LSIG_BRK_S_TH_V1 0x0E00 +#define R_CNT_LSIG_BRK_S_TH_BE4 0x20E00 +#define B_CNT_LSIG_BRK_S_TH GENMASK(31, 16) +#define R_CNT_CCA_SPOOFING 0x1A00 +#define R_CNT_CCA_SPOOFING_V1 0x0E00 +#define R_CNT_CCA_SPOOFING_BE4 0x20E00 +#define B_CNT_CCA_SPOOFING GENMASK(15, 0) +#define R_CNT_LSIG_BRK_L_TH 0x1A04 +#define R_CNT_LSIG_BRK_L_TH_V1 0x0E04 +#define R_CNT_LSIG_BRK_L_TH_BE4 0x20E04 +#define B_CNT_LSIG_BRK_L_TH GENMASK(15, 0) +#define R_CNT_BRK 0x1A08 +#define R_CNT_BRK_V1 0x0E08 +#define R_CNT_BRK_BE4 0x20E08 +#define B_CNT_BRK GENMASK(31, 16) +#define R_CNT_RXL_ERR_PARITY 0x1A0C +#define R_CNT_RXL_ERR_PARITY_V1 0x0E0C +#define R_CNT_RXL_ERR_PARITY_BE4 0x20E0C +#define B_CNT_RXL_ERR_PARITY GENMASK(31, 16) +#define R_CNT_RXL_ERR_RATE 0x1A10 +#define R_CNT_RXL_ERR_RATE_V1 0x0E10 +#define R_CNT_RXL_ERR_RATE_BE4 0x20E10 +#define B_CNT_RXL_ERR_RATE GENMASK(15, 0) +#define R_CNT_SEARCH_FAIL 0x1A20 +#define R_CNT_SEARCH_FAIL_V1 0x0E20 +#define R_CNT_SEARCH_FAIL_BE4 0x20E20 +#define B_CNT_SEARCH_FAIL GENMASK(31, 16) +#define R_CNT_OFDM_CCA 0x1A24 +#define R_CNT_OFDM_CCA_V1 0x0E24 +#define R_CNT_OFDM_CCA_BE4 0x20E24 +#define B_CNT_OFDM_CCA GENMASK(15, 0) #define R_TX_COUNTER 0x1A40 +#define R_CNT_OFDMTXON 0x1A40 +#define R_CNT_OFDMTXON_V1 0x0E40 +#define R_CNT_OFDMTXON_BE4 0x20E40 +#define B_CNT_OFDMTXON GENMASK(15, 0) +#define B_CNT_OFDMTXEN GENMASK(31, 16) +#define R_CNT_HE_CRC 0x1A58 +#define R_CNT_HE_CRC_V1 0x0E58 +#define R_CNT_HE_CRC_BE4 0x20E58 +#define B_CNT_HE_CRC_OK GENMASK(15, 0) +#define B_CNT_HE_CRC_ERR GENMASK(31, 16) +#define R_CNT_VHT_CRC 0x1A5C +#define R_CNT_VHT_CRC_V1 0x0E5C +#define R_CNT_VHT_CRC_BE4 0x20E5C +#define B_CNT_VHT_CRC_OK GENMASK(15, 0) +#define B_CNT_VHT_CRC_ERR GENMASK(31, 16) +#define R_CNT_HT_CRC 0x1A60 +#define R_CNT_HT_CRC_V1 0x0E60 +#define R_CNT_HT_CRC_BE4 0x20E60 +#define B_CNT_HT_CRC_OK GENMASK(15, 0) +#define B_CNT_HT_CRC_ERR GENMASK(31, 16) +#define R_CNT_L_CRC 0x1A64 +#define R_CNT_L_CRC_V1 0x0E64 +#define R_CNT_L_CRC_BE4 0x20E64 +#define B_CNT_L_CRC_OK GENMASK(15, 0) +#define B_CNT_L_CRC_ERR GENMASK(31, 16) +#define R_CNT_AMPDU_MISS 0x1A7C +#define R_CNT_AMPDU_MISS_V1 0x0E7C +#define R_CNT_AMPDU_MISS_BE4 0x20E7C +#define B_CNT_AMPDU_MISS GENMASK(31, 16) +#define R_CNT_AMPDU_RX_CRC32 0x1A80 +#define R_CNT_AMPDU_RX_CRC32_V1 0x0E80 +#define R_CNT_AMPDU_RX_CRC32_BE4 0x20E80 +#define B_CNT_AMPDU_RX_CRC32_OK GENMASK(15, 0) +#define B_CNT_AMPDU_RX_CRC32_ERR GENMASK(31, 16) #define R_NHM_CNT0 0x1A88 #define B_NHM_CNT0_MSK GENMASK(15, 0) #define B_NHM_CNT1_MSK GENMASK(31, 16) @@ -9034,6 +9186,14 @@ #define B_NHM_CNT11_MSK GENMASK(31, 16) #define R_NHM_AX 0x1AA4 #define B_NHM_READY_MSK BIT(16) +#define R_CLM_EDCCA_RESULT 0x1AC8 +#define R_CLM_EDCCA_RESULT_V1 0x0EC8 +#define R_CLM_EDCCA_RESULT_BE4 0x20EC8 +#define B_CLM_EDCCA_RESULT GENMASK(15, 0) +#define R_CLM_EDCCA_RDY 0x1AC8 +#define R_CLM_EDCCA_RDY_V1 0x0EC8 +#define R_CLM_EDCCA_RDY_BE4 0x20EC8 +#define B_CLM_EDCCA_RDY BIT(16) #define R_IFS_CLM_TX_CNT 0x1ACC #define R_IFS_CLM_TX_CNT_V1 0x0ECC #define R_IFS_CLM_TX_CNT_BE4 0x20ECC @@ -9089,6 +9249,8 @@ #define B_TXAGC_BB GENMASK(31, 24) #define B_TXAGC_RF GENMASK(5, 0) #define R_PATH0_TXPWR 0x1C78 +#define R_PATH0_TXPWR_V1 0xEE0C +#define R_PATH0_TXPWR_BE4 0x2F90C #define B_PATH0_TXPWR GENMASK(8, 0) #define R_S0_ADDCK 0x1E00 #define B_S0_ADDCK_I GENMASK(9, 0) @@ -9131,12 +9293,41 @@ #define B_RXCCA_DIS_V1 BIT(0) #define R_RXSC 0x237C #define B_RXSC_EN BIT(0) +#define R_R1B_RR_SEL 0x2388 +#define B_R1B_RR_SEL GENMASK(24, 23) +#define R_R1B_RX_RPT_RST 0x2388 +#define R_R1B_RX_RPT_RST_V1 0x2340 +#define R_R1B_RX_RPT_RST_BE 0x0540 +#define R_R1B_RX_RPT_RST_BE4 0x20540 +#define B_R1B_RX_RPT_RST BIT(15) +#define B_R1B_RX_RPT_RST_V1 BIT(7) +#define R_BRK_CNT 0x239C +#define R_BRK_CNT_V1 0x059C +#define R_BRK_CNT_BE4 0x2059C +#define B_BRK_CNT GENMASK(31, 16) #define R_RX_RPL_OFST 0x23AC #define B_RX_RPL_OFST_CCK_MASK GENMASK(6, 0) #define R_RXSCOBC 0x23B0 #define B_RXSCOBC_TH GENMASK(18, 0) #define R_RXSCOCCK 0x23B4 #define B_RXSCOCCK_TH GENMASK(18, 0) +#define R_SFD_GG_CNT 0x23E0 +#define R_SFD_GG_CNT_V1 0x23A0 +#define R_SFD_GG_CNT_V2 0x05A0 +#define R_SFD_GG_CNT_BE4 0x205A0 +#define B_SFD_GG_CNT GENMASK(15, 0) +#define R_SIG_GG_CNT 0x23E8 +#define R_SIG_GG_CNT_V1 0x23AC +#define R_SIG_GG_CNT_V2 0x05AC +#define R_SIG_GG_CNT_BE4 0x205AC +#define B_SIG_GG_CNT GENMASK(15, 0) +#define B_SIG_GG_CNT_V1 GENMASK(15, 8) +#define R_SPOOF_CNT 0x23EC +#define R_SPOOF_CNT_V1 0x23A8 +#define R_SPOOF_CNT_V2 0x05A8 +#define R_SPOOF_CNT_BE4 0x205A8 +#define B_SPOOF_CNT GENMASK(7, 0) +#define B_SPOOF_CNT_V1 GENMASK(31, 16) #define R_P80_AT_HIGH_FREQ_RU_ALLOC 0x2410 #define B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY1 BIT(14) #define B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY0 BIT(13) @@ -9173,6 +9364,10 @@ #define R_EDCCA_RPT_B_BE4_C1 0x2FE34 #define R_EDCCA_RPT_P1_A_BE 0x2E40 #define R_EDCCA_RPT_P1_B_BE 0x2E44 +#define R_CNT_EHT_CRC 0x2F00 +#define R_CNT_EHT_CRC_BE4 0x22F00 +#define B_CNT_EHT_CRC_OK GENMASK(15, 0) +#define B_CNT_EHT_CRC_ERR GENMASK(31, 16) #define R_S1_HW_SI_DIS 0x3200 #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) #define R_P1_RXCK 0x32A0 @@ -9200,6 +9395,8 @@ #define B_TXAGC_BB_S1_OFT GENMASK(31, 16) #define B_TXAGC_BB_S1 GENMASK(31, 24) #define R_PATH1_TXPWR 0x3C78 +#define R_PATH1_TXPWR_V1 0xEF0C +#define R_PATH1_TXPWR_BE4 0x2FA0C #define B_PATH1_TXPWR GENMASK(8, 0) #define R_S1_ADDCK 0x3E00 #define B_S1_ADDCK_I GENMASK(9, 0) @@ -10439,9 +10636,25 @@ #define B_FBTB_CT_MUMIMO_BE4 GENMASK(19, 16) #define B_FBTB_CT_FTM_BE4 GENMASK(23, 20) #define B_FBTB_CT_SENS_BE4 GENMASK(27, 24) +#define R_CIM3K_SU_FORCE 0x119F8 +#define B_CIM3K_SU_FORCE_EN BIT(17) +#define B_CIM3K_SU_FORCE_VAL BIT(18) +#define R_QAM3_TH0_BE4 0x119FC +#define B_QAM3_TH0_0_BE4 GENMASK(2, 0) +#define B_QAM3_TH0_1_BE4 GENMASK(5, 3) +#define B_QAM3_TH0_2_BE4 GENMASK(8, 6) +#define B_QAM3_TH0_3_BE4 GENMASK(11, 9) +#define B_QAM3_TH0_4_BE4 GENMASK(14, 12) +#define B_QAM3_TH0_5_BE4 GENMASK(17, 15) +#define B_QAM3_TH0_6_BE4 GENMASK(20, 18) +#define B_QAM3_TH0_7_BE4 GENMASK(23, 21) +#define B_QAM3_TH1_0_BE4 GENMASK(26, 24) +#define B_QAM3_TH1_1_BE4 GENMASK(29, 27) #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C +#define B_QAM_COMP_TH_TRIANGULAR_L GENMASK(11, 10) +#define B_QAM_COMP_TH_TRIANGULAR_H GENMASK(27, 26) #define R_QAM_COMP_TH1_BE4 0x11AA0 #define R_QAM_COMP_TH2_BE4 0x11AA4 #define R_QAM_COMP_TH3_BE4 0x11AA8 @@ -10465,6 +10678,8 @@ #define B_QAM_COMP_TH6_2L GENMASK(9, 5) #define B_QAM_COMP_TH6_2M GENMASK(19, 15) #define R_OW_VAL_0_BE4 0x11AAC +#define B_OW_VAL_TRIANGULAR_L GENMASK(11, 10) +#define B_OW_VAL_TRIANGULAR_H GENMASK(27, 26) #define R_OW_VAL_1_BE4 0x11AB0 #define R_OW_VAL_2_BE4 0x11AB4 #define R_OW_VAL_3_BE4 0x11AB8 @@ -10474,6 +10689,65 @@ #define B_BANDEDGE_DBW80_BE4 BIT(31) #define R_BANDEDGE_DBWY_BE4 0x11AD0 #define B_BANDEDGE_DBW160_BE4 BIT(0) +#define R_QAM3_TH1_BE4 0x11BE8 +#define B_QAM3_TH1_2_BE4 GENMASK(2, 0) +#define B_QAM3_TH1_3_BE4 GENMASK(5, 3) +#define B_QAM3_TH1_4_BE4 GENMASK(8, 6) +#define B_QAM3_TH1_5_BE4 GENMASK(11, 9) +#define B_QAM3_TH1_6_BE4 GENMASK(14, 12) +#define B_QAM3_TH1_7_BE4 GENMASK(17, 15) +#define B_QAM3_TH2_0_BE4 GENMASK(20, 18) +#define B_QAM3_TH2_1_BE4 GENMASK(23, 21) +#define B_QAM3_TH2_2_BE4 GENMASK(26, 24) +#define B_QAM3_TH2_3_BE4 GENMASK(29, 27) +#define R_QAM3_TH2_BE4 0x11BEC +#define B_QAM3_TH2_4_BE4 GENMASK(2, 0) +#define B_QAM3_TH2_5_BE4 GENMASK(5, 3) +#define B_QAM3_TH2_6_BE4 GENMASK(8, 6) +#define B_QAM3_TH2_7_BE4 GENMASK(11, 9) +#define B_QAM3_TH3_0_BE4 GENMASK(14, 12) +#define B_QAM3_TH3_1_BE4 GENMASK(17, 15) +#define B_QAM3_TH3_2_BE4 GENMASK(20, 18) +#define B_QAM3_TH3_3_BE4 GENMASK(23, 21) +#define B_QAM3_TH3_4_BE4 GENMASK(26, 24) +#define B_QAM3_TH3_5_BE4 GENMASK(29, 27) +#define R_QAM3_TH3_BE4 0x11BF4 +#define B_QAM3_TH3_6_BE4 GENMASK(2, 0) +#define B_QAM3_TH3_7_BE4 GENMASK(5, 3) +#define B_QAM3_TH5_0_BE4 GENMASK(8, 6) +#define B_QAM3_TH5_1_BE4 GENMASK(11, 9) +#define B_QAM3_TH5_2_BE4 GENMASK(14, 12) +#define B_QAM3_TH5_3_BE4 GENMASK(17, 15) +#define B_QAM3_TH5_4_BE4 GENMASK(20, 18) +#define B_QAM3_TH5_5_BE4 GENMASK(23, 21) +#define B_QAM3_TH5_6_BE4 GENMASK(26, 24) +#define B_QAM3_TH5_7_BE4 GENMASK(29, 27) +#define R_QAM3_TH4_BE4 0x11BF8 +#define B_QAM3_TH4_0_BE4 GENMASK(2, 0) +#define B_QAM3_TH4_1_BE4 GENMASK(5, 3) +#define B_QAM3_TH4_2_BE4 GENMASK(8, 6) +#define B_QAM3_TH4_3_BE4 GENMASK(11, 9) +#define B_QAM3_TH4_4_BE4 GENMASK(14, 12) +#define B_QAM3_TH4_5_BE4 GENMASK(17, 15) +#define B_QAM3_TH4_6_BE4 GENMASK(20, 18) +#define B_QAM3_TH4_7_BE4 GENMASK(23, 21) +#define B_QAM3_CFIR0_BE4 BIT(24) +#define B_QAM3_CFIR1_BE4 BIT(25) +#define B_QAM3_CFIR2_BE4 BIT(26) +#define B_QAM3_CFIR3_BE4 BIT(27) +#define B_QAM3_CFIR4_BE4 BIT(28) +#define B_QAM3_CFIR5_BE4 BIT(29) +#define B_QAM3_CFIR6_BE4 BIT(30) +#define B_QAM3_CFIR7_BE4 BIT(31) +#define R_QAM3_FLTR_BE4 0x11BFC +#define B_QAM3_FLTR0_BE4 GENMASK(1, 0) +#define B_QAM3_FLTR1_BE4 GENMASK(3, 2) +#define B_QAM3_FLTR2_BE4 GENMASK(5, 4) +#define B_QAM3_FLTR3_BE4 GENMASK(7, 6) +#define B_QAM3_FLTR4_BE4 GENMASK(9, 8) +#define B_QAM3_FLTR5_BE4 GENMASK(11, 10) +#define B_QAM3_FLTR6_BE4 GENMASK(13, 12) +#define B_QAM3_FLTR7_BE4 GENMASK(15, 14) #define R_SYS_DBCC_BE4 0x20000 #define B_SYS_DBCC_BE4 BIT(0) @@ -10541,6 +10815,11 @@ #define R_IFS_T3_HIS_BE4 0x20F54 #define B_IFS_T3_HIS_BE4 GENMASK(15, 0) #define B_IFS_T4_HIS_BE4 GENMASK(31, 16) +#define R_CNT_CCK_CCA_BE4 0x20FE8 +#define B_CNT_CCK_CCA_BE4 GENMASK(15, 0) +#define R_CNT_CCK_CRC32_BE4 0x20FEC +#define B_CNT_CCK_CRC32OK_BE4 GENMASK(15, 0) +#define B_CNT_CCK_CRC32FAIL_BE4 GENMASK(31, 16) #define R_TX_ERROR_SEL_BE4 0x21254 #define B_TX_ERROR_PSDU_BE4 BIT(11) @@ -10680,6 +10959,14 @@ #define B_SW_SI_R_BUSY_BE4 BIT(25) #define B_SW_SI_READ_DATA_DONE_BE4 BIT(26) +#define R_TX_INFO_0_0_COMB_BE4 0x2DF00 +#define R_TX_INFO_0_1_COMB_BE4 0x2DF04 +#define R_TX_INFO_1_0_COMB_BE4 0x2DF08 +#define R_TX_INFO_1_1_COMB_BE4 0x2DF0C +#define R_TX_INFO_2_0_COMB_BE4 0x2DF10 +#define R_TX_INFO_2_1_COMB_BE4 0x2DF14 +#define R_TX_COMMON_CTRL_0_0_COMB_BE4 0x2DF20 +#define R_TX_COMMON_CTRL_0_1_COMB_BE4 0x2DF24 #define R_RX_PATH0_TBL0_BE4 0x2E028 #define R_RX_PATH1_TBL0_BE4 0x2E128 @@ -10687,6 +10974,8 @@ #define R_KTBL0B_BE4 0x38204 #define B_KTBL0_IDX0 GENMASK(1, 0) #define B_KTBL0_IDX1 GENMASK(9, 8) +#define B_KTBL0_MLD_IDX0 GENMASK(25, 24) +#define B_KTBL0_MLD_IDX1 GENMASK(27, 26) #define B_KTBL0_RST BIT(31) #define R_KTBL1A_BE4 0x38154 #define R_KTBL1B_BE4 0x38254 diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 28466cb35ea2..14fab6b1a74a 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -360,6 +360,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_chip_info *chip = rtwdev->chip; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -377,6 +378,9 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, return; } + if (skip_acpi_dsm) + return; + bitmap_fill(regulatory->block_unii4, RTW89_REGD_MAX_COUNTRY_NUM); ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_UNII4_SUP, &res); @@ -426,6 +430,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block, static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_country_code *country; const struct rtw89_acpi_policy_6ghz *ptr; struct rtw89_acpi_dsm_result res = {}; @@ -433,6 +438,9 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) int i, j; int ret; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_BP, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -478,12 +486,16 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_6ghz_sp *ptr; struct rtw89_acpi_dsm_result res = {}; bool enable; u8 index; int ret; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -524,6 +536,7 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_6ghz_vlp *ptr = NULL; struct rtw89_acpi_dsm_result res = {}; bool enable; @@ -531,6 +544,9 @@ static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) int ret; u8 val; + if (skip_acpi_dsm) + return; + /* By default, allow 6 GHz VLP on all countries except US and CA. */ val = ~(RTW89_ACPI_CONF_6GHZ_VLP_US | RTW89_ACPI_CONF_6GHZ_VLP_CA); @@ -574,6 +590,7 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { const struct rtw89_chip_info *chip = rtwdev->chip; bool chip_support_6ghz = chip->support_bands & BIT(NL80211_BAND_6GHZ); + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; bool regd_allow_6ghz = chip_support_6ghz; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -583,6 +600,9 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy) if (!chip_support_6ghz) goto bottom; + if (skip_acpi_dsm) + goto bottom; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_DIS, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -661,10 +681,14 @@ const char *rtw89_regd_get_string(enum rtw89_regulation_type regd) static void rtw89_regd_setup_reg_rules(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_reg_rules *ptr; struct rtw89_acpi_dsm_result res = {}; int ret; + if (skip_acpi_dsm) + return; + regulatory->txpwr_uk_follow_etsi = true; ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_REG_RULES_EN, &res); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 84bdd39b3ceb..60f362593696 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -307,6 +307,54 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8851b_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8851b_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -2508,6 +2556,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .read_efuse = rtw8851b_read_efuse, .read_phycap = rtw8851b_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = rtw8851b_rfe_gpio, .rfk_hw_init = NULL, .rfk_init = rtw8851b_rfk_init, @@ -2599,13 +2648,16 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, - .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, + rtw8851b_hfc_param_ini_usb, rtw8851b_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8851b_dle_mem_pcie, + .dle_mem = {rtw8851b_dle_mem_pcie, rtw8851b_dle_mem_usb2, rtw8851b_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000}, @@ -2639,6 +2691,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .support_tas = false, .support_sar_by_ant = false, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, @@ -2720,6 +2773,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8851b_edcca_regs, + .pmac_regs = &rtw8851b_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8851b, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index ce59ac9f56ba..640672eb0d26 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8851be_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8851b_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 6a8d31544314..34ba5661d771 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -31,6 +31,7 @@ static const struct rtw89_driver_info rtw89_8851bu_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8851b_usb_info, } @@ -44,6 +45,9 @@ static const struct usb_device_id rtw_8851bu_id_table[] = { /* D-Link AX9U rev. A1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332a, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, + /* Mercusys MA60XNB */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2C4E, 0x0128, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, /* TP-Link Archer TX10UB Nano */ { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010b, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 1d4f1df524a1..2c1f166e687f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -621,6 +621,54 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852a_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static void rtw8852a_efuse_parsing_tssi(struct rtw89_dev *rtwdev, struct rtw8852a_efuse *map) { @@ -2158,13 +2206,14 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct ieee80211_rx_status *status) { - u8 path; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, phy_ppdu->phy_idx); u8 *rx_power = phy_ppdu->rssi; + u8 path; u8 raw; if (!status->signal) { if (phy_ppdu->to_self) - raw = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + raw = ewma_rssi_read(&bb->bcn_rssi); else raw = max(rx_power[RF_PATH_A], rx_power[RF_PATH_B]); @@ -2253,6 +2302,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .read_efuse = rtw8852a_read_efuse, .read_phycap = rtw8852a_read_phycap, .fem_setup = rtw8852a_fem_setup, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852a_rfk_init, @@ -2335,13 +2385,16 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, + rtw8852a_hfc_param_ini_usb, rtw8852a_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852a_dle_mem_pcie, + .dle_mem = {rtw8852a_dle_mem_pcie, rtw8852a_dle_mem_usb, rtw8852a_dle_mem_usb, NULL}, + }, .wde_qempty_acq_grpnum = 16, .wde_qempty_mgq_grpsel = 16, .rf_base_addr = {0xc000, 0xd000}, @@ -2376,6 +2429,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_tas = false, .support_sar_by_ant = false, .support_noise = true, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, @@ -2456,6 +2510,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .btc_sb = {{{R_AX_SCOREBOARD, R_AX_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8852a_edcca_regs, + .pmac_regs = &rtw8852a_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852a, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index 9e05e831569d..64306cdc1ee4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -71,6 +71,7 @@ static const struct rtw89_driver_info rtw89_8852ae_info = { .chip = &rtw8852a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852a_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c index 4cced4619b7d..29b7f7769370 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c @@ -33,6 +33,7 @@ static const struct rtw89_driver_info rtw89_8852au_info = { .chip = &rtw8852a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852a_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 5e8738bb2dc2..4e7b068aaa75 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -259,6 +259,54 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852b_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -835,6 +883,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .read_efuse = rtw8852bx_read_efuse, .read_phycap = rtw8852bx_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852b_rfk_init, @@ -930,13 +979,16 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, - .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, + rtw8852b_hfc_param_ini_usb, rtw8852b_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852b_dle_mem_pcie, + .dle_mem = {rtw8852b_dle_mem_pcie, rtw8852b_dle_mem_usb3, rtw8852b_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, @@ -972,6 +1024,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, @@ -1054,6 +1107,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8852b_edcca_regs, + .pmac_regs = &rtw8852b_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852b, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 70b1515c00fa..0d443edfe259 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -4167,11 +4167,15 @@ void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852b_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u8 idx; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[0]; + for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { struct rtw89_rfk_chan_desc *p = &desc[idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 12db0d0be547..5bc0a6a99d1d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8852be_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852b_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index ab4263bc8b9f..7fcc877f6ea0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -202,6 +202,54 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852bt_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8852bt_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -681,6 +729,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .read_efuse = rtw8852bx_read_efuse, .read_phycap = rtw8852bx_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852bt_rfk_init, @@ -776,8 +825,10 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL}, - .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, @@ -811,6 +862,8 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_ant_gain = true, .support_tas = false, .support_sar_by_ant = true, + .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, @@ -891,6 +944,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8852bt_edcca_regs, + .pmac_regs = &rtw8852bt_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852bt, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c index 961b26ba2d3c..433807e712e4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c @@ -4234,11 +4234,15 @@ void rtw8852bt_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852bt_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u8 idx; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[0]; + for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { struct rtw89_rfk_chan_desc *p = &desc[idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c index 8c995aa95325..49a72ca835ac 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c @@ -79,6 +79,7 @@ static const struct rtw89_driver_info rtw89_8852bte_info = { .chip = &rtw8852bt_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852bt_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 37111fed276f..308d3d570ff3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -31,6 +31,7 @@ static const struct rtw89_driver_info rtw89_8852bu_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852b_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 40db7e3c0d97..3861cce42b1b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -270,6 +270,54 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852c_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT_V1, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_V1, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_V1, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT, B_BRK_CNT}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {R_BRK_OPT, B_BRK_OPT}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_V1, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static void rtw8852c_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx); @@ -573,6 +621,15 @@ static void rtw8852c_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev, gain->offset_valid = valid; } +static void rtw8852c_efuse_copy_sn_uuid_usb(struct rtw89_dev *rtwdev, + const struct rtw8852c_efuse *map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + + memcpy(efuse->sn, map->u.sn, sizeof(efuse->sn)); + memcpy(efuse->uuid, map->u.uuid, sizeof(efuse->uuid)); +} + static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, enum rtw89_efuse_block block) { @@ -592,6 +649,7 @@ static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, break; case RTW89_HCI_TYPE_USB: ether_addr_copy(efuse->addr, map->u.mac_addr); + rtw8852c_efuse_copy_sn_uuid_usb(rtwdev, map); break; default: return -ENOTSUPP; @@ -3043,6 +3101,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .read_efuse = rtw8852c_read_efuse, .read_phycap = rtw8852c_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852c_rfk_init, @@ -3125,13 +3184,16 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, + rtw8852c_hfc_param_ini_usb, rtw8852c_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852c_dle_mem_pcie, + .dle_mem = {rtw8852c_dle_mem_pcie, rtw8852c_dle_mem_usb2, rtw8852c_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 16, .wde_qempty_mgq_grpsel = 16, .rf_base_addr = {0xe000, 0xf000}, @@ -3169,6 +3231,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_tas = true, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, .rx_freq_from_ie = false, @@ -3250,6 +3313,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .btc_sb = {{{R_AX_SCOREBOARD, R_AX_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8852c_edcca_regs, + .pmac_regs = &rtw8852c_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852c, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h index 8585921ac6c4..b1d7c354c18e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h @@ -13,7 +13,11 @@ struct rtw8852c_u_efuse { u8 rsvd[0x88]; u8 mac_addr[ETH_ALEN]; -}; + u8 rsvd1[8]; + u8 sn[RTW89_EFUSE_SN_LEN]; + u8 rsvd2[29]; + u8 uuid[RTW89_EFUSE_UUID_LEN]; +} __packed; struct rtw8852c_e_efuse { u8 mac_addr[ETH_ALEN]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index 150fed189414..3c64c0539205 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -102,6 +102,7 @@ static const struct rtw89_driver_info rtw89_8852ce_info = { .chip = &rtw8852c_chip_info, .variant = NULL, .quirks = rtw8852c_pci_quirks, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852c_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 092d2812a4d5..81ee96b0a048 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -33,6 +33,17 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { .chip = &rtw8852c_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, + .bus = { + .usb = &rtw8852c_usb_info, + }, +}; + +static const struct rtw89_driver_info rtw89_8852cu_valve_info = { + .chip = &rtw8852c_chip_info, + .variant = NULL, + .quirks = NULL, + .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS), .bus = { .usb = &rtw8852c_usb_info, }, @@ -52,6 +63,8 @@ static const struct usb_device_id rtw_8852cu_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_valve_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 8f6cf64271e8..ad3618dfd57d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -57,6 +57,126 @@ static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_pcie[] = { [RTW89_QTA_INVALID] = {NULL}, }; +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch7[] = { + {54, 222, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 222, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 222, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {54, 222, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {54, 222, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 222, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p7 = { + 492, /* Group 0 */ + 0, /* Group 1 */ + 492, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch8[] = { + {24, 196, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 226, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 196, grp_1}, /* ACH 4 */ + {0, 0, grp_1}, /* ACH 5 */ + {54, 196, grp_1}, /* ACH 6 */ + {0, 0, grp_1}, /* ACH 7 */ + {54, 226, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 196, grp_1}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p8 = { + 304, /* Group 0 */ + 304, /* Group 1 */ + 608, /* Public Max */ + 96, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_usb2[] = { + [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_ch7, &rtw8922a_hfc_pubcfg_p7, + &rtw89_mac_size.hfc_prec_cfg_c5, RTW89_HCIFC_STF}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_ch8, &rtw8922a_hfc_pubcfg_p8, + &rtw89_mac_size.hfc_prec_cfg_c6, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch4[] = { + {54, 606, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 606, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 606, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {54, 606, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {54, 606, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 606, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p4 = { + 876, /* Group 0 */ + 0, /* Group 1 */ + 876, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch5[] = { + {54, 311, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 311, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 311, grp_1}, /* ACH 4 */ + {0, 0, grp_1}, /* ACH 5 */ + {54, 311, grp_1}, /* ACH 6 */ + {0, 0, grp_1}, /* ACH 7 */ + {54, 311, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 311, grp_1}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p5 = { + 419, /* Group 0 */ + 419, /* Group 1 */ + 838, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_usb3[] = { + [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_ch4, &rtw8922a_hfc_pubcfg_p4, + &rtw89_mac_size.hfc_prec_cfg_c3, RTW89_HCIFC_STF}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_ch5, &rtw8922a_hfc_pubcfg_p5, + &rtw89_mac_size.hfc_prec_cfg_c3, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size0_v1, &rtw89_mac_size.ple_size0_v1, &rtw89_mac_size.wde_qt0_v1, @@ -68,7 +188,47 @@ static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { &rtw89_mac_size.wde_qt0_v1, &rtw89_mac_size.ple_qt0, &rtw89_mac_size.ple_qt1, &rtw89_mac_size.ple_rsvd_qt0, &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, - [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4_v1, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, + &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, + &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8922a_dle_mem_usb2[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size7_v1, + &rtw89_mac_size.ple_size6_v1, &rtw89_mac_size.wde_qt7_v1, + &rtw89_mac_size.wde_qt7_v1, &rtw89_mac_size.ple_qt12_v1, + &rtw89_mac_size.ple_qt13_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size5, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size8_v1, + &rtw89_mac_size.ple_size7_v1, &rtw89_mac_size.wde_qt8_v1, + &rtw89_mac_size.wde_qt8_v1, &rtw89_mac_size.ple_qt14_v1, + &rtw89_mac_size.ple_qt15_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, + &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, + &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8922a_dle_mem_usb3[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size4_v1, + &rtw89_mac_size.ple_size4_v1, &rtw89_mac_size.wde_qt4_v1, + &rtw89_mac_size.wde_qt4_v1, &rtw89_mac_size.ple_qt6_v1, + &rtw89_mac_size.ple_qt7_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size3, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size5_v1, + &rtw89_mac_size.ple_size4_v1, &rtw89_mac_size.wde_qt5_v1, + &rtw89_mac_size.wde_qt5_v1, &rtw89_mac_size.ple_qt8_v1, + &rtw89_mac_size.ple_qt9_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size3, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, @@ -223,6 +383,54 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, }; +static const struct rtw89_pmac_regs rtw8922a_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON_V1, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN_V1, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0_V1, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT_V2, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_V2, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_V2, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT_V1, B_BRK_CNT}, + .brk = {R_CNT_BRK_V1, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL_V1, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH_V1, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH_V1, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY_V1, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE_V1, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA_V1, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING_V1, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS_V1, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_BE, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0_V1, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON_V1, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC_V1, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC_V1, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC_V1, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC_V1, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = R_CNT_EHT_CRC, + .eht_crc_ok_mask = B_CNT_EHT_CRC_OK, + .eht_crc_err_mask = B_CNT_EHT_CRC_ERR, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32_V1, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_efuse_block_cfg rtw8922a_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, @@ -298,17 +506,24 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_BE_FEN_RST_ENABLE, B_BE_R_SYM_ISO_ADDA_P02PP | B_BE_R_SYM_ISO_ADDA_P12PP); rtw89_write8_set(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_PLATFORM_EN); - rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_HAXIDMA_IO_ST, - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + val32 & B_BE_HAXIDMA_IO_ST, + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + } rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); @@ -317,7 +532,9 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, B_BE_PCIE_FORCE_IBX_EN); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, + B_BE_PCIE_FORCE_IBX_EN); ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_PLL, 0x02, 0x02); if (ret) @@ -371,6 +588,10 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_SRAM_CTRL, 0, XTAL_SI_SRAM_DIS); + if (ret) + return ret; + if (hal->cv != CHIP_CAV) { rtw89_write32_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); rtw89_write32_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE); @@ -382,14 +603,16 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); } - rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, - B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | B_BE_MPDU_PROC_EN | - B_BE_WD_RLS_EN | B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | - B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | B_BE_PKT_BUF_EN | - B_BE_DMAC_TBL_EN | B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | - B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | B_BE_MAC_SEC_EN | - B_BE_H_AXIDMA_EN | B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | - B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN | B_BE_LTR_CTL_EN); + val32 = B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | B_BE_MPDU_PROC_EN | + B_BE_WD_RLS_EN | B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | + B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | B_BE_PKT_BUF_EN | + B_BE_DMAC_TBL_EN | B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | + B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | B_BE_MAC_SEC_EN | + B_BE_H_AXIDMA_EN | B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | + B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN; + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + val32 |= B_BE_LTR_CTL_EN; + rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, val32); set_bit(RTW89_FLAG_DMAC_FUNC, rtwdev->flags); @@ -465,17 +688,23 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_IO_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_IO_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + } rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); @@ -491,9 +720,22 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); + else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_SOP_EASWR); + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE); - rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + } else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) { + val32 = rtw89_read32(rtwdev, R_BE_SYS_PW_CTRL); + val32 |= B_BE_AFSM_WLSUS_EN; + val32 &= ~B_BE_AFSM_PCIE_SUS_EN; + rtw89_write32(rtwdev, R_BE_SYS_PW_CTRL, val32); + } + rtw89_write32(rtwdev, R_BE_UDM1, 0); return 0; @@ -1860,7 +2102,7 @@ static void rtw8922a_digital_pwr_comp(struct rtw89_dev *rtwdev, static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; if (mode == MLO_1_PLUS_1_1RF || mode == DBCC_LEGACY) { rtw89_phy_write32_mask(rtwdev, R_DBCC, B_DBCC_EN, 0x1); @@ -1873,19 +2115,10 @@ static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode return -EOPNOTSUPP; } - if (mode == MLO_1_PLUS_1_1RF) { - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - } else if (mode == MLO_0_PLUS_2_1RF) { - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - } else { - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - } + rtw89_entity_get_conf(rtwdev, &conf); - rtw8922a_ctrl_afe_dac(rtwdev, chan0->band_width, RF_PATH_A); - rtw8922a_ctrl_afe_dac(rtwdev, chan1->band_width, RF_PATH_B); + rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[0]->band_width, RF_PATH_A); + rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[1]->band_width, RF_PATH_B); rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0x6180); @@ -2853,6 +3086,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .read_efuse = rtw8922a_read_efuse, .read_phycap = rtw8922a_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = rtw8922a_rfk_hw_init, .rfk_init = rtw8922a_rfk_init, @@ -2935,8 +3169,16 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL}, - .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, + rtw8922a_hfc_param_ini_usb2, + rtw8922a_hfc_param_ini_usb3, + NULL}, + .dle_mem = {rtw8922a_dle_mem_pcie, + rtw8922a_dle_mem_usb2, + rtw8922a_dle_mem_usb3, + NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, @@ -2973,6 +3215,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_tas = true, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = false, @@ -3053,6 +3296,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .btc_sb = {{{R_BE_SCOREBOARD, R_BE_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8922a_edcca_regs, + .pmac_regs = &rtw8922a_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8922a, #endif @@ -3065,6 +3309,7 @@ const struct rtw89_chip_variant rtw8922ae_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 35, 54, 0), .fw_def_override = NULL, + .qta_def_override = NULL, }; EXPORT_SYMBOL(rtw8922ae_vs_variant); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index 98f14b31cf52..1e2b563f2bca 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -278,27 +278,13 @@ static u8 rtw8922a_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, static void rtw8922a_chlk_reload(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; u8 s0_tbl, s1_tbl; - switch (rtwdev->mlo_dbcc_mode) { - default: - case MLO_2_PLUS_0_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - break; - case MLO_0_PLUS_2_1RF: - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - break; - case MLO_1_PLUS_1_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - break; - } + rtw89_entity_get_conf(rtwdev, &conf); - s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan0, 0); - s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan1, 1); + s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); + s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); rtw8922a_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922a_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 90c62b757c57..5527a8db393b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -77,6 +77,7 @@ static const struct rtw89_driver_info rtw89_8922ae_info = { .chip = &rtw8922a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922a_pci_info, }, @@ -86,6 +87,7 @@ static const struct rtw89_driver_info rtw89_8922ae_vs_info = { .chip = &rtw8922a_chip_info, .variant = &rtw8922ae_vs_variant, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922a_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c new file mode 100644 index 000000000000..2b81de501d62 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include +#include +#include "rtw8922a.h" +#include "usb.h" + +static const struct rtw89_usb_info rtw8922a_usb_info = { + .usb_host_request_2 = 0, + .usb_wlan0_1 = 0, + .hci_func_en = 0, + .usb3_mac_npi_config_intf_0 = 0, + .usb_endpoint_0 = 0, + .usb_endpoint_2 = 0, + .rx_agg_alignment = 16, + .bulkout_id = { + [RTW89_DMA_ACH0] = 3, + [RTW89_DMA_ACH2] = 5, + [RTW89_DMA_ACH4] = 4, + [RTW89_DMA_ACH6] = 6, + [RTW89_DMA_B0MG] = 0, + [RTW89_DMA_B0HI] = 0, + [RTW89_DMA_B1MG] = 1, + [RTW89_DMA_B1HI] = 1, + [RTW89_DMA_H2C] = 2, + }, +}; + +static const struct rtw89_driver_info rtw89_8922au_info = { + .chip = &rtw8922a_chip_info, + .variant = NULL, + .quirks = NULL, + .dev_id_quirks = 0, + .bus = { + .usb = &rtw8922a_usb_info, + }, +}; + +static const struct usb_device_id rtw_8922au_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03ef, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0502, 0x76d7, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4026, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1bcf, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1bd2, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x8912, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0xda0e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0100, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0101, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x3822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x4822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x5822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8922au_id_table); + +static struct usb_driver rtw_8922au_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8922au_id_table, + .probe = rtw89_usb_probe, + .disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8922au_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11be wireless 8922AU driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index e3b77cd23514..838c26231897 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -25,6 +25,62 @@ RTW89_GEN_MODULE_FWNAME(RTW8922DS_FW_BASENAME, RTW8922DS_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { + {2, 732, 0}, /* ACH 0 */ + {0, 730, 0}, /* ACH 1 */ + {2, 732, 0}, /* ACH 2 */ + {0, 730, 0}, /* ACH 3 */ + {2, 732, 0}, /* ACH 4 */ + {0, 730, 0}, /* ACH 5 */ + {2, 732, 0}, /* ACH 6 */ + {0, 730, 0}, /* ACH 7 */ + {2, 732, 0}, /* B0MGQ */ + {0, 730, 0}, /* B0HIQ */ + {2, 732, 0}, /* B1MGQ */ + {0, 730, 0}, /* B1HIQ */ + {0, 0, 0}, /* GCQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { + 742, /* Group 0 */ + 0, /* Group 1 */ + 742, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size22_v1, + &rtw89_mac_size.ple_size29_v1, &rtw89_mac_size.wde_qt23_v1, + &rtw89_mac_size.wde_qt23_v1, &rtw89_mac_size.ple_qt64_v2, + &rtw89_mac_size.ple_qt65_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size17, &rtw89_mac_size.rsvd1_size3, + &rtw89_mac_size.dle_input28}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size22_v1, + &rtw89_mac_size.ple_size29_v1, &rtw89_mac_size.wde_qt23_v1, + &rtw89_mac_size.wde_qt23_v1, &rtw89_mac_size.ple_qt64_v2, + &rtw89_mac_size.ple_qt65_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size17, &rtw89_mac_size.rsvd1_size3, + &rtw89_mac_size.dle_input28}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, + &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, + &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, + &rtw89_mac_size.ple_qt47_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, + &rtw89_mac_size.dle_input3}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_hfc_ch_cfg rtw8922ds_hfc_chcfg_pcie[] = { {2, 603, 0}, /* ACH 0 */ {0, 601, 0}, /* ACH 1 */ {2, 603, 0}, /* ACH 2 */ @@ -37,45 +93,43 @@ static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { {0, 601, 0}, /* B0HIQ */ {2, 603, 0}, /* B1MGQ */ {0, 601, 0}, /* B1HIQ */ - {0, 0, 0}, /* FWCMDQ */ - {0, 0, 0}, /* BMC */ - {0, 0, 0}, /* H2D */ + {0, 0, 0}, /* GCQ */ }; -static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { +static const struct rtw89_hfc_pub_cfg rtw8922ds_hfc_pubcfg_pcie = { 613, /* Group 0 */ 0, /* Group 1 */ 613, /* Public Max */ 0, /* WP threshold */ }; -static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { - [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, +static const struct rtw89_hfc_param_ini rtw8922ds_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922ds_hfc_chcfg_pcie, &rtw8922ds_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, - [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + [RTW89_QTA_DBCC] = {rtw8922ds_hfc_chcfg_pcie, &rtw8922ds_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, RTW89_HCIFC_POH}, [RTW89_QTA_INVALID] = {NULL}, }; -static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { +static const struct rtw89_dle_mem rtw8922ds_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, - &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, - &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, + &rtw89_mac_size.ple_qt45_v2, &rtw89_mac_size.ple_rsvd_qt9, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, - &rtw89_mac_size.dle_input18}, + &rtw89_mac_size.dle_input20}, [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, - &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, - &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, + &rtw89_mac_size.ple_qt45_v2, &rtw89_mac_size.ple_rsvd_qt9, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, - &rtw89_mac_size.dle_input18}, + &rtw89_mac_size.dle_input20}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, - &rtw89_mac_size.ple_qt5_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.ple_qt47_v2, &rtw89_mac_size.ple_rsvd_qt1, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, &rtw89_mac_size.dle_input3}, [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, @@ -227,6 +281,54 @@ static const struct rtw89_edcca_regs rtw8922d_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, }; +static const struct rtw89_pmac_regs rtw8922d_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON_BE4, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN_BE4, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_BE4, B_CNT_CCK_CCA_BE4}, + .cck_sfd_gg = {R_SFD_GG_CNT_BE4, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_BE4, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_BE4, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT_BE4, B_BRK_CNT}, + .brk = {R_CNT_BRK_BE4, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL_BE4, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH_BE4, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH_BE4, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY_BE4, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE_BE4, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA_BE4, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING_BE4, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS_BE4, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_BE4, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT_BE4, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT_BE4, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_BE4, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_BE4, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_BE4, + .ofdm_txon = R_CNT_OFDMTXON_BE4, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC_BE4, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC_BE4, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC_BE4, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC_BE4, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = R_CNT_EHT_CRC_BE4, + .eht_crc_ok_mask = B_CNT_EHT_CRC_OK, + .eht_crc_err_mask = B_CNT_EHT_CRC_ERR, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32_BE4, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, @@ -238,6 +340,135 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_ENABLE, CIM3K_ENABLE}, + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 3, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_ENABLE, CIM3K_ENABLE}, + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 0, + }}, + .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, +}; + +static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_DISABLE, CIM3K_ENABLE}, + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 3, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_DISABLE, CIM3K_ENABLE}, + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 0, + }}, + .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, +}; + +static const +struct rtw89_bb_wrap_common_data_gen3 rtw8922d_bb_wrap_common_data_gen3_7090 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_th = {RFSI_QPSK, RFSI_BPSK, RFSI_256QAM, RFSI_MAX, + RFSI_MAX, RFSI_MAX}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .qam_th = {RFSI_MAX, RFSI_MAX, RFSI_256QAM, RFSI_MAX, + RFSI_MAX, RFSI_MAX}, + }}, + .cck_val = {CFIR_OFF, FILTER_A_OFF}, +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .common = &rtw8922d_bb_wrap_common_data_7025, + .common_gen3 = NULL, + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6400, 0x6500, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4400, 0x4400, 0x4000, 0, 0, 0, 0, 0, 0}, + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4400, 0x4400, 0, 0, 0, 0, 0, 0, 0}, + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, + .mdpd_by_dbw = {MDPD_ON, MDPD_ON, MDPD_ON, MDPD_ON}, +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .common = &rtw8922d_bb_wrap_common_data_7090, + .common_gen3 = &rtw8922d_bb_wrap_common_data_gen3_7090, + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0}, + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0, 0}, + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, + .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .common = &rtw8922d_bb_wrap_common_data_7090, + .common_gen3 = &rtw8922d_bb_wrap_common_data_gen3_7090, + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0}, + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0, 0}, + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, + .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, +}; + static void rtw8922d_sel_bt_rx_path(struct rtw89_dev *rtwdev, u8 val, enum rtw89_rf_path rx_path) { @@ -870,6 +1101,34 @@ static void rtw8922d_power_trim(struct rtw89_dev *rtwdev) rtw8922d_pad_bias_trim(rtwdev); } +static int rtw8922d_data_setup(struct rtw89_dev *rtwdev) +{ + const struct rtw89_bb_wrap_data *data; + struct rtw89_hal *hal = &rtwdev->hal; + bool rfe35_41_44 = false; + + switch (rtwdev->efuse.rfe_type) { + case 35: + case 41: + case 44: + rfe35_41_44 = true; + break; + } + + if (hal->cid == RTL8922D_CID7025) { + data = &rtw8922d_bb_wrap_data_7025_default; + } else { + if (rfe35_41_44) + data = &rtw8922d_bb_wrap_data_7090_rfe35_41_44; + else + data = &rtw8922d_bb_wrap_data_7090_default; + } + + rtwdev->phy_info.bb_wrap_data = data; + + return 0; +} + static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, u8 mac_idx) @@ -1913,29 +2172,52 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_phy_idx phy_idx) { -#define DIGITAL_PWR_COMP_REG_NUM 22 +#define DIGITAL_PWR_COMP_BASE_NUM 7 +#define DIGITAL_PWR_COMP_VALS_NUM 15 static const u32 pw_comp_cr[2] = {R_RX_PATH0_TBL0_BE4, R_RX_PATH1_TBL0_BE4}; - const __le32 (*pwr_comp_val)[2][RTW89_TX_COMP_BAND_NR] - [BB_PATH_NUM_8922D][DIGITAL_PWR_COMP_REG_NUM]; + const struct { + struct { + __le32 base[DIGITAL_PWR_COMP_BASE_NUM]; + __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; + } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; + } *pwr_comp_v0; + const struct { + __le32 base[DIGITAL_PWR_COMP_BASE_NUM]; + struct { + __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; + } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; + } *pwr_comp; struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_fw_element_hdr *txcomp_elm = elm_info->tx_comp; - const __le32 *digital_pwr_comp; + const __le32 (*comp_base)[DIGITAL_PWR_COMP_BASE_NUM]; + const __le32 (*comp_vals)[DIGITAL_PWR_COMP_VALS_NUM]; u32 addr, val; u32 i; - if (sizeof(*pwr_comp_val) != le32_to_cpu(txcomp_elm->size)) { + if (sizeof(*pwr_comp) == le32_to_cpu(txcomp_elm->size)) { + pwr_comp = (const void *)txcomp_elm->u.common.contents; + comp_base = &pwr_comp->base; + comp_vals = &pwr_comp->sets[nss][chan->tx_comp_band][path].vals; + } else if (sizeof(*pwr_comp_v0) == le32_to_cpu(txcomp_elm->size)) { + pwr_comp_v0 = (const void *)txcomp_elm->u.common.contents; + comp_base = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].base; + comp_vals = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].vals; + } else { rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "incorrect power comp size %d\n", le32_to_cpu(txcomp_elm->size)); return; } - pwr_comp_val = (const void *)txcomp_elm->u.common.contents; - digital_pwr_comp = (*pwr_comp_val)[nss][chan->tx_comp_band][path]; addr = pw_comp_cr[path]; - for (i = 0; i < DIGITAL_PWR_COMP_REG_NUM; i++, addr += 4) { - val = le32_to_cpu(digital_pwr_comp[i]); + for (i = 0; i < ARRAY_SIZE(*comp_base); i++, addr += 4) { + val = le32_to_cpu((*comp_base)[i]); + rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); + } + + for (i = 0; i < ARRAY_SIZE(*comp_vals); i++, addr += 4) { + val = le32_to_cpu((*comp_vals)[i]); rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); } } @@ -1943,10 +2225,19 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - const struct rtw89_chan *chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; - if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + rtw89_entity_get_conf(rtwdev, &conf); + chan0 = conf.chans[0]; + chan1 = conf.chans[1]; + + if (conf.en_emlsr) { + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_A, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_B, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_A, RTW89_PHY_1); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_B, RTW89_PHY_1); + } else if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 0, RF_PATH_A, RTW89_PHY_0); rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 0, RF_PATH_B, RTW89_PHY_1); } else { @@ -1958,7 +2249,6 @@ static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode, bool pwr_comp) { - const struct rtw89_chan *chan1; u32 reg0, reg1; u8 cck_phy_idx; @@ -2016,8 +2306,10 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0); rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0); } else if ((mode == MLO_1_PLUS_1_1RF) || (mode == DBCC_LEGACY)) { - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - cck_phy_idx = chan1->band_type == RTW89_BAND_2G ? + struct rtw89_entity_conf conf; + + rtw89_entity_get_conf(rtwdev, &conf); + cck_phy_idx = conf.chans[1]->band_type == RTW89_BAND_2G ? RTW89_PHY_1 : RTW89_PHY_0; rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, @@ -2086,7 +2378,7 @@ static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev, u8 pri_sb = chan->pri_sb_idx; u32 val; - rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, chan->rfsi_band, phy_idx); rtw8922d_ctrl_ch(rtwdev, chan, phy_idx); rtw8922d_ctrl_bw(rtwdev, pri_sb, chan->band_width, phy_idx); rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); @@ -2322,12 +2614,16 @@ static void rtw8922d_rfk_tssi(struct rtw89_dev *rtwdev, static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif_link->rtwvif); enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB, chanctx_idx); u32 tx_en; + if (ieee80211_vif_is_mld(vif)) + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, true); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_START); rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, RF_AB); @@ -2343,6 +2639,9 @@ static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_STOP); + + if (ieee80211_vif_is_mld(vif)) + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false); } static void rtw8922d_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -2435,6 +2734,66 @@ static void rtw8922d_set_txpwr_ref(struct rtw89_dev *rtwdev, B_BE_PWR_REF_CTRL_CCK, ref_cck); } +static void rtw8922d_set_tx_shape(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; + const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape; + u8 tx_shape_idx; + u8 band, regd; + const u16 *th; + + band = chan->band_type; + regd = rtw89_regd_get(rtwdev, band); + tx_shape_idx = (*tx_shape->lmt)[band][RTW89_RS_OFDM][regd]; + + if (tx_shape_idx == 0) + goto disable; + + th = d->bands[chan->rfsi_band].qam_comp_th0; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, th[0], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, th[1], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, th[2], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, th[3], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, th[4], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, th[5], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, th[6], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, th[7], phy_idx); + + th = d->bands[chan->rfsi_band].qam_comp_ow; + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, th[0], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, th[1], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, th[2], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, th[3], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, th[4], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, th[5], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, th[6], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, th[7], phy_idx); + + return; + +disable: + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); +} + static void rtw8922d_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2466,6 +2825,7 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, { rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw8922d_set_tx_shape(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); @@ -2475,9 +2835,11 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, static void rtw8922d_set_txpwr_ctrl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); + struct rtw89_entity_conf conf; - rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); + rtw89_entity_get_conf(rtwdev, &conf); + + rtw8922d_set_txpwr_ref(rtwdev, conf.chans[phy_idx], phy_idx); } static void rtw8922d_ctrl_trx_path(struct rtw89_dev *rtwdev, @@ -2866,6 +3228,7 @@ static const struct rtw89_chip_ops rtw8922d_chip_ops = { .read_efuse = rtw8922d_read_efuse, .read_phycap = rtw8922d_read_phycap, .fem_setup = NULL, + .data_setup = rtw8922d_data_setup, .rfe_gpio = NULL, .rfk_hw_init = rtw8922d_rfk_hw_init, .rfk_init = rtw8922d_rfk_init, @@ -2948,8 +3311,10 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .max_rx_agg_num = 256, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x5f800, - .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL}, - .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + }, .wde_qempty_acq_grpnum = 8, .wde_qempty_mgq_grpsel = 8, .rf_base_addr = {0x3e000, 0x3f000}, @@ -2986,9 +3351,10 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = false, + .rx_freq_from_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, @@ -3065,6 +3431,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B0HI) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8922d_edcca_regs, + .pmac_regs = &rtw8922d_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8922d, #endif @@ -3079,10 +3446,16 @@ static const struct rtw89_fw_def rtw8922de_vs_fw_def = { .fw_b_aid = RTL8922D_AID7060, }; +static const struct rtw89_qta_def rtw8922ds_qta_def = { + .hfc_param_ini = {rtw8922ds_hfc_param_ini_pcie, NULL, NULL}, + .dle_mem = {rtw8922ds_dle_mem_pcie, NULL, NULL, NULL}, +}; + const struct rtw89_chip_variant rtw8922de_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 0, 0, 0), .fw_def_override = &rtw8922de_vs_fw_def, + .qta_def_override = &rtw8922ds_qta_def, }; EXPORT_SYMBOL(rtw8922de_vs_variant); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 4e6a8e88a71e..a465e6631a22 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -105,26 +105,60 @@ static void rtw8922d_set_syn01(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) } } -static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual) { - bool mlo_linking = false; + if (manual) { + struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; + u8 path = phy_idx; /* numerical equal for target */ + u8 idx = rfk_mcc[path].table_idx; - if (idx > 2) { - rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); return; } - if (mlo_linking) { - if (kpath & RF_A) { - rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); - rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); - } + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); +} - if (kpath & RF_B) { - rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); - rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); - } +static void rtw8922d_chlk_ktbl_sel_mld(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 idx) +{ + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx); + } else if (phy_idx == RTW89_PHY_1) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx); + } + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false); +} + +static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +{ + if (idx > 2) { + rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); return; } @@ -187,30 +221,26 @@ static u8 rtw8922d_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; u8 s0_tbl, s1_tbl; - switch (rtwdev->mlo_dbcc_mode) { - default: - case MLO_2_PLUS_0_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - break; - case MLO_0_PLUS_2_1RF: - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - break; - case MLO_1_PLUS_1_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - break; - } + rtw89_entity_get_conf(rtwdev, &conf); - s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan0, 0); - s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan1, 1); + s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); + s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); + + if (conf.is_mld) + goto mld; rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); + return; + +mld: + if (test_bit(RTW89_PHY_0, conf.hw_bitmap)) + rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_0, s0_tbl); + if (test_bit(RTW89_PHY_1, conf.hw_bitmap)) + rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_1, s1_tbl); } static enum _rf_syn_pow rtw8922d_get_syn_pow(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h index c5bbe0eb972a..11742b4a8f59 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -18,5 +18,6 @@ void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev); void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_lck_track(struct rtw89_dev *rtwdev); +void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual); #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c index f144e7fc76de..a1a81c338be3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8922de_vs_info = { .chip = &rtw8922d_chip_info, .variant = &rtw8922de_vs_variant, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922d_pci_info, }, @@ -82,6 +83,7 @@ static const struct rtw89_driver_info rtw89_8922de_info = { .chip = &rtw8922d_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922d_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 7886ffaf5695..6fe3822eb598 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -693,6 +693,7 @@ static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) static void rtw89_tas_init(struct rtw89_dev *rtwdev) { + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_tas_info *tas = &rtwdev->tas; const struct rtw89_acpi_policy_tas *ptr; @@ -702,6 +703,9 @@ static void rtw89_tas_init(struct rtw89_dev *rtwdev) if (!chip->support_tas) return; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_SAR, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index b69a2529aefc..4e7109fbe7c9 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -634,8 +634,10 @@ struct rtw89_phy_sts_ie01 { #define RTW89_PHY_STS_IE01_W2_AVG_SNR GENMASK(5, 0) #define RTW89_PHY_STS_IE01_W2_EVM_MAX GENMASK(15, 8) #define RTW89_PHY_STS_IE01_W2_EVM_MIN GENMASK(23, 16) +#define RTW89_PHY_STS_IE01_W2_SU BIT(27) #define RTW89_PHY_STS_IE01_W2_LDPC BIT(28) #define RTW89_PHY_STS_IE01_W2_STBC BIT(30) +#define RTW89_PHY_STS_IE01_W3_BF BIT(8) struct rtw89_phy_sts_ie01_v2 { __le32 w0; @@ -656,6 +658,114 @@ struct rtw89_phy_sts_ie01_v2 { #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4) #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16) +union rtw89_phy_sts_ie09 { + struct { + __le64 qw0; + } gen0; + struct { + __le64 qw0; + __le64 qw1; + } gen2; +} __packed; + +#define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */ +#define RTW89_PHY_STS_IE09_L_SIG_MASK_GEN2 GENMASK(31, 15) /* Legacy/VHT/HE L-SIG for GEN2 */ +#define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0) +#define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4) +#define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2 GENMASK_ULL(55, 32) /* VHT SIG-A1 for GEN2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID GENMASK(9, 4) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_NSTS GENMASK(12, 10) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID GENMASK(21, 13) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS0 GENMASK(12, 10) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1 GENMASK(15, 13) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2 GENMASK(18, 16) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3 GENMASK(21, 19) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 56) /* VHT SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_H8 GENMASK_ULL(1, 0) /* VHT SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_LDPC BIT(3) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE SU SIG-A1 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MCS GENMASK(6, 3) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM BIT(7) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR GENMASK(13, 8) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_RSVD1 BIT(14) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR GENMASK(18, 15) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BW GENMASK(20, 19) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE SU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE SU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_STBC BIT(9) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED BIT(10) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC GENMASK(12, 11) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE BIT(13) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE TB SIG-A1 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2 GENMASK(14, 11) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3 GENMASK(18, 15) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4 GENMASK(22, 19) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE TB SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE TB SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE MU SIG-A1 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR GENMASK(10, 5) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR GENMASK(14, 11) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW GENMASK(17, 15) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR GENMASK(21, 18) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM BIT(22) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE MU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE MU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID BIT(10) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG BIT(11) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_STBC BIT(12) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC GENMASK(14, 13) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE BIT(15) + +struct rtw89_phy_sts_ie10 { + __le64 qw0; + u8 sigb[]; +} __packed; + enum rtw89_tx_channel { RTW89_TXCH_ACH0 = 0, RTW89_TXCH_ACH1 = 1, diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 767a95f759b1..aafa8b9e93df 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -11,8 +11,8 @@ static void rtw89_usb_read_port_complete(struct urb *urb); -static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, - void *data, u16 len, u8 reqtype) +static void __rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, + void *data, u16 len, u8 reqtype, bool warn) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct usb_device *udev = rtwusb->udev; @@ -52,7 +52,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, if (ret == -ESHUTDOWN || ret == -ENODEV) set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); - else if (ret < 0) + else if (ret < 0 && warn) rtw89_warn(rtwdev, "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n", str_read_write(reqtype == RTW89_USB_VENQT_READ), @@ -69,6 +69,12 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, } } +static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, + void *data, u16 len, u8 reqtype) +{ + __rtw89_usb_vendorreq(rtwdev, addr, data, len, reqtype, true); +} + static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr) { u32 addr32, val32, shift; @@ -157,6 +163,14 @@ static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val) rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE); } +static void rtw89_usb_write32_quiet(struct rtw89_dev *rtwdev, u32 addr, u32 val) +{ + __le32 data = cpu_to_le32(val); + + __rtw89_usb_vendorreq(rtwdev, addr, &data, 4, + RTW89_USB_VENQT_WRITE, false); +} + static u32 rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 txch) @@ -511,7 +525,7 @@ static void rtw89_usb_rx_resubmit(struct rtw89_usb *rtwusb, rxcb->rx_skb = rx_skb; usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, - usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe), + usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe[0]), rxcb->rx_skb->data, RTW89_USB_RECVBUF_SZ, rtw89_usb_read_port_complete, rxcb); @@ -756,6 +770,9 @@ static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev) const struct rtw89_usb_info *info = rtwusb->info; u32 val32; + if (rtwdev->chip->chip_id == RTL8922A) + return 0; + rtw89_write32_set(rtwdev, info->usb_host_request_2, B_AX_R_USBIO_MODE); @@ -800,6 +817,17 @@ static void rtw89_usb_rx_agg_cfg_v2(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_AX_RXAGG_1_V1, 0x1F); } +static void rtw89_usb_rx_agg_cfg_v3(struct rtw89_dev *rtwdev) +{ + const u32 rxagg_0 = FIELD_PREP_CONST(B_BE_RXAGG_0_EN, 1) | + FIELD_PREP_CONST(B_BE_RXAGG_0_NUM_TH, 255) | + FIELD_PREP_CONST(B_BE_RXAGG_0_TIME_32US_TH, 32) | + FIELD_PREP_CONST(B_BE_RXAGG_0_BUF_SZ_1K, 20); + + rtw89_write32(rtwdev, R_BE_RXAGG_0_V1, rxagg_0); + rtw89_write32(rtwdev, R_BE_RXAGG_1_V1, 0x1F); +} + static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) { switch (rtwdev->chip->chip_id) { @@ -811,6 +839,9 @@ static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) case RTL8852C: rtw89_usb_rx_agg_cfg_v2(rtwdev); break; + case RTL8922A: + rtw89_usb_rx_agg_cfg_v3(rtwdev); + break; default: rtw89_warn(rtwdev, "%s: USB RX agg not support\n", __func__); return; @@ -824,6 +855,9 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) enum usb_device_speed speed; u32 ep; + if (rtwdev->chip->chip_id == RTL8922A) + goto rx_agg_cfg; + rtw89_write32_clr(rtwdev, info->usb3_mac_npi_config_intf_0, B_AX_SSPHY_LFPS_FILTER); @@ -845,6 +879,7 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) rtw89_write8(rtwdev, info->usb_endpoint_2 + 1, NUMP); } +rx_agg_cfg: rtw89_usb_rx_agg_cfg(rtwdev); return 0; @@ -871,6 +906,10 @@ static int rtw89_usb_ops_mac_lv1_rcvy(struct rtw89_dev *rtwdev, reg = R_AX_USB_WLAN0_1_V1; mask = B_AX_USBRX_RST_V1 | B_AX_USBTX_RST_V1; break; + case RTL8922A: + reg = R_BE_USB2_WLAN_TRX_OPT_PAR2; + mask = B_BE_USB2_USBRX_RST | B_BE_USB2_USBTX_RST; + break; default: rtw89_err(rtwdev, "%s: fix me\n", __func__); return -EOPNOTSUPP; @@ -948,6 +987,7 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct usb_endpoint_descriptor *endpoint; int num_out_pipes = 0; + int num_in_pipes = 0; u8 num; int i; @@ -963,13 +1003,14 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, if (usb_endpoint_dir_in(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { - if (rtwusb->in_pipe) { + if (num_in_pipes >= RTW89_MAX_BULKIN_NUM) { rtw89_err(rtwdev, - "found more than 1 bulk in endpoint\n"); + "found more than %d bulk in endpoint\n", + RTW89_MAX_BULKIN_NUM); return -EINVAL; } - rtwusb->in_pipe = num; + rtwusb->in_pipe[num_in_pipes++] = num; } if (usb_endpoint_dir_out(endpoint) && @@ -985,6 +1026,11 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, } } + if (num_in_pipes < 1) { + rtw89_err(rtwdev, "no bulk in endpoints found\n"); + return -EINVAL; + } + if (num_out_pipes < 1) { rtw89_err(rtwdev, "no bulk out endpoints found\n"); return -EINVAL; @@ -1027,6 +1073,133 @@ static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev, usb_set_intfdata(intf, NULL); } +static int rtw89_usb_switch_mode_ax(struct rtw89_dev *rtwdev) +{ + u32 pad_ctrl2; + + /* No known USB 3 devices with this chip. */ + if (rtwdev->chip->chip_id == RTL8851B) + return 0; + + pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_AX_PAD_CTRL2); + + rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n", + __func__, pad_ctrl2); + + /* Already tried to switch but it's a USB 2 port. */ + if (u32_get_bits(pad_ctrl2, B_AX_MATCH_CNT) == USB_SWITCH_DELAY) + return 0; + + /* Add delay to prevent some platforms would not detect USB switch */ + u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_AX_MATCH_CNT); + + pad_ctrl2 &= ~(B_AX_FORCE_U3_CK | B_AX_USB2_FORCE | + B_AX_USB3_FORCE | B_AX_USB3_USB2_TRANSITION); + + u32p_replace_bits(&pad_ctrl2, USB_MODE_U3, B_AX_USB23_SW_MODE_V1); + + pad_ctrl2 |= B_AX_NO_PDN_CHIPOFF_V1 | B_AX_RSM_EN_V1; + + rtw89_usb_write32_quiet(rtwdev, R_AX_PAD_CTRL2, pad_ctrl2); + + return 1; +} + +static int rtw89_usb_switch_mode_be(struct rtw89_dev *rtwdev) +{ + u32 pad_ctrl2; + + pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_BE_PAD_CTRL2); + + rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n", + __func__, pad_ctrl2); + + /* Already tried to switch but it's a USB 2 port. */ + if (u32_get_bits(pad_ctrl2, B_BE_MATCH_CNT) == USB_SWITCH_DELAY) + return 0; + + /* Add delay to prevent some platforms would not detect USB switch */ + u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_BE_MATCH_CNT); + + pad_ctrl2 |= B_BE_RSM_EN_V1 | B_BE_NO_PDN_CHIPOFF_V1 | + B_BE_USB_AUTO_INSTALL_MASK | B_BE_USB23_SW_MODE; + + pad_ctrl2 &= ~(B_BE_USB3_FORCE | B_BE_USB2_FORCE | + B_BE_FORCE_U3_CK | B_BE_FORCE_U2_CK | + B_BE_FORCE_CLK_U2 | B_BE_USB3_GEN_MODE | + B_BE_USB3_LANE_MODE); + + rtw89_usb_write32_quiet(rtwdev, R_BE_PAD_CTRL2, pad_ctrl2); + + return 1; +} + +static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev) +{ + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + + if (rtwusb->udev->speed == USB_SPEED_SUPER) { + rtw89_info(rtwdev, + "2.4 GHz performance may be better in a USB 2 port\n"); + return 0; + } + + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) + return rtw89_usb_switch_mode_ax(rtwdev); + + return rtw89_usb_switch_mode_be(rtwdev); +} + +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + return sysfs_emit(buf, "%*phN\n", + (int)sizeof(efuse->sn), efuse->sn); +} +static DEVICE_ATTR_RO(serial_number); + +static ssize_t uuid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + return sysfs_emit(buf, "%pUb\n", efuse->uuid); +} +static DEVICE_ATTR_RO(uuid); + +static struct attribute *rtw89_usb_attrs[] = { + &dev_attr_serial_number.attr, + &dev_attr_uuid.attr, + NULL, +}; + +static bool rtw89_usb_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + + return test_bit(RTW89_QUIRK_HW_INFO_SYSFS, rtwdev->quirks); +} + +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(rtw89_usb); + +static const struct attribute_group rtw89_usb_group = { + .name = "rtw89_usb", + .attrs = rtw89_usb_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(rtw89_usb), +}; +__ATTRIBUTE_GROUPS(rtw89_usb); + int rtw89_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1038,8 +1211,7 @@ int rtw89_usb_probe(struct usb_interface *intf, info = (const struct rtw89_driver_info *)id->driver_info; rtwdev = rtw89_alloc_ieee80211_hw(&intf->dev, - sizeof(struct rtw89_usb), - info->chip, info->variant); + sizeof(struct rtw89_usb), info); if (!rtwdev) { dev_err(&intf->dev, "failed to allocate hw\n"); return -ENOMEM; @@ -1049,6 +1221,8 @@ int rtw89_usb_probe(struct usb_interface *intf, rtwusb->rtwdev = rtwdev; rtwusb->info = info->bus.usb; + rtwdev->hw->wiphy->dev.groups = rtw89_usb_groups; + rtwdev->hci.ops = &rtw89_usb_ops; rtwdev->hci.type = RTW89_HCI_TYPE_USB; rtwdev->hci.tx_rpt_enabled = true; @@ -1059,6 +1233,13 @@ int rtw89_usb_probe(struct usb_interface *intf, goto err_free_hw; } + ret = rtw89_usb_switch_mode(rtwdev); + if (ret) { + /* Not a fail, but we do need to skip rtw89_core_register. */ + ret = 0; + goto err_intf_deinit; + } + if (rtwusb->udev->speed == USB_SPEED_SUPER) rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3; else diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 507f61f58ed9..bdf312559743 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -18,6 +18,7 @@ #define RTW89_USB_MOD512_PADDING 4 #define RTW89_MAX_ENDPOINT_NUM 9 +#define RTW89_MAX_BULKIN_NUM 2 #define RTW89_MAX_BULKOUT_NUM 7 #define R_AX_RXAGG_0_V1 0x6000 @@ -28,6 +29,14 @@ #define R_AX_RXAGG_1_V1 0x6004 +#define R_BE_RXAGG_0_V1 0x6000 +#define B_BE_RXAGG_0_EN BIT(31) +#define B_BE_RXAGG_0_NUM_TH GENMASK(23, 16) +#define B_BE_RXAGG_0_TIME_32US_TH GENMASK(15, 8) +#define B_BE_RXAGG_0_BUF_SZ_1K GENMASK(7, 0) + +#define R_BE_RXAGG_1_V1 0x6004 + #define R_AX_RXAGG_0 0x8900 #define B_AX_RXAGG_0_BUF_SZ_4K GENMASK(7, 0) @@ -65,7 +74,7 @@ struct rtw89_usb { atomic_t continual_io_error; - u8 in_pipe; + u8 in_pipe[RTW89_MAX_BULKIN_NUM]; u8 out_pipe[RTW89_MAX_BULKOUT_NUM]; struct workqueue_struct *rxwq; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1c340a4a0930..be583ae331c0 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -32,6 +32,15 @@ #define WL1271_BOOT_RETRIES 3 #define WL1271_WAKEUP_TIMEOUT 500 +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WL1271_CIPHER_SUITE_GEM, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + static char *fwlog_param; static int fwlog_mem_blocks = -1; static int bug_on_recovery = -1; @@ -2367,6 +2376,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) static int wl12xx_init_fw(struct wl1271 *wl) { + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev); int retries = WL1271_BOOT_RETRIES; bool booted = false; struct wiphy *wiphy = wl->hw->wiphy; @@ -2421,8 +2431,9 @@ static int wl12xx_init_fw(struct wl1271 *wl) /* WLAN_CIPHER_SUITE_AES_CMAC must be last in cipher_suites; support only with firmware 8.9.1 and newer */ - if (wl->chip.fw_ver[FW_VER_MAJOR] < 1) - wl->hw->wiphy->n_cipher_suites--; + if (wl->chip.fw_ver[FW_VER_MAJOR] < 1 || + (!strncmp(pdev_data->family->name, "wl12", 4))) + wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - 1; /* * Now we know if 11a is supported (info from the NVS), so disable @@ -6198,14 +6209,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl) static int wl1271_init_ieee80211(struct wl1271 *wl) { int i; - static const u32 cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WL1271_CIPHER_SUITE_GEM, - WLAN_CIPHER_SUITE_AES_CMAC, - }; /* The tx descriptor buffer */ wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr); diff --git a/drivers/net/wireless/virtual/Makefile b/drivers/net/wireless/virtual/Makefile index 5773cc6d643e..6ad860dd7643 100644 --- a/drivers/net/wireless/virtual/Makefile +++ b/drivers/net/wireless/virtual/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o +mac80211_hwsim-objs += mac80211_hwsim_main.o +mac80211_hwsim-objs += mac80211_hwsim_nan.o obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h new file mode 100644 index 000000000000..0f0f2ac6d80e --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 + * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2011, Javier Lopez + * Copyright (c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 - 2026 Intel Corporation + */ + +#ifndef __MAC80211_HWSIM_I_H +#define __MAC80211_HWSIM_I_H + +#include +#include "mac80211_hwsim.h" +#include "mac80211_hwsim_nan.h" + +struct hwsim_sta_nan_sched { + /* Later members are protected by this lock */ + spinlock_t lock; + u16 committed_dw; + struct { + u8 map_id; + struct cfg80211_chan_def chans[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + } maps[CFG80211_NAN_MAX_PEER_MAPS]; +}; + +struct hwsim_sta_priv { + u32 magic; + unsigned int last_link; + u16 active_links_rx; + + /* NAN peer schedule - must be accessed under nan_sched.lock */ + struct hwsim_sta_nan_sched nan_sched; +}; + +#define HWSIM_STA_MAGIC 0x6d537749 + +struct mac80211_hwsim_link_data { + u32 link_id; + u64 beacon_int /* beacon interval in us */; + struct hrtimer beacon_timer; +}; + +#define HWSIM_NUM_CHANNELS_2GHZ 14 +#define HWSIM_NUM_CHANNELS_5GHZ 40 +#define HWSIM_NUM_CHANNELS_6GHZ 59 +#define HWSIM_NUM_S1G_CHANNELS_US 51 +#define HWSIM_NUM_RATES 12 +#define HWSIM_NUM_CIPHERS 11 + +struct mac80211_hwsim_data { + struct list_head list; + struct rhash_head rht; + struct ieee80211_hw *hw; + struct device *dev; + struct ieee80211_supported_band bands[NUM_NL80211_BANDS]; + struct ieee80211_channel channels_2ghz[HWSIM_NUM_CHANNELS_2GHZ]; + struct ieee80211_channel channels_5ghz[HWSIM_NUM_CHANNELS_5GHZ]; + struct ieee80211_channel channels_6ghz[HWSIM_NUM_CHANNELS_6GHZ]; + struct ieee80211_channel channels_s1g[HWSIM_NUM_S1G_CHANNELS_US]; + struct ieee80211_rate rates[HWSIM_NUM_RATES]; + struct ieee80211_iface_combination if_combination; + struct ieee80211_iface_limit if_limits[5]; + int n_if_limits; + /* Storage space for channels, etc. */ + struct mac80211_hwsim_phy_data *phy_data; + + struct ieee80211_iface_combination if_combination_radio; + struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS]; + struct wiphy_radio radio[NUM_NL80211_BANDS]; + + u32 ciphers[HWSIM_NUM_CIPHERS]; + + struct mac_address addresses[3]; + int channels, idx; + bool use_chanctx; + bool destroy_on_close; + u32 portid; + char alpha2[2]; + const struct ieee80211_regdomain *regd; + + struct ieee80211_channel *tmp_chan; + struct ieee80211_channel *roc_chan; + u32 roc_duration; + struct delayed_work roc_start; + struct delayed_work roc_done; + struct delayed_work hw_scan; + struct cfg80211_scan_request *hw_scan_request; + struct ieee80211_vif *hw_scan_vif; + int scan_chan_idx; + u8 scan_addr[ETH_ALEN]; + struct { + struct ieee80211_channel *channel; + unsigned long next_start, start, end; + } survey_data[HWSIM_NUM_CHANNELS_2GHZ + + HWSIM_NUM_CHANNELS_5GHZ + + HWSIM_NUM_CHANNELS_6GHZ]; + + struct ieee80211_channel *channel; + enum nl80211_chan_width bw; + unsigned int rx_filter; + bool started, idle, scanning; + struct mutex mutex; + enum ps_mode { + PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL + } ps; + bool ps_poll_pending; + struct dentry *debugfs; + struct cfg80211_chan_def radar_background_chandef; + + atomic_t pending_cookie; + struct sk_buff_head pending; /* packets pending */ + /* + * Only radios in the same group can communicate together (the + * channel has to match too). Each bit represents a group. A + * radio can be in more than one group. + */ + u64 group; + + /* group shared by radios created in the same netns */ + int netgroup; + /* wmediumd portid responsible for netgroup of this radio */ + u32 wmediumd; + + /* difference between this hw's clock and the real clock, in usecs */ + spinlock_t tsf_offset_lock; + s64 tsf_offset; + + /* Stats */ + u64 tx_pkts; + u64 rx_pkts; + u64 tx_bytes; + u64 rx_bytes; + u64 tx_dropped; + u64 tx_failed; + + /* RSSI in rx status of the receiver */ + int rx_rssi; + + /* only used when pmsr capability is supplied */ + struct cfg80211_pmsr_capabilities pmsr_capa; + struct cfg80211_pmsr_request *pmsr_request; + struct wireless_dev *pmsr_request_wdev; + + struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; + + struct mac80211_hwsim_nan_data nan; +}; + +extern spinlock_t hwsim_radio_lock; +extern struct list_head hwsim_radios; + +ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, + u64 tsf); +u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, + ktime_t ts); + +u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan); + +void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); + +#endif /* __MAC80211_HWSIM_I_H */ diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c similarity index 90% rename from drivers/net/wireless/virtual/mac80211_hwsim.c rename to drivers/net/wireless/virtual/mac80211_hwsim_main.c index 1fcf5d0d2e13..0dd8a6c85953 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.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 - 2025 Intel Corporation + * Copyright (C) 2018 - 2026 Intel Corporation */ /* @@ -39,6 +39,7 @@ #include #include #include "mac80211_hwsim.h" +#include "mac80211_hwsim_i.h" #define WARN_QUEUE 100 #define MAX_QUEUE 200 @@ -251,14 +252,6 @@ static inline void hwsim_clear_magic(struct ieee80211_vif *vif) vp->magic = 0; } -struct hwsim_sta_priv { - u32 magic; - unsigned int last_link; - u16 active_links_rx; -}; - -#define HWSIM_STA_MAGIC 0x6d537749 - static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta) { struct hwsim_sta_priv *sp = (void *)sta->drv_priv; @@ -363,6 +356,14 @@ static struct net_device *hwsim_mon; /* global monitor netdev */ .hw_value = (_freq), \ } +#define CHANS1G(_freq, _offset, _flags) { \ + .band = NL80211_BAND_S1GHZ, \ + .center_freq = (_freq), \ + .freq_offset = (_offset), \ + .hw_value = (_freq), \ + .flags = (_flags), \ +} + static const struct ieee80211_channel hwsim_channels_2ghz[] = { CHAN2G(2412), /* Channel 1 */ CHAN2G(2417), /* Channel 2 */ @@ -379,6 +380,8 @@ static const struct ieee80211_channel hwsim_channels_2ghz[] = { CHAN2G(2472), /* Channel 13 */ CHAN2G(2484), /* Channel 14 */ }; +static_assert(HWSIM_NUM_CHANNELS_2GHZ == ARRAY_SIZE(hwsim_channels_2ghz), + "Inconsistent 2 GHz channel count"); static const struct ieee80211_channel hwsim_channels_5ghz[] = { CHAN5G(5180), /* Channel 36 */ @@ -428,6 +431,8 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = { CHAN5G(5920), /* Channel 184 */ CHAN5G(5925), /* Channel 185 */ }; +static_assert(HWSIM_NUM_CHANNELS_5GHZ == ARRAY_SIZE(hwsim_channels_5ghz), + "Inconsistent 5 GHz channel count"); static const struct ieee80211_channel hwsim_channels_6ghz[] = { CHAN6G(5955), /* Channel 1 */ @@ -490,9 +495,41 @@ static const struct ieee80211_channel hwsim_channels_6ghz[] = { CHAN6G(7095), /* Channel 229 */ CHAN6G(7115), /* Channel 233 */ }; +static_assert(HWSIM_NUM_CHANNELS_6GHZ == ARRAY_SIZE(hwsim_channels_6ghz), + "Inconsistent 6 GHz channel count"); -#define NUM_S1G_CHANS_US 51 -static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US]; +/* + * US 2024 channels (op class 1). Additionally to emulate real world + * US operation, the edgeband 1MHz channels (1, 51) are marked as NO_PRIMARY. + */ +static const struct ieee80211_channel hwsim_channels_s1g[] = { + CHANS1G(902, 500, IEEE80211_CHAN_S1G_NO_PRIMARY), /* Channel 1 */ + CHANS1G(903, 500, 0), /* Channel 3 */ + CHANS1G(904, 500, 0), /* Channel 5 */ + CHANS1G(905, 500, 0), /* Channel 7 */ + CHANS1G(906, 500, 0), /* Channel 9 */ + CHANS1G(907, 500, 0), /* Channel 11 */ + CHANS1G(908, 500, 0), /* Channel 13 */ + CHANS1G(909, 500, 0), /* Channel 15 */ + CHANS1G(910, 500, 0), /* Channel 17 */ + CHANS1G(911, 500, 0), /* Channel 19 */ + CHANS1G(912, 500, 0), /* Channel 21 */ + CHANS1G(913, 500, 0), /* Channel 23 */ + CHANS1G(914, 500, 0), /* Channel 25 */ + CHANS1G(915, 500, 0), /* Channel 27 */ + CHANS1G(916, 500, 0), /* Channel 29 */ + CHANS1G(917, 500, 0), /* Channel 31 */ + CHANS1G(918, 500, 0), /* Channel 33 */ + CHANS1G(919, 500, 0), /* Channel 35 */ + CHANS1G(920, 500, 0), /* Channel 37 */ + CHANS1G(921, 500, 0), /* Channel 39 */ + CHANS1G(922, 500, 0), /* Channel 41 */ + CHANS1G(923, 500, 0), /* Channel 43 */ + CHANS1G(924, 500, 0), /* Channel 45 */ + CHANS1G(925, 500, 0), /* Channel 47 */ + CHANS1G(926, 500, 0), /* Channel 49 */ + CHANS1G(927, 500, IEEE80211_CHAN_S1G_NO_PRIMARY), /* Channel 51 */ +}; static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = { .s1g = true, @@ -521,19 +558,6 @@ static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = { 0 }, }; -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; - 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; - } -} - static const struct ieee80211_rate hwsim_rates[] = { { .bitrate = 10 }, { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, @@ -548,6 +572,8 @@ static const struct ieee80211_rate hwsim_rates[] = { { .bitrate = 480 }, { .bitrate = 540 } }; +static_assert(HWSIM_NUM_RATES == ARRAY_SIZE(hwsim_rates), + "Inconsistent rates count"); #define DEFAULT_RX_RSSI -50 @@ -564,6 +590,8 @@ static const u32 hwsim_ciphers[] = { WLAN_CIPHER_SUITE_BIP_GMAC_128, WLAN_CIPHER_SUITE_BIP_GMAC_256, }; +static_assert(HWSIM_NUM_CIPHERS == ARRAY_SIZE(hwsim_ciphers), + "Inconsistent cipher count"); #define OUI_QCA 0x001374 #define QCA_NL80211_SUBCMD_TEST 1 @@ -644,12 +672,11 @@ static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = { { .vendor_id = OUI_QCA, .subcmd = 1 }, }; -static DEFINE_SPINLOCK(hwsim_radio_lock); -static LIST_HEAD(hwsim_radios); +DEFINE_SPINLOCK(hwsim_radio_lock); +LIST_HEAD(hwsim_radios); static struct rhashtable hwsim_radios_rht; static int hwsim_radio_idx; static int hwsim_radios_generation = 1; -static u8 hwsim_nan_cluster_id[ETH_ALEN]; static struct platform_driver mac80211_hwsim_driver = { .driver = { @@ -657,117 +684,6 @@ static struct platform_driver mac80211_hwsim_driver = { }, }; -struct mac80211_hwsim_link_data { - u32 link_id; - u64 beacon_int /* beacon interval in us */; - struct hrtimer beacon_timer; -}; - -struct mac80211_hwsim_data { - struct list_head list; - struct rhash_head rht; - struct ieee80211_hw *hw; - struct device *dev; - struct ieee80211_supported_band bands[NUM_NL80211_BANDS]; - struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; - struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; - struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)]; - struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)]; - struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; - struct ieee80211_iface_combination if_combination; - struct ieee80211_iface_limit if_limits[4]; - int n_if_limits; - - struct ieee80211_iface_combination if_combination_radio; - struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS]; - struct wiphy_radio radio[NUM_NL80211_BANDS]; - - u32 ciphers[ARRAY_SIZE(hwsim_ciphers)]; - - struct mac_address addresses[3]; - int channels, idx; - bool use_chanctx; - bool destroy_on_close; - u32 portid; - char alpha2[2]; - const struct ieee80211_regdomain *regd; - - struct ieee80211_channel *tmp_chan; - struct ieee80211_channel *roc_chan; - u32 roc_duration; - struct delayed_work roc_start; - struct delayed_work roc_done; - struct delayed_work hw_scan; - struct cfg80211_scan_request *hw_scan_request; - struct ieee80211_vif *hw_scan_vif; - int scan_chan_idx; - u8 scan_addr[ETH_ALEN]; - struct { - struct ieee80211_channel *channel; - unsigned long next_start, start, end; - } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) + - ARRAY_SIZE(hwsim_channels_5ghz) + - ARRAY_SIZE(hwsim_channels_6ghz)]; - - struct ieee80211_channel *channel; - enum nl80211_chan_width bw; - unsigned int rx_filter; - bool started, idle, scanning; - struct mutex mutex; - enum ps_mode { - PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL - } ps; - bool ps_poll_pending; - struct dentry *debugfs; - struct cfg80211_chan_def radar_background_chandef; - - atomic_t pending_cookie; - struct sk_buff_head pending; /* packets pending */ - /* - * Only radios in the same group can communicate together (the - * channel has to match too). Each bit represents a group. A - * radio can be in more than one group. - */ - u64 group; - - /* group shared by radios created in the same netns */ - int netgroup; - /* wmediumd portid responsible for netgroup of this radio */ - u32 wmediumd; - - /* difference between this hw's clock and the real clock, in usecs */ - s64 tsf_offset; - s64 bcn_delta; - /* absolute beacon transmission time. Used to cover up "tx" delay. */ - u64 abs_bcn_ts; - - /* Stats */ - u64 tx_pkts; - u64 rx_pkts; - u64 tx_bytes; - u64 rx_bytes; - u64 tx_dropped; - u64 tx_failed; - - /* RSSI in rx status of the receiver */ - int rx_rssi; - - /* only used when pmsr capability is supplied */ - struct cfg80211_pmsr_capabilities pmsr_capa; - struct cfg80211_pmsr_request *pmsr_request; - struct wireless_dev *pmsr_request_wdev; - - struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; - - struct ieee80211_vif *nan_device_vif; - u8 nan_bands; - - enum nl80211_band nan_curr_dw_band; - struct hrtimer nan_timer; - bool notify_dw; - struct ieee80211_vif *nan_vif; -}; - static const struct rhashtable_params hwsim_rht_params = { .nelem_hint = 2, .automatic_shrinking = true, @@ -848,6 +764,17 @@ hwsim_ftm_result_policy[NL80211_PMSR_FTM_RESP_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD] = { .type = NLA_U64 }, [NL80211_PMSR_FTM_RESP_ATTR_LCI] = { .type = NLA_STRING }, [NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_STRING }, + [NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS] = { .type = NLA_U8 }, + [NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS] = { .type = NLA_U8 }, + [NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR] = { .type = NLA_FLAG }, }; static const struct nla_policy @@ -881,6 +808,19 @@ hwsim_pmsr_peers_result_policy[NL80211_PMSR_ATTR_MAX + 1] = { [NL80211_PMSR_ATTR_PEERS] = NLA_POLICY_NESTED_ARRAY(hwsim_pmsr_peer_result_policy), }; +static const struct nla_policy +hwsim_ftm_role_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = { + [NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA] = { .type = NLA_FLAG }, +}; + +static const struct nla_policy +hwsim_ftm_type_capa_policy[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1] = { + [NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT] = { .type = NLA_FLAG }, +}; + static const struct nla_policy hwsim_ftm_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_CAPA_ATTR_ASAP] = { .type = NLA_FLAG }, @@ -893,6 +833,19 @@ hwsim_ftm_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST] = NLA_POLICY_MAX(NLA_U8, 31), [NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED] = { .type = NLA_FLAG }, [NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS] = { .type = NLA_U8 }, + [NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS] = { .type = NLA_U8 }, + [NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS] = + NLA_POLICY_NESTED(hwsim_ftm_role_capa_policy), + [NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS] = + NLA_POLICY_NESTED(hwsim_ftm_role_capa_policy), + [NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS] = + NLA_POLICY_NESTED(hwsim_ftm_type_capa_policy), + [NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT] = { .type = NLA_FLAG }, }; static const struct nla_policy @@ -1022,10 +975,6 @@ static int hwsim_get_chanwidth(enum nl80211_chan_width bw) return INT_MAX; } -static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan); - /* sysfs attributes */ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) { @@ -1313,22 +1262,45 @@ static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static inline u64 mac80211_hwsim_get_tsf_raw(void) +static inline u64 mac80211_hwsim_get_sim_tsf(void) { - return ktime_to_us(ktime_get_real()); + return ktime_to_us(ktime_get_boottime()); +} + +ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, + u64 tsf) +{ + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return us_to_ktime(tsf - data->tsf_offset); + } +} + +u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, + ktime_t ts) +{ + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return ktime_to_us(ts) + data->tsf_offset; + } +} + +u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = hw->priv; + u64 sim_time = mac80211_hwsim_get_sim_tsf(); + + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return sim_time + data->tsf_offset; + } } static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) { - u64 now = mac80211_hwsim_get_tsf_raw(); - return cpu_to_le64(now + data->tsf_offset); -} + u64 sim_time = mac80211_hwsim_get_sim_tsf(); -static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = hw->priv; - return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return cpu_to_le64(sim_time + data->tsf_offset); + } } static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, @@ -1336,8 +1308,6 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, { struct mac80211_hwsim_data *data = hw->priv; u64 now = mac80211_hwsim_get_tsf(hw, vif); - /* MLD not supported here */ - u32 bcn_int = data->link_data[0].beacon_int; u64 delta = abs(tsf - now); struct ieee80211_bss_conf *conf; @@ -1345,13 +1315,12 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, if (conf && !conf->enable_beacon) return; - /* adjust after beaconing with new timestamp at old TBTT */ - if (tsf > now) { - data->tsf_offset += delta; - data->bcn_delta = do_div(delta, bcn_int); - } else { - data->tsf_offset -= delta; - data->bcn_delta = -(s64)do_div(delta, bcn_int); + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + /* adjust after beaconing with new timestamp at old TBTT */ + if (tsf > now) + data->tsf_offset += delta; + else + data->tsf_offset -= delta; } } @@ -1622,6 +1591,47 @@ static inline u16 trans_tx_rate_flags_ieee2hwsim(struct ieee80211_tx_rate *rate) return result; } +static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data, + struct sk_buff *skb, u64 sim_time) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_rate *txrate; + /* TODO: get MCS */ + int bitrate = 100; + + spin_lock_bh(&data->tsf_offset_lock); + + txrate = ieee80211_get_tx_rate(data->hw, info); + if (txrate) + bitrate = txrate->bitrate; + + if (skb->len >= offsetofend(typeof(*mgmt), u.probe_resp.timestamp) && + ieee80211_is_probe_resp(hdr->frame_control)) { + mgmt->u.probe_resp.timestamp = + cpu_to_le64(sim_time + data->tsf_offset + + 24 * 8 * 10 / bitrate); + } else if (skb->len >= offsetofend(typeof(*mgmt), u.beacon.timestamp) && + ieee80211_is_beacon(mgmt->frame_control)) { + mgmt->u.beacon.timestamp = cpu_to_le64(sim_time + + data->tsf_offset + + 24 * 8 * 10 / + bitrate); + } else if (skb->len >= offsetofend(struct ieee80211_ext, + u.s1g_beacon.timestamp) && + ieee80211_is_s1g_beacon(mgmt->frame_control)) { + struct ieee80211_ext *ext = (void *)mgmt; + + ext->u.s1g_beacon.timestamp = cpu_to_le32(sim_time + + data->tsf_offset + + 10 * 8 * 10 / + bitrate); + } + + spin_unlock_bh(&data->tsf_offset_lock); +} + static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct sk_buff *my_skb, int dst_portid, @@ -1637,6 +1647,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; struct hwsim_tx_rate_flag tx_attempts_flags[IEEE80211_TX_MAX_RATES]; uintptr_t cookie; + u64 sim_tsf; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); @@ -1649,6 +1660,9 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, } } + sim_tsf = mac80211_hwsim_get_sim_tsf(); + mac80211_hwsim_write_tsf(data, my_skb, sim_tsf); + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (skb == NULL) goto nla_put_failure; @@ -1745,6 +1759,8 @@ static bool hwsim_chans_compat(struct ieee80211_channel *c1, struct tx_iter_data { struct ieee80211_channel *channel; + struct ieee80211_rx_status *rx_status; + struct ieee80211_hw *hw; bool receive; }; @@ -1754,13 +1770,11 @@ static void mac80211_hwsim_tx_iter(void *_data, u8 *addr, struct tx_iter_data *data = _data; int i; - /* For NAN Device simulation purposes, assume that NAN is always - * on channel 6 or channel 149. - */ - if (vif->type == NL80211_IFTYPE_NAN) { - data->receive = (data->channel && - (data->channel->center_freq == 2437 || - data->channel->center_freq == 5745)); + if (vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA) { + data->receive = mac80211_hwsim_nan_receive(data->hw, + data->channel, + data->rx_status); return; } @@ -1866,6 +1880,9 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data, mac80211_hwsim_add_vendor_rtap(skb); + if (data->nan.device_vif) + mac80211_hwsim_nan_rx(data->hw, skb); + data->rx_pkts++; data->rx_bytes += skb->len; ieee80211_rx_irqsafe(data->hw, skb); @@ -1880,7 +1897,11 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rx_status rx_status; - u64 now; + u64 sim_tsf = mac80211_hwsim_get_sim_tsf(); + + mac80211_hwsim_write_tsf(data, skb, sim_tsf); + + mac80211_hwsim_monitor_rx(hw, skb, chan); memset(&rx_status, 0, sizeof(rx_status)); rx_status.flag |= RX_FLAG_MACTIME_START; @@ -1923,20 +1944,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, skb_ext_reset(skb); nf_reset_ct(skb); - /* - * Get absolute mactime here so all HWs RX at the "same time", and - * absolute TX time for beacon mactime so the timestamp matches. - * Giving beacons a different mactime than non-beacons looks messy, but - * it helps the Toffset be exact and a ~10us mactime discrepancy - * probably doesn't really matter. - */ if (ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control)) { + ieee80211_is_probe_resp(hdr->frame_control)) rx_status.boottime_ns = ktime_get_boottime_ns(); - now = data->abs_bcn_ts; - } else { - now = mac80211_hwsim_get_tsf_raw(); - } /* Copy skb to all enabled radios that are on the current frequency */ spin_lock(&hwsim_radio_lock); @@ -1944,7 +1954,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct sk_buff *nskb; struct tx_iter_data tx_iter_data = { .receive = false, + .hw = data2->hw, .channel = chan, + .rx_status = &rx_status, }; if (data == data2) @@ -1960,6 +1972,12 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (data->netgroup != data2->netgroup) continue; + /* + * Set mactime early since NAN RX filtering relies on it + * for slot calculation + */ + rx_status.mactime = sim_tsf + data2->tsf_offset; + if (!hwsim_chans_compat(chan, data2->tmp_chan) && !hwsim_chans_compat(chan, data2->channel)) { ieee80211_iterate_active_interfaces_atomic( @@ -1996,8 +2014,6 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; - rx_status.mactime = now + data2->tsf_offset; - mac80211_hwsim_rx(data2, &rx_status, nskb); } spin_unlock(&hwsim_radio_lock); @@ -2104,22 +2120,19 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, /* re-assign hdr since skb data may have shifted after encryption */ hdr = (void *)skb->data; - if (vif && vif->type == NL80211_IFTYPE_NAN && !data->tmp_chan) { - /* For NAN Device simulation purposes, assume that NAN is always - * on channel 6 or channel 149, unless a ROC is in progress (for - * USD use cases). - */ - if (data->nan_curr_dw_band == NL80211_BAND_2GHZ) - channel = ieee80211_get_channel(hw->wiphy, 2437); - else if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) - channel = ieee80211_get_channel(hw->wiphy, 5745); - else - channel = NULL; + if (vif && !data->tmp_chan && + (vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA)) { + struct cfg80211_chan_def chandef; - if (WARN_ON(!channel)) { + mac80211_hwsim_nan_get_tx_chandef(hw, &chandef); + if (WARN_ON(!chandef.chan)) { + /* No valid channel in current slot, drop frame */ ieee80211_free_txskb(hw, skb); return; } + channel = chandef.chan; + confbw = chandef.width; } else if (!data->use_chanctx) { channel = data->channel; confbw = data->bw; @@ -2236,27 +2249,6 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return; } - if (skb->len >= 24 + 8 && - ieee80211_is_probe_resp(hdr->frame_control)) { - /* fake header transmission time */ - struct ieee80211_mgmt *mgmt; - struct ieee80211_rate *txrate; - /* TODO: get MCS */ - int bitrate = 100; - u64 ts; - - mgmt = (struct ieee80211_mgmt *)skb->data; - txrate = ieee80211_get_tx_rate(hw, txi); - if (txrate) - bitrate = txrate->bitrate; - ts = mac80211_hwsim_get_tsf_raw(); - mgmt->u.probe_resp.timestamp = - cpu_to_le64(ts + data->tsf_offset + - 24 * 8 * 10 / bitrate); - } - - mac80211_hwsim_monitor_rx(hw, skb, channel); - /* wmediumd mode check */ _portid = READ_ONCE(data->wmediumd); @@ -2282,6 +2274,22 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, ieee80211_tx_status_irqsafe(hw, skb); } +void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct ieee80211_tx_control control = { + .sta = txq->sta, + }; + struct sk_buff *skb; + + if ((txq->vif->type == NL80211_IFTYPE_NAN || + txq->vif->type == NL80211_IFTYPE_NAN_DATA) && + !mac80211_hwsim_nan_txq_transmitting(hw, txq)) + return; + + while ((skb = ieee80211_tx_dequeue(hw, txq))) + mac80211_hwsim_tx(hw, &control, skb); +} static int mac80211_hwsim_start(struct ieee80211_hw *hw) { @@ -2376,9 +2384,9 @@ static void mac80211_hwsim_remove_interface( mac80211_hwsim_config_mac_nl(hw, vif->addr, false); } -static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan) +void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan) { struct mac80211_hwsim_data *data = hw->priv; u32 _portid = READ_ONCE(data->wmediumd); @@ -2390,8 +2398,6 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, ARRAY_SIZE(txi->control.rates)); } - mac80211_hwsim_monitor_rx(hw, skb, chan); - if (_portid || hwsim_virtio_enabled) return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, chan); @@ -2409,10 +2415,6 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf, { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct ieee80211_tx_info *info; - struct ieee80211_rate *txrate; - struct ieee80211_mgmt *mgmt; - /* TODO: get MCS */ - int bitrate = 100; if (vp->skip_beacons[link_conf->link_id]) { vp->skip_beacons[link_conf->link_id]--; @@ -2426,27 +2428,6 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf, info->control.rates, ARRAY_SIZE(info->control.rates)); - txrate = ieee80211_get_tx_rate(hw, info); - if (txrate) - bitrate = txrate->bitrate; - - mgmt = (struct ieee80211_mgmt *) skb->data; - /* fake header transmission time */ - data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw(); - if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { - struct ieee80211_ext *ext = (void *) mgmt; - - ext->u.s1g_beacon.timestamp = cpu_to_le32(data->abs_bcn_ts + - data->tsf_offset + - 10 * 8 * 10 / - bitrate); - } else { - mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts + - data->tsf_offset + - 24 * 8 * 10 / - bitrate); - } - mac80211_hwsim_tx_frame(hw, skb, rcu_dereference(link_conf->chanctx_conf)->def.chan); } @@ -2523,7 +2504,9 @@ mac80211_hwsim_beacon(struct hrtimer *timer) container_of(link_data, struct mac80211_hwsim_data, link_data[link_data->link_id]); struct ieee80211_hw *hw = data->hw; - u64 bcn_int = link_data->beacon_int; + u32 remainder; + u64 tsf_now; + u64 tbtt; if (!data->started) return HRTIMER_NORESTART; @@ -2532,13 +2515,19 @@ mac80211_hwsim_beacon(struct hrtimer *timer) hw, IEEE80211_IFACE_ITER_NORMAL, mac80211_hwsim_beacon_tx, link_data); - /* beacon at new TBTT + beacon interval */ - if (data->bcn_delta) { - bcn_int -= data->bcn_delta; - data->bcn_delta = 0; - } - hrtimer_forward_now(&link_data->beacon_timer, - ns_to_ktime(bcn_int * NSEC_PER_USEC)); + /* TSF is the same for all VIFs, parameter is unused */ + tsf_now = mac80211_hwsim_get_tsf(hw, NULL); + + /* Wrap value to be after the next TBTT */ + tbtt = tsf_now + link_data->beacon_int; + + /* Round TBTT down to the correct time */ + div_u64_rem(tbtt, link_data->beacon_int, &remainder); + tbtt = tbtt - remainder; + + hrtimer_set_expires(&link_data->beacon_timer, + mac80211_hwsim_tsf_to_boottime(data, tbtt)); + return HRTIMER_RESTART; } @@ -2689,6 +2678,9 @@ static void mac80211_hwsim_vif_info_changed(struct ieee80211_hw *hw, vp->aid = vif->cfg.aid; } + if (changed & BSS_CHANGED_NAN_LOCAL_SCHED) + mac80211_hwsim_nan_local_sched_changed(hw, vif); + if (vif->type == NL80211_IFTYPE_STATION && changed & (BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM)) { u16 usable_links = ieee80211_vif_usable_links(vif); @@ -2789,6 +2781,10 @@ mac80211_hwsim_sta_rc_update(struct ieee80211_hw *hw, u32 bw = U32_MAX; int link_id; + if (vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA) + return; + rcu_read_lock(); for (link_id = 0; link_id < ARRAY_SIZE(vif->link_conf); @@ -2846,7 +2842,10 @@ static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, hwsim_check_magic(vif); hwsim_set_sta_magic(sta); - mac80211_hwsim_sta_rc_update(hw, vif, &sta->deflink, 0); + + /* For now, don't run RC update on STAs on an S1G interface */ + if (!vif->cfg.s1g) + mac80211_hwsim_sta_rc_update(hw, vif, &sta->deflink, 0); if (sta->valid_links) { WARN(hweight16(sta->valid_links) > 1, @@ -2855,6 +2854,8 @@ static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, sp->active_links_rx = sta->valid_links; } + spin_lock_init(&sp->nan_sched.lock); + return 0; } @@ -3046,11 +3047,7 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, goto nla_put_failure; return cfg80211_testmode_reply(skb); case HWSIM_TM_CMD_STOP_QUEUES: - ieee80211_stop_queues(hw); - return 0; case HWSIM_TM_CMD_WAKE_QUEUES: - ieee80211_wake_queues(hw); - return 0; default: return -EOPNOTSUPP; } @@ -3519,7 +3516,8 @@ static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw) static int mac80211_hwsim_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value) { - return -EOPNOTSUPP; + /* hwsim ignores the use_rts instruction from mac80211 anyway */ + return 0; } static int mac80211_hwsim_change_vif_links(struct ieee80211_hw *hw, @@ -3536,8 +3534,13 @@ static int mac80211_hwsim_change_vif_links(struct ieee80211_hw *hw, if (!new_links) add |= BIT(0); - for_each_set_bit(i, &rem, IEEE80211_MLD_MAX_NUM_LINKS) + wiphy_dbg(hw->wiphy, "%s:\n", __func__); + + for_each_set_bit(i, &rem, IEEE80211_MLD_MAX_NUM_LINKS) { mac80211_hwsim_config_mac_nl(hw, old[i]->addr, false); + wiphy_dbg(hw->wiphy, + " link [%d/%pM] removed\n", i, old[i]->addr); + } for_each_set_bit(i, &add, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link_conf; @@ -3547,6 +3550,8 @@ static int mac80211_hwsim_change_vif_links(struct ieee80211_hw *hw, continue; mac80211_hwsim_config_mac_nl(hw, link_conf->addr, true); + wiphy_dbg(hw->wiphy, + " link [%d/%pM] added\n", i, link_conf->addr); } return 0; @@ -3623,6 +3628,47 @@ static int mac80211_hwsim_send_pmsr_ftm_request_peer(struct sk_buff *msg, if (nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR, request->bss_color)) return -ENOBUFS; + if (request->min_time_between_measurements && + nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS, + request->min_time_between_measurements)) + return -ENOBUFS; + + if (request->max_time_between_measurements && + nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS, + request->max_time_between_measurements)) + return -ENOBUFS; + + if (request->availability_window && + nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION, + request->availability_window)) + return -ENOBUFS; + + if (request->nominal_time && + nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME, + request->nominal_time)) + return -ENOBUFS; + + if (request->num_measurements && + nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS, + request->num_measurements)) + return -ENOBUFS; + + if (request->ingress_distance && + nla_put_u64_64bit(msg, NL80211_PMSR_FTM_REQ_ATTR_INGRESS, + request->ingress_distance, + NL80211_PMSR_FTM_REQ_ATTR_PAD)) + return -ENOBUFS; + + if (request->egress_distance && + nla_put_u64_64bit(msg, NL80211_PMSR_FTM_REQ_ATTR_EGRESS, + request->egress_distance, + NL80211_PMSR_FTM_REQ_ATTR_PAD)) + return -ENOBUFS; + + if (request->pd_suppress_range_results && + nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS)) + return -ENOBUFS; + nla_nest_end(msg, ftm); return 0; @@ -3992,6 +4038,69 @@ static int mac80211_hwsim_parse_ftm_result(struct nlattr *ftm, result->civicloc_len = nla_len(tb[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]); } + if (tb[NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT]) { + result->tx_ltf_repetition_count_valid = 1; + result->tx_ltf_repetition_count = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT]) { + result->rx_ltf_repetition_count_valid = 1; + result->rx_ltf_repetition_count = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]) { + result->max_time_between_measurements_valid = 1; + result->max_time_between_measurements = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]) { + result->min_time_between_measurements_valid = 1; + result->min_time_between_measurements = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS]) { + result->num_tx_spatial_streams_valid = 1; + result->num_tx_spatial_streams = + nla_get_u8(tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS]) { + result->num_rx_spatial_streams_valid = 1; + result->num_rx_spatial_streams = + nla_get_u8(tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME]) { + result->nominal_time_valid = 1; + result->nominal_time = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW]) { + result->availability_window_valid = 1; + result->availability_window = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH]) { + result->chan_width_valid = 1; + result->chan_width = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH]); + } + + if (tb[NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE]) { + result->preamble_valid = 1; + result->preamble = + nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE]); + } + + result->is_delayed_lmr = + nla_get_flag(tb[NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR]); + return 0; } @@ -4127,168 +4236,6 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info) return err; } -static enum hrtimer_restart -mac80211_hwsim_nan_dw_start(struct hrtimer *timer) -{ - struct mac80211_hwsim_data *data = - container_of(timer, struct mac80211_hwsim_data, - nan_timer); - struct ieee80211_hw *hw = data->hw; - u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf; - u32 dw_int = 512 * 1024; - u64 until_dw; - - if (!data->nan_device_vif) - return HRTIMER_NORESTART; - - if (data->nan_bands & BIT(NL80211_BAND_5GHZ)) { - if (data->nan_curr_dw_band == NL80211_BAND_2GHZ) { - dw_int = 128 * 1024; - data->nan_curr_dw_band = NL80211_BAND_5GHZ; - } else if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) { - data->nan_curr_dw_band = NL80211_BAND_2GHZ; - } - } - - until_dw = dw_int - do_div(tsf, dw_int); - - /* The timer might fire just before the actual DW, in which case - * update the timeout to the actual next DW - */ - if (until_dw < dw_int / 2) - until_dw += dw_int; - - /* The above do_div() call directly modifies the 'tsf' variable, thus, - * use a copy so that the print below would show the original TSF. - */ - wiphy_debug(hw->wiphy, - "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n", - __func__, orig_tsf, data->nan_curr_dw_band, - until_dw); - - hrtimer_forward_now(&data->nan_timer, - ns_to_ktime(until_dw * NSEC_PER_USEC)); - - if (data->notify_dw) { - struct ieee80211_channel *ch; - struct wireless_dev *wdev = - ieee80211_vif_to_wdev(data->nan_device_vif); - - if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) - ch = ieee80211_get_channel(hw->wiphy, 5745); - else - ch = ieee80211_get_channel(hw->wiphy, 2437); - - cfg80211_next_nan_dw_notif(wdev, ch, GFP_ATOMIC); - } - - return HRTIMER_RESTART; -} - -static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_nan_conf *conf) -{ - struct mac80211_hwsim_data *data = hw->priv; - u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); - u32 dw_int = 512 * 1000; - u64 until_dw = dw_int - do_div(tsf, dw_int); - struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); - - if (vif->type != NL80211_IFTYPE_NAN) - return -EINVAL; - - if (data->nan_device_vif) - return -EALREADY; - - /* set this before starting the timer, as preemption might occur */ - data->nan_device_vif = vif; - data->nan_bands = conf->bands; - data->nan_curr_dw_band = NL80211_BAND_2GHZ; - - wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n", - until_dw); - - hrtimer_start(&data->nan_timer, - ns_to_ktime(until_dw * NSEC_PER_USEC), - HRTIMER_MODE_REL_SOFT); - - if (!is_zero_ether_addr(conf->cluster_id) && - is_zero_ether_addr(hwsim_nan_cluster_id)) { - memcpy(hwsim_nan_cluster_id, conf->cluster_id, ETH_ALEN); - } else if (is_zero_ether_addr(hwsim_nan_cluster_id)) { - hwsim_nan_cluster_id[0] = 0x50; - hwsim_nan_cluster_id[1] = 0x6f; - hwsim_nan_cluster_id[2] = 0x9a; - hwsim_nan_cluster_id[3] = 0x01; - hwsim_nan_cluster_id[4] = get_random_u8(); - hwsim_nan_cluster_id[5] = get_random_u8(); - } - - data->notify_dw = conf->enable_dw_notification; - - cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true, - GFP_KERNEL); - - return 0; -} - -static int mac80211_hwsim_stop_nan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = hw->priv; - struct mac80211_hwsim_data *data2; - bool nan_cluster_running = false; - - if (vif->type != NL80211_IFTYPE_NAN || !data->nan_device_vif || - data->nan_device_vif != vif) - return -EINVAL; - - hrtimer_cancel(&data->nan_timer); - data->nan_device_vif = NULL; - - spin_lock_bh(&hwsim_radio_lock); - list_for_each_entry(data2, &hwsim_radios, list) { - if (data2->nan_device_vif) { - nan_cluster_running = true; - break; - } - } - spin_unlock_bh(&hwsim_radio_lock); - - if (!nan_cluster_running) - memset(hwsim_nan_cluster_id, 0, ETH_ALEN); - - return 0; -} - -static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_nan_conf *conf, - u32 changes) -{ - struct mac80211_hwsim_data *data = hw->priv; - - if (vif->type != NL80211_IFTYPE_NAN) - return -EINVAL; - - if (!data->nan_device_vif) - return -EINVAL; - - wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes); - - /* Handle only the changes we care about for simulation purposes */ - if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) { - data->nan_bands = conf->bands; - data->nan_curr_dw_band = NL80211_BAND_2GHZ; - } - - if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG) - data->notify_dw = conf->enable_dw_notification; - - return 0; -} - static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw, struct cfg80211_chan_def *chan) { @@ -4316,7 +4263,7 @@ static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw, #define HWSIM_COMMON_OPS \ .tx = mac80211_hwsim_tx, \ - .wake_tx_queue = ieee80211_handle_wake_tx_queue, \ + .wake_tx_queue = ieee80211_hwsim_wake_tx_queue, \ .start = mac80211_hwsim_start, \ .stop = mac80211_hwsim_stop, \ .add_interface = mac80211_hwsim_add_interface, \ @@ -4339,11 +4286,13 @@ static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw, .get_et_strings = mac80211_hwsim_get_et_strings, \ .start_pmsr = mac80211_hwsim_start_pmsr, \ .abort_pmsr = mac80211_hwsim_abort_pmsr, \ - .start_nan = mac80211_hwsim_start_nan, \ - .stop_nan = mac80211_hwsim_stop_nan, \ - .nan_change_conf = mac80211_hwsim_change_nan_config, \ .set_radar_background = mac80211_hwsim_set_radar_background, \ .set_key = mac80211_hwsim_set_key, \ + .set_rts_threshold = mac80211_hwsim_set_rts_threshold, \ + .start_nan = mac80211_hwsim_nan_start, \ + .stop_nan = mac80211_hwsim_nan_stop, \ + .nan_change_conf = mac80211_hwsim_nan_change_config, \ + .nan_peer_sched_changed = mac80211_hwsim_nan_peer_sched_changed, \ HWSIM_DEBUGFS_OPS #define HWSIM_NON_MLO_OPS \ @@ -4385,7 +4334,6 @@ static const struct ieee80211_ops mac80211_hwsim_mchan_ops = { static const struct ieee80211_ops mac80211_hwsim_mlo_ops = { HWSIM_COMMON_OPS HWSIM_CHANCTX_OPS - .set_rts_threshold = mac80211_hwsim_set_rts_threshold, .change_vif_links = mac80211_hwsim_change_vif_links, .change_sta_links = mac80211_hwsim_change_sta_links, .sta_state = mac80211_hwsim_sta_state, @@ -4642,8 +4590,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, { @@ -4755,8 +4706,12 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, #ifdef CONFIG_MAC80211_MESH @@ -4929,8 +4884,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, { @@ -5059,8 +5017,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX), }, }, #ifdef CONFIG_MAC80211_MESH @@ -5257,8 +5218,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, { @@ -5408,8 +5372,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX), }, }, #ifdef CONFIG_MAC80211_MESH @@ -5504,13 +5471,105 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + }, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, #endif }; +#define HWSIM_VHT_MCS_MAP \ + (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \ + IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) + +static const struct ieee80211_sta_ht_cap hwsim_nan_ht_cap = { + .ht_supported = true, + .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40, + .ampdu_factor = 0x3, + .ampdu_density = 0x6, + .mcs = { + .rx_mask = { 0xff, 0xff }, + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, +}; + +static const struct ieee80211_sta_vht_cap hwsim_nan_vht_cap = { + .vht_supported = true, + .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + .vht_mcs = { + .rx_mcs_map = cpu_to_le16(HWSIM_VHT_MCS_MAP), + .tx_mcs_map = cpu_to_le16(HWSIM_VHT_MCS_MAP), + }, +}; + +static const struct ieee80211_sta_he_cap hwsim_nan_he_cap = { + .has_he = true, + .he_cap_elem = { + .mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE, + .mac_cap_info[1] = + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | + IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, + .mac_cap_info[2] = + IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_MU_CASCADING | + IEEE80211_HE_MAC_CAP2_ACK_EN, + .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3, + .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU, + .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 | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G, + .phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | + IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, + .phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, + + /* + * Leave all the other PHY capability bytes + * unset, as DCM, beam forming, RU and PPE + * threshold information are not supported + */ + }, + .he_mcs_nss_supp = { + .rx_mcs_80 = cpu_to_le16(0xfffa), + .tx_mcs_80 = cpu_to_le16(0xfffa), + .rx_mcs_160 = cpu_to_le16(0xfffa), + .tx_mcs_160 = cpu_to_le16(0xfffa), + .rx_mcs_80p80 = cpu_to_le16(0xfffa), + .tx_mcs_80p80 = cpu_to_le16(0xfffa), + }, +}; + static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband) { switch (sband->band) { @@ -5713,12 +5772,37 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->nan_capa.n_antennas = 0x22; hw->wiphy->nan_capa.max_channel_switch_time = 0; - hw->wiphy->nan_capa.dev_capabilities = - NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED | - NAN_DEV_CAPA_NDPE_SUPPORTED; - hrtimer_setup(&data->nan_timer, mac80211_hwsim_nan_dw_start, - CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT); + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_SECURE_NAN); + + hrtimer_setup(&data->nan.slot_timer, + mac80211_hwsim_nan_slot_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); + hrtimer_setup(&data->nan.resume_txqs_timer, + mac80211_hwsim_nan_resume_txqs_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); + hrtimer_setup(&data->nan.discovery_beacon_timer, + mac80211_hwsim_nan_discovery_beacon_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); + + spin_lock_init(&data->nan.state_lock); + } + + if (param->iftypes & BIT(NL80211_IFTYPE_NAN_DATA)) { + data->if_limits[n_limits].max = 2; + data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_NAN_DATA); + n_limits++; + + hw->wiphy->nan_capa.phy.ht = hwsim_nan_ht_cap; + hw->wiphy->nan_capa.phy.vht = hwsim_nan_vht_cap; + hw->wiphy->nan_capa.phy.he = hwsim_nan_he_cap; + + /* + * NAN switches between bands/channels per its schedule, + * so mac80211 rate control can't work here. + */ + ieee80211_hw_set(hw, HAS_RATE_CONTROL); } data->if_combination.radar_detect_widths = @@ -5772,6 +5856,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->mbssid_max_interfaces = 8; hw->wiphy->ema_max_profile_periodicity = 3; + spin_lock_init(&data->tsf_offset_lock); + data->rx_rssi = DEFAULT_RX_RSSI; INIT_DELAYED_WORK(&data->roc_start, hw_roc_start); @@ -5794,6 +5880,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, TDLS_WIDER_BW); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); ieee80211_hw_set(hw, STRICT); + ieee80211_hw_set(hw, BUFF_MMPDU_TXQ); + ieee80211_hw_set(hw, STA_MMPDU_TXQ); if (param->mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; @@ -5816,7 +5904,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_SUPPORTS_5_10_MHZ | WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR | @@ -5852,6 +5939,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->vif_data_size = sizeof(struct hwsim_vif_priv); hw->sta_data_size = sizeof(struct hwsim_sta_priv); hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); + hw->txq_data_size = 0; memcpy(data->channels_2ghz, hwsim_channels_2ghz, sizeof(hwsim_channels_2ghz)); @@ -5895,14 +5983,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, IEEE80211_VHT_CAP_RXSTBC_4 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; sband->vht_cap.vht_mcs.rx_mcs_map = - cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 14); + cpu_to_le16(HWSIM_VHT_MCS_MAP); sband->vht_cap.vht_mcs.tx_mcs_map = sband->vht_cap.vht_mcs.rx_mcs_map; break; @@ -5928,7 +6009,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; + IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_TX_STBC | + IEEE80211_HT_CAP_RX_STBC; sband->ht_cap.ampdu_factor = 0x3; sband->ht_cap.ampdu_density = 0x6; memset(&sband->ht_cap.mcs, 0, @@ -6294,6 +6377,8 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, if (!found) goto out; + mac80211_hwsim_monitor_rx(data2->hw, skb, data2->channel); + /* Tx info received because the frame was broadcasted on user space, so we get all the necessary info: tx attempts and skb control buff */ @@ -6394,7 +6479,10 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, /* A frame is received from user space */ memset(&rx_status, 0, sizeof(rx_status)); if (info->attrs[HWSIM_ATTR_FREQ]) { - struct tx_iter_data iter_data = {}; + struct tx_iter_data iter_data = { + .hw = data2->hw, + .rx_status = &rx_status, + }; /* throw away off-channel packets, but allow both the temporary * ("hw" scan/remain-on-channel), regular channels and links, @@ -6532,6 +6620,82 @@ static int parse_ftm_capa(const struct nlattr *ftm_capa, struct cfg80211_pmsr_ca out->ftm.trigger_based = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED]; out->ftm.non_trigger_based = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED]; + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]) + out->ftm.max_no_of_tx_antennas = + nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]) + out->ftm.max_no_of_rx_antennas = + nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]) + out->ftm.min_allowed_ranging_interval_edca = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]) + out->ftm.min_allowed_ranging_interval_ntb = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES]) + out->ftm.pd_preambles = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS]) + out->ftm.pd_bandwidths = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS]) { + struct nlattr *ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1]; + + if (!nla_parse_nested(ista_tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, + tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS], + hwsim_ftm_role_capa_policy, NULL)) { + out->ftm.ista.support_ntb = + !!ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB]; + out->ftm.ista.support_tb = + !!ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB]; + out->ftm.ista.support_edca = + !!ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA]; + if (ista_tb[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]) + out->ftm.ista.max_peers = + nla_get_u32(ista_tb[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]); + } + } + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS]) { + struct nlattr *rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1]; + + if (!nla_parse_nested(rsta_tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, + tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS], + hwsim_ftm_role_capa_policy, NULL)) { + out->ftm.rsta.support_ntb = + !!rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB]; + out->ftm.rsta.support_tb = + !!rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB]; + out->ftm.rsta.support_edca = + !!rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA]; + if (rsta_tb[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]) + out->ftm.rsta.max_peers = + nla_get_u32(rsta_tb[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]); + } + } + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS]) { + struct nlattr *type_tb[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1]; + + if (!nla_parse_nested(type_tb, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX, + tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS], + hwsim_ftm_type_capa_policy, NULL)) { + out->ftm.type.infra_support = + !!type_tb[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT]; + out->ftm.type.pd_support = + !!type_tb[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT]; + } + } + + out->ftm.concurrent_ista_rsta_support = + !!tb[NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT]; + return 0; } @@ -6657,11 +6821,14 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) param.p2p_device = true; } - /* ensure both flag and iftype support is honored */ - if (param.nan_device || - param.iftypes & BIT(NL80211_IFTYPE_NAN)) { - param.iftypes |= BIT(NL80211_IFTYPE_NAN); - param.nan_device = true; + if (param.nan_device) { + if (param.multi_radio) { + NL_SET_ERR_MSG(info->extack, + "NAN is not supported on multi-radio wiphys"); + return -EINVAL; + } + param.iftypes |= BIT(NL80211_IFTYPE_NAN) | + BIT(NL80211_IFTYPE_NAN_DATA); } if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) { @@ -7309,8 +7476,6 @@ static int __init init_mac80211_hwsim(void) if (err) goto out_exit_virtio; - hwsim_init_s1g_channels(hwsim_channels_s1g); - for (i = 0; i < radios; i++) { struct hwsim_new_radio_params param = { 0 }; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c new file mode 100644 index 000000000000..7be64c45babf --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -0,0 +1,1346 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim + * Copyright (C) 2025-2026 Intel Corporation + */ + +#include +#include "mac80211_hwsim_i.h" + +/* Defined as the lower 23 bits being zero */ +#define DW0_TSF_MASK GENMASK(22, 0) + +/* DWs are repeated every 512 TUs */ +#define DWST_TU 512 +#define DWST_TSF_MASK (ieee80211_tu_to_usec(DWST_TU) - 1) + +#define SLOT_TU 16 +#define SLOT_TSF_MASK (ieee80211_tu_to_usec(DWST_TU) - 1) + +/* The 2.4 GHz DW is at the start, the 5 GHz is in slot 8 (after 128 TUs) */ +#define DW_5G_OFFSET_TU 128 + +#define SLOT_24GHZ_DW 0 +#define SLOT_5GHZ_DW (DW_5G_OFFSET_TU / SLOT_TU) + +/* The special DW0 happens every 16 DWSTs (8192 TUs) */ +static_assert(16 * DWST_TU * 1024 == 8192 * 1024); +static_assert(DW0_TSF_MASK + 1 == 8192 * 1024); + +/* warmup phase should be 120 seconds, which is approximately 225 DWSTs */ +#define NAN_WARMUP_DWST 225 + +#define NAN_RSSI_CLOSE (-60) +#define NAN_RSSI_MIDDLE (-75) + +/* Quiet time at the end of each slot where TX is suppressed */ +#define NAN_CHAN_SWITCH_TIME_US 256 + +struct hwsim_nan_sta_iter_ctx { + struct ieee80211_hw *hw; + bool can_tx; +}; + +struct hwsim_nan_mcast_data_iter_ctx { + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + size_t n_vif_sta; + size_t n_sta_can_tx; +}; + +static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data); + +static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data) +{ + ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer); + + return mac80211_hwsim_boottime_to_tsf(data, expires); +} + +static u8 hwsim_nan_slot_from_tsf(u64 tsf) +{ + return (tsf & DWST_TSF_MASK) / ieee80211_tu_to_usec(SLOT_TU); +} + +static u64 hwsim_nan_encode_master_rank(u8 master_pref, u8 random_factor, + const u8 *addr) +{ + return ((u64)master_pref << 56) + + ((u64)random_factor << 48) + + ((u64)addr[5] << 40) + + ((u64)addr[4] << 32) + + ((u64)addr[3] << 24) + + ((u64)addr[2] << 16) + + ((u64)addr[1] << 8) + + ((u64)addr[0] << 0); +} + +static u64 hwsim_nan_get_master_rank(struct mac80211_hwsim_data *data) +{ + u8 master_pref = 0; + u8 random_factor = 0; + + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) { + master_pref = data->nan.master_pref; + random_factor = data->nan.random_factor; + } + + return hwsim_nan_encode_master_rank(master_pref, random_factor, + data->nan.device_vif->addr); +} + +static bool mac80211_hwsim_nan_is_dw_slot(struct mac80211_hwsim_data *data, + u8 slot) +{ + return slot == SLOT_24GHZ_DW || + (slot == SLOT_5GHZ_DW && + (data->nan.bands & BIT(NL80211_BAND_5GHZ))); +} + +static bool +hwsim_nan_rx_chandef_compatible(struct mac80211_hwsim_data *data, u8 slot, + struct ieee80211_channel *rx_chan, u8 rx_bw) +{ + static const int bw_to_mhz[] = { + [RATE_INFO_BW_20] = 20, [RATE_INFO_BW_40] = 40, + [RATE_INFO_BW_80] = 80, [RATE_INFO_BW_160] = 160, + }; + struct cfg80211_chan_def sched_chandef; + int rx_mhz, sched_mhz; + + scoped_guard(spinlock_bh, &data->nan.state_lock) + sched_chandef = data->nan.local_sched[slot]; + + if (!sched_chandef.chan || + sched_chandef.chan->center_freq != rx_chan->center_freq) + return false; + + if (rx_bw >= ARRAY_SIZE(bw_to_mhz) || !bw_to_mhz[rx_bw]) + return false; + + rx_mhz = bw_to_mhz[rx_bw]; + sched_mhz = cfg80211_chandef_get_width(&sched_chandef); + + /* Accept RX at narrower or equal bandwidth */ + return rx_mhz <= sched_mhz; +} + +static bool hwsim_nan_peer_present_in_dw(struct hwsim_sta_priv *sp, u64 tsf) +{ + u8 slot = hwsim_nan_slot_from_tsf(tsf); + u8 cdw = 0; + u8 dw_index, wake_interval; + u16 committed_dw; + + scoped_guard(spinlock_bh, &sp->nan_sched.lock) + committed_dw = sp->nan_sched.committed_dw; + + /* If peer doesn't advertise committed DW, assume presence in + * all 2.4 GHz DW slots + */ + if (!committed_dw) + return slot == SLOT_24GHZ_DW; + + /* Get DW index (0-15) within the 16-DWST DW0 cycle */ + dw_index = (tsf / ieee80211_tu_to_usec(DWST_TU)) & 0xf; + + /* Extract CDW for the appropriate band (spec Table 80) */ + if (slot == SLOT_24GHZ_DW) + cdw = committed_dw & 0x7; + else if (slot == SLOT_5GHZ_DW) + cdw = (committed_dw >> 3) & 0x7; + + if (cdw == 0) + return false; + + /* Peer wakes every 2^(cdw-1) DWs: 1, 2, 4, 8, or 16 */ + wake_interval = 1 << (cdw - 1); + + return (dw_index % wake_interval) == 0; +} + +static bool +hwsim_nan_peer_present_in_faw(struct hwsim_sta_priv *sp, + struct mac80211_hwsim_data *data, u8 slot) +{ + struct cfg80211_chan_def local_chandef; + + scoped_guard(spinlock_bh, &data->nan.state_lock) + local_chandef = data->nan.local_sched[slot]; + + if (!local_chandef.chan) + return false; + + scoped_guard(spinlock_bh, &sp->nan_sched.lock) { + for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) { + struct cfg80211_chan_def *peer_chandef; + + if (sp->nan_sched.maps[i].map_id == + CFG80211_NAN_INVALID_MAP_ID) + continue; + + peer_chandef = &sp->nan_sched.maps[i].chans[slot]; + if (!peer_chandef->chan) + continue; + + if (cfg80211_chandef_compatible(&local_chandef, + peer_chandef)) + return true; + } + } + + return false; +} + +static void +mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot, + bool discontinuity) +{ + u64 tsf; + + if (!discontinuity) + tsf = hwsim_nan_get_timer_tsf(data); + else + tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + + /* Only called by mac80211_hwsim_nan_dw_timer from softirq context */ + lockdep_assert_in_softirq(); + + tsf &= ~DWST_TSF_MASK; + tsf += ieee80211_tu_to_usec(slot * SLOT_TU); + + hrtimer_set_expires(&data->nan.slot_timer, + mac80211_hwsim_tsf_to_boottime(data, tsf)); +} + +void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct mac80211_hwsim_data *data = hw->priv; + const struct ieee80211_mgmt *mgmt = (void *)skb->data; + struct element *nan_elem = (void *)mgmt->u.beacon.variable; + struct ieee80211_nan_anchor_master_info *ami = NULL; + const struct ieee80211_nan_attr *nan_attr; + struct ieee80211_rx_status rx_status; + bool joined_cluster = false; + bool adopt_tsf = false; + bool is_sync_beacon; + bool is_same_cluster; + u64 master_rank = 0; + ssize_t data_len; + u8 slot; + + /* Need a NAN vendor element at the start */ + if (skb->len < (offsetofend(struct ieee80211_mgmt, u.beacon) + 6) || + !ieee80211_is_beacon(mgmt->frame_control)) + return; + + data_len = skb->len - offsetofend(struct ieee80211_mgmt, u.beacon); + + /* Copy the RX status to add a MAC timestamp if needed */ + memcpy(&rx_status, IEEE80211_SKB_RXCB(skb), + sizeof(struct ieee80211_rx_status)); + + /* And deal with the lack of mac time stamp */ + if ((rx_status.flag & RX_FLAG_MACTIME) != RX_FLAG_MACTIME_START) { + u64 tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);; + + /* In that case there should be no timestamp */ + WARN_ON_ONCE(rx_status.flag & RX_FLAG_MACTIME); + + /* No mac timestamp, set current TSF for the frame end */ + rx_status.flag |= RX_FLAG_MACTIME_END; + rx_status.mactime = tsf; + + /* And translate to the start for the rest of the code */ + rx_status.mactime = + ieee80211_calculate_rx_timestamp(hw, &rx_status, + skb->len, 0); + rx_status.flag &= ~RX_FLAG_MACTIME; + rx_status.flag |= RX_FLAG_MACTIME_START; + + /* Match mac80211_hwsim_nan_receive, see comment there */ + slot = hwsim_nan_slot_from_tsf(tsf + 128); + } else { + slot = hwsim_nan_slot_from_tsf(rx_status.mactime); + } + + /* + * (overly) simplify things, only track 2.4 GHz here. Also, ignore + * frames outside of the 2.4 GHz DW slot, unless in the initial SCAN + * phase. + */ + if ((slot != SLOT_24GHZ_DW && + data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN) || + rx_status.freq != 2437) + return; + + /* Just ignore low RSSI beacons that we cannot sync to */ + if (rx_status.signal < NAN_RSSI_MIDDLE) + return; + + /* Needs to be a valid NAN cluster ID in A3 */ + if (get_unaligned_be32(mgmt->bssid) != ((WLAN_OUI_WFA << 8) | 0x01)) + return; + + /* We are only interested in NAN beacons */ + if (nan_elem->id != WLAN_EID_VENDOR_SPECIFIC || + nan_elem->datalen < 4 || + get_unaligned_be32(nan_elem->data) != + (WLAN_OUI_WFA << 8 | WLAN_OUI_TYPE_WFA_NAN)) + return; + + u8 *nan_defragmented __free(kfree) = kzalloc(data_len, GFP_ATOMIC); + if (!nan_defragmented) + return; + + data_len = cfg80211_defragment_element(nan_elem, + mgmt->u.beacon.variable, + data_len, + nan_defragmented, data_len, + WLAN_EID_FRAGMENT); + + if (data_len < 0) + return; + + /* Assume it is a synchronization beacon if beacon_int is 512 TUs */ + is_sync_beacon = le16_to_cpu(mgmt->u.beacon.beacon_int) == DWST_TU; + is_same_cluster = ether_addr_equal(mgmt->bssid, data->nan.cluster_id); + + for_each_nan_attr(nan_attr, nan_defragmented + 4, data_len - 4) { + if (nan_attr->attr == NAN_ATTR_MASTER_INDICATION && + le16_to_cpu(nan_attr->length) >= + sizeof(struct ieee80211_nan_master_indication)) { + struct ieee80211_nan_master_indication *mi = + (void *)nan_attr->data; + + master_rank = + hwsim_nan_encode_master_rank(mi->master_pref, + mi->random_factor, + mgmt->sa); + } + + if (nan_attr->attr == NAN_ATTR_CLUSTER_INFO && + le16_to_cpu(nan_attr->length) >= + sizeof(struct ieee80211_nan_anchor_master_info)) { + ami = (void *)nan_attr->data; + + /* + * The AMBTT should be set to the beacon timestamp when + * the sender is the anchor master. We can simply + * modify the structure because we created a copy when + * defragmenting the NAN element. + */ + if (ami->hop_count == 0) + ami->ambtt = cpu_to_le32( + le64_to_cpu(mgmt->u.beacon.timestamp)); + } + } + + /* Do the rest of the processing under lock */ + spin_lock_bh(&data->nan.state_lock); + + /* + * sync beacon should be discarded if the master rank is the same + * and the AMBTT is older than 16 * 512 TUs compared to our own TSF. + * + * Subtract the AMBTT from the lowered TSF. If the AMBTT is older + * (smaller) then the calculation will not underflow. + */ + if (is_sync_beacon && ami && + ami->master_rank == data->nan.current_ami.master_rank && + (((u32)rx_status.mactime - + ieee80211_tu_to_usec(16 * 512)) - + le32_to_cpu(ami->ambtt)) < 0x8000000) { + wiphy_dbg(hw->wiphy, + "NAN: ignoring sync beacon with old AMBTT\n"); + is_sync_beacon = false; + } + + if (is_same_cluster && is_sync_beacon && + master_rank > hwsim_nan_get_master_rank(data)) { + if (rx_status.signal > NAN_RSSI_CLOSE) + data->nan.master_transition_score += 3; + else + data->nan.master_transition_score += 1; + } + + if (is_same_cluster && is_sync_beacon && ami && + ((ami->master_rank == data->nan.current_ami.master_rank && + ami->hop_count < data->nan.current_ami.hop_count) || + (master_rank > hwsim_nan_get_master_rank(data) && + ami->hop_count == data->nan.current_ami.hop_count))) { + if (rx_status.signal > NAN_RSSI_CLOSE) + data->nan.sync_transition_score += 3; + else + data->nan.sync_transition_score += 1; + } + + /* + * Decide on TSF adjustments before updating any other state + */ + if (is_same_cluster && is_sync_beacon && ami && + data->nan.current_ami.hop_count != 0) { + if (le64_to_cpu(ami->master_rank) > + le64_to_cpu(data->nan.current_ami.master_rank) && + ami->master_rank != data->nan.last_ami.master_rank) + adopt_tsf = true; + + if (le64_to_cpu(ami->master_rank) > + le64_to_cpu(data->nan.current_ami.master_rank) && + ami->master_rank == data->nan.last_ami.master_rank && + le32_to_cpu(ami->ambtt) > + le32_to_cpu(data->nan.last_ami.ambtt)) + adopt_tsf = true; + + if (le64_to_cpu(ami->master_rank) < + le64_to_cpu(data->nan.current_ami.master_rank) && + le64_to_cpu(ami->master_rank) > + hwsim_nan_get_master_rank(data) && + ether_addr_equal(ami->master_addr, + data->nan.current_ami.master_addr)) + adopt_tsf = true; + + if (ami->master_rank == data->nan.current_ami.master_rank && + le32_to_cpu(ami->ambtt) > + le32_to_cpu(data->nan.current_ami.ambtt)) + adopt_tsf = true; + + /* Anchor Master case is handled below */ + } + + /* + * NAN Cluster merging + */ + if (!is_same_cluster && ami) { + u64 curr_amr; + u64 own_cg; + u64 frame_amr; + u64 cg; + + /* Shifted down by 19 bits compared to spec */ + frame_amr = le64_to_cpu(ami->master_rank); + cg = (u64)ami->master_pref << (64 - 19); + cg += le64_to_cpu(mgmt->u.beacon.timestamp) >> 19; + + curr_amr = le64_to_cpu(data->nan.current_ami.master_rank); + own_cg = (u64)data->nan.current_ami.master_pref << (64 - 19); + own_cg += rx_status.mactime >> 19; + + /* + * Check if the cluster shall be joined + * + * When in the "scan" phase, just join immediately. + */ + if (cg > own_cg || + (cg == own_cg && frame_amr > curr_amr) || + data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) { + /* Avoid a state transition */ + data->nan.master_transition_score = 0; + data->nan.sync_transition_score = 0; + + /* + * NOTE: The spec says we should TX sync beacons on the + * old schedule after joining. We do not implement this. + */ + + wiphy_dbg(hw->wiphy, "NAN: joining cluster %pM\n", + mgmt->bssid); + + joined_cluster = true; + adopt_tsf = true; + + memcpy(&data->nan.last_ami, &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + + memcpy(data->nan.cluster_id, mgmt->bssid, ETH_ALEN); + + /* + * Assume we are UP if we joined a cluster. + * + * If the other anchor master is still in the warmup + * phase, then we may temporarily become the anchor + * master until it sets its own master preference to + * be non-zero. + */ + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP; + data->nan.random_factor_valid_dwst = 0; + } + } + + /* + * Anchor master selection + */ + /* We are not anchor master */ + if (is_same_cluster && is_sync_beacon && ami && + data->nan.current_ami.hop_count != 0) { + if (le64_to_cpu(data->nan.current_ami.master_rank) < + le64_to_cpu(ami->master_rank)) { + if (ami->master_rank == data->nan.last_ami.master_rank && + le32_to_cpu(ami->ambtt) <= + le32_to_cpu(data->nan.last_ami.ambtt)) { + /* disregard frame */ + } else { + memcpy(&data->nan.last_ami, + &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + } + } + + if (le64_to_cpu(data->nan.current_ami.master_rank) > + le64_to_cpu(ami->master_rank)) { + if (!ether_addr_equal(data->nan.current_ami.master_addr, + ami->master_addr)) { + /* disregard frame */ + } else { + u64 amr = hwsim_nan_get_master_rank(data); + + if (amr > le64_to_cpu(ami->master_rank)) { + /* assume ourselves as anchor master */ + wiphy_dbg(hw->wiphy, + "NAN: assume anchor master role\n"); + data->nan.current_ami.master_rank = + cpu_to_le64(amr); + data->nan.current_ami.hop_count = 0; + memset(&data->nan.last_ami, 0, + sizeof(data->nan.last_ami)); + data->nan.last_ami.ambtt = + data->nan.current_ami.ambtt; + data->nan.current_ami.ambtt = 0; + } else { + memcpy(&data->nan.last_ami, + &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + } + } + } + + if (data->nan.current_ami.master_rank == ami->master_rank) { + if (le32_to_cpu(data->nan.current_ami.ambtt) < + le32_to_cpu(ami->ambtt)) { + data->nan.current_ami.ambtt = ami->ambtt; + } + + if (data->nan.current_ami.hop_count > + ami->hop_count + 1) { + data->nan.current_ami.hop_count = + ami->hop_count + 1; + } + } + } + + /* We are anchor master */ + if (is_same_cluster && is_sync_beacon && ami && + data->nan.current_ami.hop_count == 0) { + WARN_ON_ONCE(!ether_addr_equal(data->nan.current_ami.master_addr, + data->nan.device_vif->addr)); + + if (le64_to_cpu(ami->master_rank) < + le64_to_cpu(data->nan.current_ami.master_rank) || + ether_addr_equal(ami->master_addr, + data->nan.current_ami.master_addr)) { + /* disregard */ + } else { + wiphy_dbg(hw->wiphy, "NAN: lost anchor master role\n"); + adopt_tsf = true; + memcpy(&data->nan.last_ami, &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + } + } + + if (adopt_tsf && !data->nan.tsf_adjusted) { + int threshold = 5; + s64 adjustment; + + /* Timestamp is likely inaccurate (and late) in this case */ + if (!(IEEE80211_SKB_RXCB(skb)->flag & RX_FLAG_MACTIME)) + threshold = 128; + + adjustment = + le64_to_cpu(mgmt->u.beacon.timestamp) - + ieee80211_calculate_rx_timestamp(hw, &rx_status, + skb->len, 24); + + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + if (adjustment < -threshold || adjustment > threshold) { + if (adjustment < -(s64)ieee80211_tu_to_usec(4) || + adjustment > (s64)ieee80211_tu_to_usec(4)) + data->nan.tsf_discontinuity = true; + + wiphy_debug(hw->wiphy, + "NAN: Adjusting TSF by +/- %d us or more: %lld us (discontinuity: %d, from: %pM, old offset: %lld)\n", + threshold, adjustment, + data->nan.tsf_discontinuity, mgmt->sa, + data->tsf_offset); + } else { + /* smooth things out a little bit */ + adjustment /= 2; + } + + /* + * Do the TSF adjustment + * The flag prevents further adjustments until the next + * 2.4 GHz DW starts to avoid race conditions for + * in-flight packets. + */ + data->nan.tsf_adjusted = true; + data->tsf_offset += adjustment; + } + } + + spin_unlock_bh(&data->nan.state_lock); + + if (joined_cluster) + ieee80211_nan_cluster_joined(data->nan.device_vif, + data->nan.cluster_id, false, + GFP_ATOMIC); +} + +static void +mac80211_hwsim_nan_exec_state_transitions(struct mac80211_hwsim_data *data) +{ + bool notify_join = false; + + /* + * Handle NAN role and state transitions at the end of the DW period + * in accordance to Wi-Fi Aware version 4.0 section 3.3.7 point 2, i.e. + * end of 5 GHz DW if enabled else at the end of the 2.4 GHz DW. + */ + + spin_lock(&data->nan.state_lock); + + /* Handle role transitions, Wi-Fi Aware version 4.0 section 3.3.6 */ + if (data->nan.master_transition_score < 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER; + else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER && + data->nan.master_transition_score >= 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC; + else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_SYNC && + data->nan.sync_transition_score >= 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_NON_SYNC; + else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_NON_SYNC && + data->nan.sync_transition_score < 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC; + + /* + * The discovery beacon timer will stop automatically. Make sure it is + * running if we are master. Do not bother with a proper alignment it + * will sync itself to the TSF after the first TX. + */ + if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER && + !hrtimer_active(&data->nan.discovery_beacon_timer)) + hrtimer_start(&data->nan.discovery_beacon_timer, + ns_to_ktime(10 * NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + + data->nan.master_transition_score = 0; + data->nan.sync_transition_score = 0; + + if (data->nan.random_factor_valid_dwst == 0) { + u64 amr; + + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) { + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_WARMUP; + data->nan.random_factor_valid_dwst = NAN_WARMUP_DWST; + + notify_join = true; + } else { + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP; + data->nan.random_factor_valid_dwst = + get_random_u32_inclusive(120, 240); + data->nan.random_factor = get_random_u8(); + } + + amr = hwsim_nan_get_master_rank(data); + + if (data->nan.current_ami.hop_count == 0) { + /* Update if we are already anchor master */ + data->nan.current_ami.master_rank = cpu_to_le64(amr); + } else if (le64_to_cpu(data->nan.current_ami.master_rank) < amr) { + /* assume role if we have a higher rank */ + wiphy_dbg(data->hw->wiphy, + "NAN: assume anchor master role\n"); + data->nan.current_ami.master_rank = cpu_to_le64(amr); + data->nan.current_ami.hop_count = 0; + memset(&data->nan.last_ami, 0, + sizeof(data->nan.last_ami)); + data->nan.last_ami.ambtt = data->nan.current_ami.ambtt; + data->nan.current_ami.ambtt = 0; + } + } else { + data->nan.random_factor_valid_dwst--; + } + + spin_unlock(&data->nan.state_lock); + + if (notify_join) + ieee80211_nan_cluster_joined(data->nan.device_vif, + data->nan.cluster_id, true, + GFP_ATOMIC); +} + +static void +mac80211_hwsim_nan_tx_beacon(struct mac80211_hwsim_data *data, + bool is_discovery, + struct ieee80211_channel *channel) +{ + struct ieee80211_vendor_ie nan_ie = { + .element_id = WLAN_EID_VENDOR_SPECIFIC, + .len = 27 - 2, + .oui = { u32_get_bits(WLAN_OUI_WFA, 0xff0000), + u32_get_bits(WLAN_OUI_WFA, 0xff00), + u32_get_bits(WLAN_OUI_WFA, 0xff) }, + .oui_type = WLAN_OUI_TYPE_WFA_NAN, + }; + size_t alloc_size = + IEEE80211_TX_STATUS_HEADROOM + + offsetofend(struct ieee80211_mgmt, u.beacon) + + 27 /* size of NAN vendor element */; + struct ieee80211_nan_master_indication master_indication; + struct ieee80211_nan_attr nan_attr; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + + /* + * TODO: Should the configured vendor elements or NAN attributes be + * included in some of these beacons? + */ + + skb = alloc_skb(alloc_size, GFP_ATOMIC); + if (!skb) + return; + + spin_lock(&data->nan.state_lock); + + skb_reserve(skb, IEEE80211_TX_STATUS_HEADROOM); + mgmt = skb_put(skb, offsetofend(struct ieee80211_mgmt, u.beacon)); + + memset(mgmt, 0, offsetofend(struct ieee80211_mgmt, u.beacon)); + memcpy(mgmt->sa, data->nan.device_vif->addr, ETH_ALEN); + memset(mgmt->da, 0xff, ETH_ALEN); + memcpy(mgmt->bssid, data->nan.cluster_id, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); + mgmt->u.beacon.beacon_int = cpu_to_le16(is_discovery ? 100 : DWST_TU); + mgmt->u.beacon.capab_info = + cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME | + WLAN_CAPABILITY_SHORT_PREAMBLE); + + /* FIXME: set these to saner values? */ + mgmt->duration = 0; + mgmt->seq_ctrl = 0; + + /* Put the NAN element */ + skb_put_data(skb, &nan_ie, sizeof(nan_ie)); + + nan_attr.attr = NAN_ATTR_MASTER_INDICATION; + nan_attr.length = cpu_to_le16(sizeof(master_indication)); + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) { + master_indication.master_pref = data->nan.master_pref; + master_indication.random_factor = data->nan.random_factor; + } else { + master_indication.master_pref = 0; + master_indication.random_factor = 0; + } + + skb_put_data(skb, &nan_attr, sizeof(nan_attr)); + skb_put_data(skb, &master_indication, sizeof(master_indication)); + + nan_attr.attr = NAN_ATTR_CLUSTER_INFO; + nan_attr.length = cpu_to_le16(sizeof(data->nan.current_ami)); + skb_put_data(skb, &nan_attr, sizeof(nan_attr)); + skb_put_data(skb, &data->nan.current_ami, + sizeof(data->nan.current_ami)); + + spin_unlock(&data->nan.state_lock); + + mac80211_hwsim_tx_frame(data->hw, skb, channel); +} + +enum hrtimer_restart +mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) +{ + struct mac80211_hwsim_data *data = + container_of(timer, struct mac80211_hwsim_data, + nan.slot_timer); + struct ieee80211_hw *hw = data->hw; + struct ieee80211_channel *notify_dw_chan = NULL; + struct ieee80211_channel *beacon_sync_chan = NULL; + u64 tsf = hwsim_nan_get_timer_tsf(data); + u8 slot = hwsim_nan_slot_from_tsf(tsf); + bool dwst_of_dw0 = false; + bool dw_end = false; + bool tx_sync_beacon; + + if (!data->nan.device_vif) + return HRTIMER_NORESTART; + + if ((tsf & DW0_TSF_MASK & ~DWST_TSF_MASK) == 0) + dwst_of_dw0 = true; + + + scoped_guard(spinlock, &data->nan.state_lock) { + if (data->nan.tsf_discontinuity) { + data->nan.tsf_discontinuity = false; + + mac80211_hwsim_nan_schedule_slot(data, 32, true); + + return HRTIMER_RESTART; + } + + if (slot == SLOT_24GHZ_DW) + data->nan.tsf_adjusted = false; + + tx_sync_beacon = + data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN && + data->nan.role != MAC80211_HWSIM_NAN_ROLE_NON_SYNC; + } + + switch (slot) { + case SLOT_24GHZ_DW: + wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n", + dwst_of_dw0); + beacon_sync_chan = ieee80211_get_channel(hw->wiphy, 2437); + break; + + case SLOT_24GHZ_DW + 1: + if (!(data->nan.bands & BIT(NL80211_BAND_5GHZ))) { + notify_dw_chan = ieee80211_get_channel(hw->wiphy, 2437); + dw_end = true; + } else { + notify_dw_chan = ieee80211_get_channel(hw->wiphy, 5745); + } + break; + + case SLOT_5GHZ_DW: + if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) { + wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n"); + beacon_sync_chan = + ieee80211_get_channel(hw->wiphy, 5745); + } + break; + + case SLOT_5GHZ_DW + 1: + if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) { + notify_dw_chan = + ieee80211_get_channel(hw->wiphy, 2437); + dw_end = true; + } + break; + } + + /* TODO: This does not implement DW contention mitigation */ + if (beacon_sync_chan && tx_sync_beacon) + mac80211_hwsim_nan_tx_beacon(data, false, beacon_sync_chan); + + if (dw_end) + mac80211_hwsim_nan_exec_state_transitions(data); + + if (data->nan.notify_dw && notify_dw_chan) { + struct wireless_dev *wdev = + ieee80211_vif_to_wdev(data->nan.device_vif); + + cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC); + } + + mac80211_hwsim_nan_resume_txqs(data); + + mac80211_hwsim_nan_schedule_slot(data, slot + 1, false); + + return HRTIMER_RESTART; +} + +enum hrtimer_restart +mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer) +{ + struct mac80211_hwsim_data *data = + container_of(timer, struct mac80211_hwsim_data, + nan.discovery_beacon_timer); + u32 remainder; + u64 tsf_now; + u64 tbtt; + + if (!data->nan.device_vif) + return HRTIMER_NORESTART; + + scoped_guard(spinlock, &data->nan.state_lock) { + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN || + data->nan.role != MAC80211_HWSIM_NAN_ROLE_MASTER) + return HRTIMER_NORESTART; + } + + mac80211_hwsim_nan_tx_beacon( + data, true, ieee80211_get_channel(data->hw->wiphy, 2437)); + + if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) + mac80211_hwsim_nan_tx_beacon( + data, true, + ieee80211_get_channel(data->hw->wiphy, 5745)); + + /* Read the TSF from the current time in case of adjustments */ + tsf_now = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + + /* Wrap value to be after the next TBTT */ + tbtt = tsf_now + ieee80211_tu_to_usec(100); + + /* Round TBTT down to the correct time */ + div_u64_rem(tbtt, ieee80211_tu_to_usec(100), &remainder); + tbtt = tbtt - remainder; + + hrtimer_set_expires(&data->nan.discovery_beacon_timer, + mac80211_hwsim_tsf_to_boottime(data, tbtt)); + + return HRTIMER_RESTART; +} + +int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf) +{ + struct mac80211_hwsim_data *data = hw->priv; + + if (vif->type != NL80211_IFTYPE_NAN) + return -EINVAL; + + if (data->nan.device_vif) + return -EALREADY; + + /* set this before starting the timer, as preemption might occur */ + data->nan.device_vif = vif; + data->nan.bands = conf->bands; + + scoped_guard(spinlock_bh, &data->nan.state_lock) { + /* Start in the "scan" phase and stay there for a little bit */ + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_SCAN; + data->nan.random_factor_valid_dwst = 1; + data->nan.random_factor = 0; + data->nan.master_pref = conf->master_pref; + data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER; + memset(&data->nan.current_ami, 0, + sizeof(data->nan.current_ami)); + memset(&data->nan.last_ami, 0, sizeof(data->nan.last_ami)); + data->nan.current_ami.master_rank = + cpu_to_le64(hwsim_nan_get_master_rank(data)); + } + + /* Just run this "soon" and start in a random schedule position */ + hrtimer_start(&data->nan.slot_timer, + ns_to_ktime(10 * NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + + ether_addr_copy(data->nan.cluster_id, conf->cluster_id); + + data->nan.notify_dw = conf->enable_dw_notification; + + return 0; +} + +int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = hw->priv; + + if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif || + data->nan.device_vif != vif) + return -EINVAL; + + hrtimer_cancel(&data->nan.slot_timer); + hrtimer_cancel(&data->nan.resume_txqs_timer); + hrtimer_cancel(&data->nan.discovery_beacon_timer); + data->nan.device_vif = NULL; + + return 0; +} + +int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf, + u32 changes) +{ + struct mac80211_hwsim_data *data = hw->priv; + + if (vif->type != NL80211_IFTYPE_NAN) + return -EINVAL; + + if (!data->nan.device_vif) + return -EINVAL; + + wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes); + + /* Handle only the changes we care about for simulation purposes */ + if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) + data->nan.bands = conf->bands; + + if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG) + data->nan.notify_dw = conf->enable_dw_notification; + + if (changes & CFG80211_NAN_CONF_CHANGED_PREF) { + scoped_guard(spinlock_bh, &data->nan.state_lock) + data->nan.master_pref = conf->master_pref; + } + + return 0; +} + +static void hwsim_nan_can_sta_transmit(void *_ctx, struct ieee80211_sta *sta) +{ + struct hwsim_nan_sta_iter_ctx *ctx = _ctx; + + if (ctx->can_tx) + return; + + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct ieee80211_txq *txq = sta->txq[i]; + + if (!txq) + continue; + + if (txq->vif->type != NL80211_IFTYPE_NAN && + txq->vif->type != NL80211_IFTYPE_NAN_DATA) + return; + + if (mac80211_hwsim_nan_txq_transmitting(ctx->hw, txq)) { + ctx->can_tx = true; + return; + } + } +} + +static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data) +{ + u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + u8 slot = hwsim_nan_slot_from_tsf(tsf); + bool is_dw_slot = mac80211_hwsim_nan_is_dw_slot(data, slot); + struct hwsim_nan_sta_iter_ctx ctx = { + .hw = data->hw, + .can_tx = false, + }; + u32 timeout_ns; + + /* Outside DW, require local FAW schedule to proceed */ + if (!is_dw_slot) { + scoped_guard(spinlock_bh, &data->nan.state_lock) { + if (!data->nan.local_sched[slot].chan) + return; + } + } + + guard(rcu)(); + + /* Check if management queue can transmit */ + if (mac80211_hwsim_nan_txq_transmitting(data->hw, + data->nan.device_vif->txq_mgmt)) + goto resume_txqs_timer; + + /* Check if any STA queue can transmit */ + ieee80211_iterate_stations_atomic(data->hw, + hwsim_nan_can_sta_transmit, + &ctx); + + if (!ctx.can_tx) + return; + +resume_txqs_timer: + /* + * Wait a bit and also randomize things so that not everyone is TXing + * at the same time. Each slot is 16 TU long, this waits between 100 us + * and 5 ms before starting to TX (unless a new frame arrives). + */ + timeout_ns = get_random_u32_inclusive(100 * NSEC_PER_USEC, + 5 * NSEC_PER_MSEC); + + hrtimer_start(&data->nan.resume_txqs_timer, + ns_to_ktime(timeout_ns), + HRTIMER_MODE_REL_SOFT); +} + +static void hwsim_nan_wake_sta_iter(void *_data, struct ieee80211_sta *sta) +{ + struct ieee80211_hw *hw = _data; + + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct ieee80211_txq *txq = sta->txq[i]; + + if (!txq) + continue; + + /* exit early if non-NAN */ + if (txq->vif->type != NL80211_IFTYPE_NAN && + txq->vif->type != NL80211_IFTYPE_NAN_DATA) + return; + + if (mac80211_hwsim_nan_txq_transmitting(hw, txq)) + ieee80211_hwsim_wake_tx_queue(hw, txq); + } +} + +enum hrtimer_restart +mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer) +{ + struct mac80211_hwsim_data *data = + container_of(timer, struct mac80211_hwsim_data, + nan.resume_txqs_timer); + + guard(rcu)(); + + /* Wake TX queue for management frames on the NAN device interface */ + if (mac80211_hwsim_nan_txq_transmitting(data->hw, + data->nan.device_vif->txq_mgmt)) + ieee80211_hwsim_wake_tx_queue(data->hw, + data->nan.device_vif->txq_mgmt); + + /* Wake TX queues for all stations */ + ieee80211_iterate_stations_atomic(data->hw, + hwsim_nan_wake_sta_iter, + data->hw); + + return HRTIMER_NORESTART; +} + +static void +hwsim_nan_can_mcast_sta_transmit(void *_ctx, struct ieee80211_sta *sta) +{ + struct hwsim_nan_mcast_data_iter_ctx *ctx = _ctx; + struct ieee80211_txq *txq = sta->txq[0]; + + if (!txq || txq->vif != ctx->vif) + return; + + ctx->n_vif_sta++; + if (mac80211_hwsim_nan_txq_transmitting(ctx->hw, txq)) + ctx->n_sta_can_tx++; +} + +static bool +mac80211_hwsim_nan_mcast_data_transmitting(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct hwsim_nan_mcast_data_iter_ctx ctx = { + .hw = hw, + .vif = txq->vif, + .n_sta_can_tx = 0, + .n_vif_sta = 0, + }; + + /* Check if all the stations associated with the current + * interface are available. + */ + ieee80211_iterate_stations_atomic(data->hw, + hwsim_nan_can_mcast_sta_transmit, + &ctx); + + return ctx.n_vif_sta && ctx.n_sta_can_tx == ctx.n_vif_sta; +} + +bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct ieee80211_sta *nmi_sta; + struct hwsim_sta_priv *sp; + bool is_dw_slot; + u64 tsf; + u8 slot; + + if (WARN_ON_ONCE(!data->nan.device_vif)) + return true; + + tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif); + slot = hwsim_nan_slot_from_tsf(tsf); + + /* Enforce a maximum channel switch time and guard against TX delays */ + if (slot != hwsim_nan_slot_from_tsf(tsf + NAN_CHAN_SWITCH_TIME_US)) + return false; + + is_dw_slot = mac80211_hwsim_nan_is_dw_slot(data, slot); + + if (!txq->sta) { + /* Non-STA TXQ: allow management frames during DW */ + if (txq->vif->type == NL80211_IFTYPE_NAN) + return is_dw_slot; + + /* Allow multicast data when all the peers are available + * on this slot + */ + return mac80211_hwsim_nan_mcast_data_transmitting(hw, txq); + } + + /* STA TXQ: need peer schedule for availability check */ + nmi_sta = rcu_dereference(txq->sta->nmi) ?: txq->sta; + sp = (void *)nmi_sta->drv_priv; + + /* DW slot: NDI can TX only mgmt but not worth checking, + * NMI checks peer's committed DW + */ + if (is_dw_slot) { + if (txq->vif->type == NL80211_IFTYPE_NAN_DATA) + return false; + return hwsim_nan_peer_present_in_dw(sp, tsf); + } + + /* FAW slot: verify local schedule and peer availability */ + return hwsim_nan_peer_present_in_faw(sp, data, slot); +} + +void mac80211_hwsim_nan_get_tx_chandef(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef) +{ + struct mac80211_hwsim_data *data = hw->priv; + u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + u8 slot = hwsim_nan_slot_from_tsf(tsf); + + /* DW slots are always 20 MHz */ + if (slot == SLOT_24GHZ_DW) { + cfg80211_chandef_create(chandef, + ieee80211_get_channel(hw->wiphy, 2437), + NL80211_CHAN_NO_HT); + return; + } + + if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ)) { + cfg80211_chandef_create(chandef, + ieee80211_get_channel(hw->wiphy, 5745), + NL80211_CHAN_NO_HT); + return; + } + + /* FAW slot: copy local schedule for this slot */ + scoped_guard(spinlock_bh, &data->nan.state_lock) + *chandef = data->nan.local_sched[slot]; +} + +bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw, + struct ieee80211_channel *channel, + struct ieee80211_rx_status *rx_status) +{ + struct mac80211_hwsim_data *data = hw->priv; + u8 slot; + + if (WARN_ON_ONCE(!data->nan.device_vif)) + return false; + + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) + return channel->center_freq == 2437; + + if (rx_status->flag & RX_FLAG_MACTIME) { + slot = hwsim_nan_slot_from_tsf(rx_status->mactime); + } else { + u64 tsf; + + /* + * This is not perfect, but that should be fine. + * + * Assume the frame might be a bit early in relation to our + * own TSF. This is largely because the TSF sync is going to be + * pretty bad when the frame was RXed via NL and the beacon as + * well as RX timestamps are not accurate. + */ + tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + slot = hwsim_nan_slot_from_tsf(tsf + 128); + } + + if (slot == SLOT_24GHZ_DW && channel->center_freq == 2437) + return true; + + if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ) && + channel->center_freq == 5745) + return true; + + /* Accept frames during FAW slots if chandef is compatible */ + return hwsim_nan_rx_chandef_compatible(data, slot, channel, + rx_status->bw); +} + +void mac80211_hwsim_nan_local_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = hw->priv; + struct ieee80211_nan_channel **slots = vif->cfg.nan_sched.schedule; + + if (WARN_ON(vif->type != NL80211_IFTYPE_NAN)) + return; + + spin_lock_bh(&data->nan.state_lock); + + for (int i = 0; i < ARRAY_SIZE(data->nan.local_sched); i++) { + struct ieee80211_chanctx_conf *chanctx; + + if (!slots[i] || IS_ERR(slots[i])) { + memset(&data->nan.local_sched[i], 0, + sizeof(data->nan.local_sched[i])); + continue; + } + + chanctx = slots[i]->chanctx_conf; + if (!chanctx) { + memset(&data->nan.local_sched[i], 0, + sizeof(data->nan.local_sched[i])); + continue; + } + + data->nan.local_sched[i] = chanctx->def; + } + + spin_unlock_bh(&data->nan.state_lock); +} + +int mac80211_hwsim_nan_peer_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct hwsim_sta_priv *sp = (void *)sta->drv_priv; + struct ieee80211_nan_peer_sched *sched = sta->nan_sched; + + spin_lock_bh(&sp->nan_sched.lock); + + /* Clear existing schedule */ + sp->nan_sched.committed_dw = 0; + for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) { + sp->nan_sched.maps[i].map_id = CFG80211_NAN_INVALID_MAP_ID; + memset(sp->nan_sched.maps[i].chans, 0, + sizeof(sp->nan_sched.maps[i].chans)); + } + + if (!sched) + goto out; + + sp->nan_sched.committed_dw = sched->committed_dw; + + for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) { + struct ieee80211_nan_peer_map *map = &sched->maps[i]; + + if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + sp->nan_sched.maps[i].map_id = map->map_id; + + for (int j = 0; j < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; j++) { + struct ieee80211_nan_channel *peer_chan = + map->slots[j]; + + if (peer_chan && peer_chan->chanreq.oper.chan) + sp->nan_sched.maps[i].chans[j] = + peer_chan->chanreq.oper; + else + memset(&sp->nan_sched.maps[i].chans[j], 0, + sizeof(sp->nan_sched.maps[i].chans[j])); + } + } + +out: + spin_unlock_bh(&sp->nan_sched.lock); + return 0; +} diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h new file mode 100644 index 000000000000..81e105ac7b8e --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim + * Copyright (C) 2025-2026 Intel Corporation + */ + +#ifndef __MAC80211_HWSIM_NAN_H +#define __MAC80211_HWSIM_NAN_H + +enum mac80211_hwsim_nan_phase { + MAC80211_HWSIM_NAN_PHASE_SCAN, + MAC80211_HWSIM_NAN_PHASE_WARMUP, + MAC80211_HWSIM_NAN_PHASE_UP, +}; + +enum mac80211_hwsim_nan_role { + MAC80211_HWSIM_NAN_ROLE_MASTER, + MAC80211_HWSIM_NAN_ROLE_SYNC, + MAC80211_HWSIM_NAN_ROLE_NON_SYNC, +}; + +struct mac80211_hwsim_nan_data { + struct ieee80211_vif *device_vif; + u8 bands; + + struct hrtimer slot_timer; + struct hrtimer resume_txqs_timer; + bool notify_dw; + + struct hrtimer discovery_beacon_timer; + + /* Later members are protected by this lock */ + spinlock_t state_lock; + + u8 master_pref; + u8 random_factor; + + u8 random_factor_valid_dwst; + + enum mac80211_hwsim_nan_phase phase; + enum mac80211_hwsim_nan_role role; + + u8 cluster_id[ETH_ALEN]; + + struct ieee80211_nan_anchor_master_info current_ami; + struct ieee80211_nan_anchor_master_info last_ami; + + /* Wi-Fi Aware version 4.0, section 3.3.6.1 and 3.3.6.2 */ + int master_transition_score; + /* Wi-Fi Aware version 4.0, section 3.3.6.3 and 3.3.6.4 */ + int sync_transition_score; + + bool tsf_adjusted; + bool tsf_discontinuity; + + /* + * Local schedule - stores channel definition for each 16TU slot. + * Derived from NMI vif->cfg.nan_schedule. chan == NULL means not + * available in that slot (except DW which is implicit). + */ + struct cfg80211_chan_def local_sched[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; +}; + +enum hrtimer_restart +mac80211_hwsim_nan_slot_timer(struct hrtimer *timer); +enum hrtimer_restart +mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer); +enum hrtimer_restart +mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer); + +int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf); + +int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf, + u32 changes); + +int mac80211_hwsim_nan_peer_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_sta *sta); + +bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); + +void mac80211_hwsim_nan_get_tx_chandef(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef); + +bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw, + struct ieee80211_channel *channel, + struct ieee80211_rx_status *rx_status); + +void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw, + struct sk_buff *skb); + +void mac80211_hwsim_nan_local_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +#endif /* __MAC80211_HWSIM_NAN_H */ diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig index 88df55d78d90..958dbc7347fa 100644 --- a/drivers/net/wwan/Kconfig +++ b/drivers/net/wwan/Kconfig @@ -38,7 +38,7 @@ config WWAN_HWSIM called wwan_hwsim. If unsure, say N. config MHI_WWAN_CTRL - tristate "MHI WWAN control driver for QCOM-based PCIe modems" + tristate "MHI WWAN control driver for Qualcomm-based PCIe modems" depends on MHI_BUS help MHI WWAN CTRL allows QCOM-based PCIe modems to expose different modem @@ -51,7 +51,7 @@ config MHI_WWAN_CTRL called mhi_wwan_ctrl. config MHI_WWAN_MBIM - tristate "MHI WWAN MBIM network driver for QCOM-based PCIe modems" + tristate "MHI WWAN MBIM network driver for Qualcomm-based PCIe modems" depends on MHI_BUS help MHI WWAN MBIM is a WWAN network driver for QCOM-based PCIe modems. diff --git a/drivers/net/wwan/t7xx/t7xx_pci.c b/drivers/net/wwan/t7xx/t7xx_pci.c index eb137e078423..46613bba1f41 100644 --- a/drivers/net/wwan/t7xx/t7xx_pci.c +++ b/drivers/net/wwan/t7xx/t7xx_pci.c @@ -447,6 +447,9 @@ static int __t7xx_pci_pm_suspend(struct pci_dev *pdev) goto abort_suspend; } + /* Delay to prevent SAP suspend timeout */ + msleep(50); + ret = t7xx_send_pm_request(t7xx_dev, H2D_CH_SUSPEND_REQ_AP); if (ret) { t7xx_send_pm_request(t7xx_dev, H2D_CH_RESUME_REQ); diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c index 7fc569565ff9..d2529df7592a 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c +++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c @@ -106,6 +106,8 @@ static int t7xx_port_ctrl_tx(struct t7xx_port *port, struct sk_buff *skb) while (cur) { cloned = skb_clone(cur, GFP_KERNEL); + if (!cloned) + return cnt ? cnt : -ENOMEM; cloned->len = skb_headlen(cur); ret = t7xx_port_send_skb(port, cloned, 0, 0); if (ret) { diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c index f00cff7f9693..f45e883b8730 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -276,7 +276,7 @@ static void microread_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id microread_i2c_id[] = { - { MICROREAD_I2C_DRIVER_NAME }, + { .name = MICROREAD_I2C_DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, microread_i2c_id); diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c index 39ecf2aeda80..66877a7d03f2 100644 --- a/drivers/nfc/nfcmrvl/i2c.c +++ b/drivers/nfc/nfcmrvl/i2c.c @@ -252,8 +252,8 @@ static const struct of_device_id of_nfcmrvl_i2c_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, of_nfcmrvl_i2c_match); static const struct i2c_device_id nfcmrvl_i2c_id_table[] = { - { "nfcmrvl_i2c" }, - {} + { .name = "nfcmrvl_i2c" }, + { } }; MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table); diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c index 66b198663387..448e5a4e7338 100644 --- a/drivers/nfc/nxp-nci/core.c +++ b/drivers/nfc/nxp-nci/core.c @@ -25,6 +25,7 @@ NFC_PROTO_FELICA_MASK | \ NFC_PROTO_ISO14443_MASK | \ NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_ISO15693_MASK | \ NFC_PROTO_NFC_DEP_MASK) #define NXP_NCI_RF_PLL_UNLOCKED_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x21) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index a6c08175d9dd..faebc89a7ef5 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -231,6 +231,14 @@ static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id) if (info->mode == NXP_NCI_MODE_FW) nxp_nci_fw_recv_frame(phy->ndev, NULL); } + if (r == -ENXIO) { + /* + * -ENXIO may occur if the controller has not yet + * provided data after asserting IRQ. + */ + dev_dbg(&client->dev, "No data available yet\n"); + goto exit_irq_handled; + } if (r < 0) { nfc_err(&client->dev, "Read failed with error %d\n", r); goto exit_irq_handled; @@ -341,8 +349,8 @@ static void nxp_nci_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id nxp_nci_i2c_id_table[] = { - { "nxp-nci_i2c" }, - {} + { .name = "nxp-nci_i2c" }, + { } }; MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table); diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c index 132c050a365d..94aca9119f0f 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -249,8 +249,8 @@ static const struct of_device_id of_pn533_i2c_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, of_pn533_i2c_match); static const struct i2c_device_id pn533_i2c_id_table[] = { - { PN533_I2C_DRIVER_NAME }, - {} + { .name = PN533_I2C_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, pn533_i2c_id_table); diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index b31b5bef7187..dcfa96bd4345 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -44,8 +44,8 @@ PN544_HCI_I2C_LLC_MAX_PAYLOAD) static const struct i2c_device_id pn544_hci_i2c_id_table[] = { - { "pn544" }, - {} + { .name = "pn544" }, + { } }; MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index 91b8d1445efd..e9a34d27a369 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -205,8 +205,8 @@ static void s3fwrn5_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id s3fwrn5_i2c_id_table[] = { - { S3FWRN5_I2C_DRIVER_NAME }, - {} + { .name = S3FWRN5_I2C_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, s3fwrn5_i2c_id_table); diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 416770adbeba..9ae839a6f5cc 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -257,8 +257,8 @@ static void st_nci_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id st_nci_i2c_id_table[] = { - { ST_NCI_DRIVER_NAME }, - {} + { .name = ST_NCI_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 6d7861383806..aa5f4922b6b0 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -572,8 +572,8 @@ static void st21nfca_hci_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id st21nfca_hci_i2c_id_table[] = { - { ST21NFCA_HCI_DRIVER_NAME }, - {} + { .name = ST21NFCA_HCI_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index 08c27bb438b5..f22e091019de 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -61,15 +61,15 @@ * support that. So, if an abort is received before trf7970a_send_cmd() * has sent the command to the tag, it simply returns -ECANCELED. If the * command has already been sent to the tag, then the driver continues - * normally and recieves the response data (or error) but just before + * normally and receives the response data (or error) but just before * sending the data upstream, it frees the rx_skb and sends -ECANCELED * upstream instead. If the command failed, that error will be sent * upstream. * - * When recieving data from a tag and the interrupt status register has + * When receiving data from a tag and the interrupt status register has * only the SRX bit set, it means that all of the data has been received * (once what's in the fifo has been read). However, depending on timing - * an interrupt status with only the SRX bit set may not be recived. In + * an interrupt status with only the SRX bit set may not be received. In * those cases, the timeout mechanism is used to wait 20 ms in case more * data arrives. After 20 ms, it is assumed that all of the data has been * received and the accumulated rx data is sent upstream. The diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 8231c2d7785e..35e911f1ad78 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -5186,6 +5186,7 @@ static struct pci_driver ptp_ocp_driver = { .id_table = ptp_ocp_pcidev_id, .probe = ptp_ocp_probe, .remove = ptp_ocp_remove, + .shutdown = ptp_ocp_remove, }; static int diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c index 8510121d79d1..14fc587d0a00 100644 --- a/drivers/ptp/ptp_vmw.c +++ b/drivers/ptp/ptp_vmw.c @@ -18,7 +18,6 @@ #define VMWARE_CMD_PCLK(nr) ((nr << 16) | 97) #define VMWARE_CMD_PCLK_GETTIME VMWARE_CMD_PCLK(0) -static struct acpi_device *ptp_vmw_acpi_device; static struct ptp_clock *ptp_vmw_clock; @@ -91,8 +90,6 @@ static int ptp_vmw_acpi_probe(struct platform_device *pdev) pr_err("failed to register ptp clock\n"); return PTR_ERR(ptp_vmw_clock); } - - ptp_vmw_acpi_device = ACPI_COMPANION(&pdev->dev); return 0; } diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 7d0479e4e095..d99c588d3e00 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -23,8 +23,8 @@ MODULE_LICENSE("GPL"); #define DRV_NAME "ism" static const struct pci_device_id ism_device_table[] = { - { PCI_VDEVICE(IBM, PCI_DEVICE_ID_IBM_ISM), 0 }, - { 0, } + { PCI_VDEVICE(IBM, PCI_DEVICE_ID_IBM_ISM) }, + { } }; MODULE_DEVICE_TABLE(pci, ism_device_table); diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 26e0ff380860..c95b084cad69 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -17,6 +17,7 @@ #include "bnx2fc.h" #include +#include static struct list_head adapter_list; static struct list_head if_list; @@ -815,7 +816,9 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev) port->fcoe_pending_queue_active = 0; timer_setup(&port->timer, fcoe_queue_timer, 0); + netdev_lock_ops(netdev); fcoe_link_speed_update(lport); + netdev_unlock_ops(netdev); if (!lport->vport) { if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 534596c6d76c..438ac7c3a9e3 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -737,7 +738,9 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) port->fcoe_pending_queue_active = 0; timer_setup(&port->timer, fcoe_queue_timer, 0); + netdev_lock_ops(netdev); fcoe_link_speed_update(lport); + netdev_unlock_ops(netdev); if (!lport->vport) { if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) @@ -1841,6 +1844,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, break; case NETDEV_UP: case NETDEV_CHANGE: + fcoe_link_speed_update(lport); break; case NETDEV_CHANGEMTU: if (netdev->fcoe_mtu) @@ -1871,8 +1875,6 @@ static int fcoe_device_notification(struct notifier_block *notifier, "from netdev netlink\n", event); } - fcoe_link_speed_update(lport); - cdev = fcoe_ctlr_to_ctlr_dev(ctlr); if (link_possible && !fcoe_link_ok(lport)) { diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 88d85fc9a52a..2bbb9a38e61d 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -111,6 +111,8 @@ static inline u32 eth2fc_speed(u32 eth_port_speed) * fcoe_link_speed_update() - Update the supported and actual link speeds * @lport: The local port to update speeds for * + * Caller must hold the netdev's ops lock. + * * Returns: 0 if the ethtool query was successful * -1 if the ethtool query failed */ @@ -119,7 +121,7 @@ int fcoe_link_speed_update(struct fc_lport *lport) struct net_device *netdev = fcoe_get_netdev(lport); struct ethtool_link_ksettings ecmd; - if (!__ethtool_get_link_ksettings(netdev, &ecmd)) { + if (!netif_get_link_ksettings(netdev, &ecmd)) { lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT | FC_PORTSPEED_20GBIT | diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index f3ae72feb5bf..11e3490d62ca 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -1572,10 +1572,8 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver) } else file = cmv_file[sc->modem_index]; - strcpy(cmv_name, FW_DIR); - strlcat(cmv_name, file, UEA_FW_NAME_MAX); - if (ver == 2) - strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX); + snprintf(cmv_name, UEA_FW_NAME_MAX, FW_DIR "%s%s", + file, ver == 2 ? ".v2" : ""); kernel_param_unlock(THIS_MODULE); } diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index c6536cad9c4f..db341c922673 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -176,13 +176,21 @@ static void *vhost_net_buf_consume(struct vhost_net_buf *rxq) return ret; } -static int vhost_net_buf_produce(struct vhost_net_virtqueue *nvq) +static int vhost_net_buf_produce(struct sock *sk, + struct vhost_net_virtqueue *nvq) { + struct file *file = sk->sk_socket->file; struct vhost_net_buf *rxq = &nvq->rxq; rxq->head = 0; - rxq->tail = ptr_ring_consume_batched(nvq->rx_ring, rxq->queue, - VHOST_NET_BATCH); + spin_lock(&nvq->rx_ring->consumer_lock); + rxq->tail = __ptr_ring_consume_batched(nvq->rx_ring, rxq->queue, + VHOST_NET_BATCH); + + if (rxq->tail) + tun_wake_queue(file, rxq->tail); + + spin_unlock(&nvq->rx_ring->consumer_lock); return rxq->tail; } @@ -209,14 +217,15 @@ static int vhost_net_buf_peek_len(void *ptr) return __skb_array_len_with_tag(ptr); } -static int vhost_net_buf_peek(struct vhost_net_virtqueue *nvq) +static int vhost_net_buf_peek(struct sock *sk, + struct vhost_net_virtqueue *nvq) { struct vhost_net_buf *rxq = &nvq->rxq; if (!vhost_net_buf_is_empty(rxq)) goto out; - if (!vhost_net_buf_produce(nvq)) + if (!vhost_net_buf_produce(sk, nvq)) return 0; out: @@ -995,7 +1004,7 @@ static int peek_head_len(struct vhost_net_virtqueue *rvq, struct sock *sk) unsigned long flags; if (rvq->rx_ring) - return vhost_net_buf_peek(rvq); + return vhost_net_buf_peek(sk, rvq); spin_lock_irqsave(&sk->sk_receive_queue.lock, flags); head = skb_peek(&sk->sk_receive_queue); diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 588f8de51167..d5cfd24e815b 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -127,6 +127,7 @@ void afs_close_socket(struct afs_net *net) { _enter(""); + cancel_work_sync(&net->charge_preallocation_work); kernel_listen(net->socket, 0); flush_workqueue(afs_async_calls); @@ -742,7 +743,7 @@ void afs_charge_preallocation(struct work_struct *work) container_of(work, struct afs_net, charge_preallocation_work); struct afs_call *call = net->spare_incoming_call; - for (;;) { + while (READ_ONCE(net->live)) { if (!call) { call = afs_alloc_call(net, &afs_RXCMxxxx, GFP_KERNEL); if (!call) @@ -792,7 +793,8 @@ static void afs_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall, if (!call->server) trace_afs_cm_no_server(call, rxrpc_kernel_remote_srx(call->peer)); - queue_work(afs_wq, &net->charge_preallocation_work); + if (net->live) + queue_work(afs_wq, &net->charge_preallocation_work); } /* diff --git a/include/crypto/hash.h b/include/crypto/hash.h index 586700332c73..e474f8461ea1 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -148,7 +148,6 @@ struct ahash_request { * @exit_tfm: Deinitialize the cryptographic transformation object. * This is a counterpart to @init_tfm, used to remove * various changes set in @init_tfm. - * @clone_tfm: Copy transform into new object, may allocate memory. * @halg: see struct hash_alg_common */ struct ahash_alg { @@ -165,7 +164,6 @@ struct ahash_alg { unsigned int keylen); int (*init_tfm)(struct crypto_ahash *tfm); void (*exit_tfm)(struct crypto_ahash *tfm); - int (*clone_tfm)(struct crypto_ahash *dst, struct crypto_ahash *src); struct hash_alg_common halg; }; @@ -239,7 +237,6 @@ struct shash_desc { * @exit_tfm: Deinitialize the cryptographic transformation object. * This is a counterpart to @init_tfm, used to remove * various changes set in @init_tfm. - * @clone_tfm: Copy transform into new object, may allocate memory. * @descsize: Size of the operational state for the message digest. This state * size is the memory size that needs to be allocated for * shash_desc.__ctx @@ -263,7 +260,6 @@ struct shash_alg { unsigned int keylen); int (*init_tfm)(struct crypto_shash *tfm); void (*exit_tfm)(struct crypto_shash *tfm); - int (*clone_tfm)(struct crypto_shash *dst, struct crypto_shash *src); unsigned int descsize; @@ -322,8 +318,6 @@ static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm) struct crypto_ahash *crypto_alloc_ahash(const char *alg_name, u32 type, u32 mask); -struct crypto_ahash *crypto_clone_ahash(struct crypto_ahash *tfm); - static inline struct crypto_tfm *crypto_ahash_tfm(struct crypto_ahash *tfm) { return &tfm->base; @@ -759,8 +753,6 @@ static inline void ahash_request_set_virt(struct ahash_request *req, struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type, u32 mask); -struct crypto_shash *crypto_clone_shash(struct crypto_shash *tfm); - int crypto_has_shash(const char *alg_name, u32 type, u32 mask); static inline struct crypto_tfm *crypto_shash_tfm(struct crypto_shash *tfm) diff --git a/include/crypto/internal/cipher.h b/include/crypto/internal/cipher.h index 5030f6d2df31..a9174ba90250 100644 --- a/include/crypto/internal/cipher.h +++ b/include/crypto/internal/cipher.h @@ -176,8 +176,6 @@ void crypto_cipher_encrypt_one(struct crypto_cipher *tfm, void crypto_cipher_decrypt_one(struct crypto_cipher *tfm, u8 *dst, const u8 *src); -struct crypto_cipher *crypto_clone_cipher(struct crypto_cipher *cipher); - struct crypto_cipher_spawn { struct crypto_spawn base; }; diff --git a/include/linux/atalk.h b/include/linux/atalk.h deleted file mode 100644 index a55bfc6567d0..000000000000 --- a/include/linux/atalk.h +++ /dev/null @@ -1,186 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LINUX_ATALK_H__ -#define __LINUX_ATALK_H__ - - -#include -#include - -struct atalk_route { - struct net_device *dev; - struct atalk_addr target; - struct atalk_addr gateway; - int flags; - struct atalk_route *next; -}; - -/** - * struct atalk_iface - AppleTalk Interface - * @dev - Network device associated with this interface - * @address - Our address - * @status - What are we doing? - * @nets - Associated direct netrange - * @next - next element in the list of interfaces - */ -struct atalk_iface { - struct net_device *dev; - struct atalk_addr address; - int status; -#define ATIF_PROBE 1 /* Probing for an address */ -#define ATIF_PROBE_FAIL 2 /* Probe collided */ - struct atalk_netrange nets; - struct atalk_iface *next; -}; - -struct atalk_sock { - /* struct sock has to be the first member of atalk_sock */ - struct sock sk; - __be16 dest_net; - __be16 src_net; - unsigned char dest_node; - unsigned char src_node; - unsigned char dest_port; - unsigned char src_port; -}; - -static inline struct atalk_sock *at_sk(struct sock *sk) -{ - return (struct atalk_sock *)sk; -} - -struct ddpehdr { - __be16 deh_len_hops; /* lower 10 bits are length, next 4 - hops */ - __be16 deh_sum; - __be16 deh_dnet; - __be16 deh_snet; - __u8 deh_dnode; - __u8 deh_snode; - __u8 deh_dport; - __u8 deh_sport; - /* And netatalk apps expect to stick the type in themselves */ -}; - -static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb) -{ - return (struct ddpehdr *)skb_transport_header(skb); -} - -/* AppleTalk AARP headers */ -struct elapaarp { - __be16 hw_type; -#define AARP_HW_TYPE_ETHERNET 1 -#define AARP_HW_TYPE_TOKENRING 2 - __be16 pa_type; - __u8 hw_len; - __u8 pa_len; -#define AARP_PA_ALEN 4 - __be16 function; -#define AARP_REQUEST 1 -#define AARP_REPLY 2 -#define AARP_PROBE 3 - __u8 hw_src[ETH_ALEN]; - __u8 pa_src_zero; - __be16 pa_src_net; - __u8 pa_src_node; - __u8 hw_dst[ETH_ALEN]; - __u8 pa_dst_zero; - __be16 pa_dst_net; - __u8 pa_dst_node; -} __attribute__ ((packed)); - -static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb) -{ - return (struct elapaarp *)skb_transport_header(skb); -} - -/* Not specified - how long till we drop a resolved entry */ -#define AARP_EXPIRY_TIME (5 * 60 * HZ) -/* Size of hash table */ -#define AARP_HASH_SIZE 16 -/* Fast retransmission timer when resolving */ -#define AARP_TICK_TIME (HZ / 5) -/* Send 10 requests then give up (2 seconds) */ -#define AARP_RETRANSMIT_LIMIT 10 -/* - * Some value bigger than total retransmit time + a bit for last reply to - * appear and to stop continual requests - */ -#define AARP_RESOLVE_TIME (10 * HZ) - -extern struct datalink_proto *ddp_dl, *aarp_dl; -extern int aarp_proto_init(void); - -/* Inter module exports */ - -/* Give a device find its atif control structure */ -#if IS_ENABLED(CONFIG_ATALK) -static inline struct atalk_iface *atalk_find_dev(struct net_device *dev) -{ - return dev->atalk_ptr; -} -#endif - -extern struct atalk_addr *atalk_find_dev_addr(struct net_device *dev); -extern struct net_device *atrtr_get_dev(struct atalk_addr *sa); -extern int aarp_send_ddp(struct net_device *dev, - struct sk_buff *skb, - struct atalk_addr *sa, void *hwaddr); -extern void aarp_device_down(struct net_device *dev); -extern void aarp_probe_network(struct atalk_iface *atif); -extern int aarp_proxy_probe_network(struct atalk_iface *atif, - struct atalk_addr *sa); -extern void aarp_proxy_remove(struct net_device *dev, - struct atalk_addr *sa); - -extern void aarp_cleanup_module(void); - -extern struct hlist_head atalk_sockets; -extern rwlock_t atalk_sockets_lock; - -extern struct atalk_route *atalk_routes; -extern rwlock_t atalk_routes_lock; - -extern struct atalk_iface *atalk_interfaces; -extern rwlock_t atalk_interfaces_lock; - -extern struct atalk_route atrtr_default; - -struct aarp_iter_state { - int bucket; - struct aarp_entry **table; -}; - -extern const struct seq_operations aarp_seq_ops; - -extern int sysctl_aarp_expiry_time; -extern int sysctl_aarp_tick_time; -extern int sysctl_aarp_retransmit_limit; -extern int sysctl_aarp_resolve_time; - -#ifdef CONFIG_SYSCTL -extern int atalk_register_sysctl(void); -extern void atalk_unregister_sysctl(void); -#else -static inline int atalk_register_sysctl(void) -{ - return 0; -} -static inline void atalk_unregister_sysctl(void) -{ -} -#endif - -#ifdef CONFIG_PROC_FS -extern int atalk_proc_init(void); -extern void atalk_proc_exit(void); -#else -static inline int atalk_proc_init(void) -{ - return 0; -} -static inline void atalk_proc_exit(void) -{ -} -#endif /* CONFIG_PROC_FS */ - -#endif /* __LINUX_ATALK_H__ */ diff --git a/include/linux/atm_tcp.h b/include/linux/atm_tcp.h deleted file mode 100644 index 2558439d849b..000000000000 --- a/include/linux/atm_tcp.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* atm_tcp.h - Driver-specific declarations of the ATMTCP driver (for use by - driver-specific utilities) */ - -/* Written 1997-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#ifndef LINUX_ATM_TCP_H -#define LINUX_ATM_TCP_H - -#include - -struct atm_vcc; -struct module; - -struct atm_tcp_ops { - int (*attach)(struct atm_vcc *vcc,int itf); - int (*create_persistent)(int itf); - int (*remove_persistent)(int itf); - struct module *owner; -}; - -extern struct atm_tcp_ops atm_tcp_ops; - -#endif diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 82a32526df64..fe21d2f547b5 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -51,33 +51,11 @@ enum { driver, cleared by anybody. */ ATM_VF_PARTIAL, /* resources are bound to PVC (partial PVC setup), controlled by socket layer */ - ATM_VF_REGIS, /* registered with demon, controlled by SVC - socket layer */ - ATM_VF_BOUND, /* local SAP is set, controlled by SVC socket - layer */ - ATM_VF_RELEASED, /* demon has indicated/requested release, - controlled by SVC socket layer */ ATM_VF_HASQOS, /* QOS parameters have been set */ - ATM_VF_LISTEN, /* socket is used for listening */ - ATM_VF_META, /* SVC socket isn't used for normal data - traffic and doesn't depend on signaling - to be available */ - ATM_VF_SESSION, /* VCC is p2mp session control descriptor */ - ATM_VF_HASSAP, /* SAP has been set */ - ATM_VF_CLOSE, /* asynchronous close - treat like VF_RELEASED*/ - ATM_VF_WAITING, /* waiting for reply from sigd */ - ATM_VF_IS_CLIP, /* in use by CLIP protocol */ + ATM_VF_CLOSE, /* asynchronous close - VC is being torn down */ }; -#define ATM_VF2VS(flags) \ - (test_bit(ATM_VF_READY,&(flags)) ? ATM_VS_CONNECTED : \ - test_bit(ATM_VF_RELEASED,&(flags)) ? ATM_VS_CLOSING : \ - test_bit(ATM_VF_LISTEN,&(flags)) ? ATM_VS_LISTEN : \ - test_bit(ATM_VF_REGIS,&(flags)) ? ATM_VS_INUSE : \ - test_bit(ATM_VF_BOUND,&(flags)) ? ATM_VS_BOUND : ATM_VS_IDLE) - - enum { ATM_DF_REMOVED, /* device was removed from atm_devs list */ }; @@ -100,26 +78,16 @@ struct atm_vcc { unsigned long atm_options; /* ATM layer options */ struct atm_dev *dev; /* device back pointer */ struct atm_qos qos; /* QOS */ - struct atm_sap sap; /* SAP */ void (*release_cb)(struct atm_vcc *vcc); /* release_sock callback */ void (*push)(struct atm_vcc *vcc,struct sk_buff *skb); void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */ - int (*push_oam)(struct atm_vcc *vcc,void *cell); int (*send)(struct atm_vcc *vcc,struct sk_buff *skb); void *dev_data; /* per-device data */ void *proto_data; /* per-protocol data */ struct k_atm_aal_stats *stats; /* pointer to AAL stats group */ struct module *owner; /* owner of ->push function */ - /* SVC part --- may move later ------------------------------------- */ - short itf; /* interface number */ - struct sockaddr_atmsvc local; - struct sockaddr_atmsvc remote; - /* Multipoint part ------------------------------------------------- */ - struct atm_vcc *session; /* session VCC descriptor */ - /* Other stuff ----------------------------------------------------- */ - void *user_back; /* user backlink - not touched by */ - /* native ATM stack. Currently used */ - /* by CLIP and sch_atm. */ + void *user_back; /* user backlink - not touched by the */ + /* native ATM stack, used by sch_atm */ }; static inline struct atm_vcc *atm_sk(struct sock *sk) @@ -137,31 +105,19 @@ static inline struct sock *sk_atm(struct atm_vcc *vcc) return (struct sock *)vcc; } -struct atm_dev_addr { - struct sockaddr_atmsvc addr; /* ATM address */ - struct list_head entry; /* next address */ -}; - -enum atm_addr_type_t { ATM_ADDR_LOCAL, ATM_ADDR_LECS }; - struct atm_dev { const struct atmdev_ops *ops; /* device operations; NULL if unused */ - const struct atmphy_ops *phy; /* PHY operations, may be undefined */ - /* (NULL) */ const char *type; /* device type name */ int number; /* device index */ void *dev_data; /* per-device data */ void *phy_data; /* private PHY data */ unsigned long flags; /* device flags (ATM_DF_*) */ - struct list_head local; /* local ATM addresses */ - struct list_head lecs; /* LECS ATM addresses learned via ILMI */ unsigned char esi[ESI_LEN]; /* ESI ("MAC" addr) */ struct atm_cirange ci_range; /* VPI/VCI range */ struct k_atm_dev_stats stats; /* statistics */ char signal; /* signal status (ATM_PHY_SIG_*) */ int link_rate; /* link rate (default: OC3) */ refcount_t refcnt; /* reference count */ - spinlock_t lock; /* protect internal members */ #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; /* proc entry */ char *proc_name; /* proc entry name */ @@ -170,12 +126,6 @@ struct atm_dev { struct list_head dev_list; /* linkage */ }; - -/* OF: send_Oam Flags */ - -#define ATM_OF_IMMED 1 /* Attempt immediate delivery */ -#define ATM_OF_INRATE 2 /* Attempt in-rate delivery */ - struct atmdev_ops { /* only send is required */ void (*dev_close)(struct atm_dev *dev); int (*open)(struct atm_vcc *vcc); @@ -185,25 +135,11 @@ struct atmdev_ops { /* only send is required */ int (*compat_ioctl)(struct atm_dev *dev,unsigned int cmd, void __user *arg); #endif - int (*pre_send)(struct atm_vcc *vcc, struct sk_buff *skb); int (*send)(struct atm_vcc *vcc,struct sk_buff *skb); - int (*send_bh)(struct atm_vcc *vcc, struct sk_buff *skb); - int (*send_oam)(struct atm_vcc *vcc,void *cell,int flags); - void (*phy_put)(struct atm_dev *dev,unsigned char value, - unsigned long addr); - unsigned char (*phy_get)(struct atm_dev *dev,unsigned long addr); - int (*change_qos)(struct atm_vcc *vcc,struct atm_qos *qos,int flags); int (*proc_read)(struct atm_dev *dev,loff_t *pos,char *page); struct module *owner; }; -struct atmphy_ops { - int (*start)(struct atm_dev *dev); - int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void __user *arg); - void (*interrupt)(struct atm_dev *dev); - int (*stop)(struct atm_dev *dev); -}; - struct atm_skb_data { struct atm_vcc *vcc; /* ATM VCC */ unsigned long atm_options; /* ATM layer options */ diff --git a/include/linux/crypto.h b/include/linux/crypto.h index a2137e19be7d..b7c97f1c47c9 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -409,7 +409,6 @@ int crypto_has_alg(const char *name, u32 type, u32 mask); */ struct crypto_tfm { - refcount_t refcnt; u32 crt_flags; diff --git a/include/linux/dpll.h b/include/linux/dpll.h index 2dbe8567eafc..7a6f8796cda2 100644 --- a/include/linux/dpll.h +++ b/include/linux/dpll.h @@ -13,6 +13,7 @@ #include #include #include +#include struct dpll_device; struct dpll_pin; @@ -60,7 +61,20 @@ struct dpll_device_ops { struct netlink_ext_ack *extack); }; +enum dpll_ffo_type { + DPLL_FFO_PORT_RXTX_RATE, + DPLL_FFO_PIN_DEVICE, + + __DPLL_FFO_TYPE_MAX, +}; + +struct dpll_ffo_param { + enum dpll_ffo_type type; + s64 ffo; +}; + struct dpll_pin_ops { + unsigned long supported_ffo; int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv, const struct dpll_device *dpll, void *dpll_priv, const u64 frequency, @@ -85,6 +99,12 @@ struct dpll_pin_ops { const struct dpll_device *dpll, void *dpll_priv, enum dpll_pin_state *state, struct netlink_ext_ack *extack); + int (*operstate_on_dpll_get)(const struct dpll_pin *pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_operstate *operstate, + struct netlink_ext_ack *extack); int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv, const struct dpll_pin *parent_pin, void *parent_pin_priv, @@ -115,7 +135,8 @@ struct dpll_pin_ops { struct netlink_ext_ack *extack); int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv, const struct dpll_device *dpll, void *dpll_priv, - s64 *ffo, struct netlink_ext_ack *extack); + struct dpll_ffo_param *ffo, + struct netlink_ext_ack *extack); int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv, const struct dpll_device *dpll, void *dpll_priv, u64 *measured_freq, @@ -212,13 +233,18 @@ struct dpll_pin_notifier_info { u64 clock_id; const struct fwnode_handle *fwnode; const struct dpll_pin_properties *prop; + u64 src_clock_id; }; #if IS_ENABLED(CONFIG_DPLL) void dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin); void dpll_netdev_pin_clear(struct net_device *dev); -size_t dpll_netdev_pin_handle_size(const struct net_device *dev); +static inline size_t dpll_netdev_pin_handle_size(void) +{ + return nla_total_size(4); /* DPLL_A_PIN_ID */ +} + int dpll_netdev_add_pin_handle(struct sk_buff *msg, const struct net_device *dev); @@ -229,7 +255,7 @@ static inline void dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) { } static inline void dpll_netdev_pin_clear(struct net_device *dev) { } -static inline size_t dpll_netdev_pin_handle_size(const struct net_device *dev) +static inline size_t dpll_netdev_pin_handle_size(void) { return 0; } diff --git a/include/linux/dsa/tag_netc.h b/include/linux/dsa/tag_netc.h new file mode 100644 index 000000000000..fe964722e5b0 --- /dev/null +++ b/include/linux/dsa/tag_netc.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2025-2026 NXP + */ + +#ifndef __NET_DSA_TAG_NETC_H +#define __NET_DSA_TAG_NETC_H + +#include +#include + +#define NETC_TAG_MAX_LEN 14 + +#endif diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 1cb0740ba331..1b834e2a522e 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -325,6 +325,8 @@ struct ethtool_link_ksettings { extern int __ethtool_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *link_ksettings); +int netif_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *link_ksettings); struct ethtool_keee { __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -928,6 +930,19 @@ struct kernel_ethtool_ts_info { u32 rx_filters; }; +/* Bits for ethtool_ops::op_needs_rtnl + * LINKSETTINGS cover a number of commands, but in most cases we want to keep + * these bits separate, per GET and SET. GET is much easier to "unlock". + */ +#define ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS BIT(0) +#define ETHTOOL_OP_NEEDS_RTNL_SPFLAGS BIT(1) +#define ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM BIT(2) +#define ETHTOOL_OP_NEEDS_RTNL_SCHANNELS BIT(3) +#define ETHTOOL_OP_NEEDS_RTNL_SCOALESCE BIT(4) +#define ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM BIT(5) +#define ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM BIT(6) +#define ETHTOOL_OP_NEEDS_RTNL_RSS BIT(7) + /** * struct ethtool_ops - optional netdev operations * @supported_input_xfrm: supported types of input xfrm from %RXH_XFRM_*. @@ -954,6 +969,16 @@ struct kernel_ethtool_ts_info { * @supported_coalesce_params: supported types of interrupt coalescing. * @supported_ring_params: supported ring params. * @supported_hwtstamp_qualifiers: bitfield of supported hwtstamp qualifier. + * @op_needs_rtnl: mask of %ETHTOOL_OP_NEEDS_RTNL_* bits. + * For use with ops-locked drivers (ignored otherwise). Selects which + * ethtool callbacks driver needs to still be executed under rtnl_lock + * (in addition to the netdev instance lock). + * The following commonly used core APIs currently require rtnl_lock + * (this list may not be exhaustive): + * - phylink helpers (note that phydev is currently unsupported!) + * - netdev_update_features() + * - netif_set_real_num_tx_queues() + * * @get_drvinfo: Report driver/device information. Modern drivers no * longer have to implement this callback. Most fields are * correctly filled in by the core using system information, or @@ -1152,8 +1177,14 @@ struct kernel_ethtool_ts_info { * @get_mm_stats: Query the 802.3 MAC Merge layer statistics. * * All operations are optional (i.e. the function pointer may be set - * to %NULL) and callers must take this into account. Callers must - * hold the RTNL lock. + * to %NULL) and callers must take this into account. + * + * For traditional drivers callers hold ``rtnl_lock`` across the call. + * For "ops locked" drivers (see Documentation/networking/netdevices.rst) + * callers instead hold the netdev instance lock (``netdev_lock_ops``); + * ``rtnl_lock`` is additionally held only for callbacks for which + * the driver opts in via the matching ``ETHTOOL_OP_NEEDS_RTNL_*`` bit + * in @op_needs_rtnl. * * See the structures used by these operations for further documentation. * Note that for all operations using a structure ending with a zero- @@ -1176,6 +1207,7 @@ struct ethtool_ops { u32 supported_coalesce_params; u32 supported_ring_params; u32 supported_hwtstamp_qualifiers; + u32 op_needs_rtnl; void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); void (*get_regs)(struct net_device *, struct ethtool_regs *, void *); @@ -1349,6 +1381,7 @@ int ethtool_virtdev_set_link_ksettings(struct net_device *dev, * within RTNL. * @rss_indir_user_size: Number of user provided entries for the default * (context 0) indirection table. + * @phys_id_busy: Loop blinking the device LED is running. * @wol_enabled: Wake-on-LAN is enabled * @module_fw_flash_in_progress: Module firmware flashing is in progress. */ @@ -1356,6 +1389,7 @@ struct ethtool_netdev_state { struct xarray rss_ctx; struct mutex rss_lock; u32 rss_indir_user_size; + unsigned phys_id_busy:1; unsigned wol_enabled:1; unsigned module_fw_flash_in_progress:1; }; diff --git a/include/linux/fsl/netc_global.h b/include/linux/fsl/netc_global.h index fdecca8c90f0..5b8ff528d369 100644 --- a/include/linux/fsl/netc_global.h +++ b/include/linux/fsl/netc_global.h @@ -5,6 +5,7 @@ #define __NETC_GLOBAL_H #include +#include static inline u32 netc_read(void __iomem *reg) { @@ -16,4 +17,9 @@ static inline void netc_write(void __iomem *reg, u32 val) iowrite32(val, reg); } +static inline u64 netc_read64(void __iomem *reg) +{ + return ioread64(reg); +} + #endif diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h index 83a449b4d6ec..d3b6c476b91a 100644 --- a/include/linux/fsl/ntmp.h +++ b/include/linux/fsl/ntmp.h @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ -/* Copyright 2025 NXP */ +/* Copyright 2025-2026 NXP */ #ifndef __NETC_NTMP_H #define __NETC_NTMP_H +#include #include #include +#define NTMP_NULL_ENTRY_ID 0xffffffffU +#define IPFT_MAX_PLD_LEN 24 + struct maft_keye_data { u8 mac_addr[ETH_ALEN]; __le16 resv; @@ -29,6 +33,12 @@ struct netc_cbdr_regs { struct netc_tbl_vers { u8 maft_ver; u8 rsst_ver; + u8 fdbt_ver; + u8 vft_ver; + u8 bpt_ver; + u8 ipft_ver; + u8 ett_ver; + u8 ect_ver; }; struct netc_swcbd { @@ -61,6 +71,12 @@ struct ntmp_user { struct device *dev; struct netc_cbdr *ring; struct netc_tbl_vers tbl; + + /* NTMP table bitmaps for resource management */ + u32 ett_bitmap_size; + u32 ect_bitmap_size; + unsigned long *ett_gid_bitmap; /* only valid for switch */ + unsigned long *ect_gid_bitmap; /* only valid for switch */ }; struct maft_entry_data { @@ -68,10 +84,205 @@ struct maft_entry_data { struct maft_cfge_data cfge; }; +struct ipft_pld_byte { + u8 data; + u8 mask; +}; + +struct ipft_keye_data { + __le16 precedence; + __le16 resv0[3]; + __le16 frm_attr_flags; +#define IPFT_FAF_OVLAN BIT(2) +#define IPFT_FAF_IVLAN BIT(3) +#define IPFT_FAF_IP_HDR BIT(7) +#define IPFT_FAF_IP_VER6 BIT(8) +#define IPFT_FAF_L4_CODE GENMASK(11, 10) +#define IPFT_FAF_TCP_HDR 1 +#define IPFT_FAF_UDP_HDR 2 +#define IPFT_FAF_SCTP_HDR 3 +#define IPFT_FAF_WOL_MAGIC BIT(12) + __le16 frm_attr_flags_mask; + __le16 dscp; +#define IPFT_DSCP GENMASK(5, 0) +#define IPFT_DSCP_MASK GENMASK(11, 6) +#define IPFT_DSCP_MASK_ALL 0x3f + __le16 src_port; /* This field is reserved for ENETC */ +#define IPFT_SRC_PORT GENMASK(4, 0) +#define IPFT_SRC_PORT_MASK GENMASK(9, 5) +#define IPFT_SRC_PORT_MASK_ALL 0x1f + __be16 outer_vlan_tci; + __be16 outer_vlan_tci_mask; + u8 dmac[ETH_ALEN]; + u8 dmac_mask[ETH_ALEN]; + u8 smac[ETH_ALEN]; + u8 smac_mask[ETH_ALEN]; + __be16 inner_vlan_tci; + __be16 inner_vlan_tci_mask; + __be16 ethertype; + __be16 ethertype_mask; + u8 ip_protocol; + u8 ip_protocol_mask; + __le16 resv1[7]; + __be32 ip_src[4]; + __le32 resv2[2]; + __be32 ip_src_mask[4]; + __be16 l4_src_port; + __be16 l4_src_port_mask; + __le32 resv3; + __be32 ip_dst[4]; + __le32 resv4[2]; + __be32 ip_dst_mask[4]; + __be16 l4_dst_port; + __be16 l4_dst_port_mask; + __le32 resv5; + struct ipft_pld_byte byte[IPFT_MAX_PLD_LEN]; +}; + +struct ipft_cfge_data { + __le32 cfg; +#define IPFT_IPV GENMASK(3, 0) +#define IPFT_OIPV BIT(4) +#define IPFT_DR GENMASK(6, 5) +#define IPFT_ODR BIT(7) +#define IPFT_FLTFA GENMASK(10, 8) +#define IPFT_FLTFA_DISCARD 0 +#define IPFT_FLTFA_PERMIT 1 +/* Redirect is only for switch */ +#define IPFT_FLTFA_REDIRECT 2 +#define IPFT_IMIRE BIT(11) +#define IPFT_WOLTE BIT(12) +#define IPFT_FLTA GENMASK(14, 13) +#define IPFT_FLTA_RP 1 +#define IPFT_FLTA_IS 2 +#define IPFT_FLTA_SI_BITMAP 3 +#define IPFT_RPR GENMASK(16, 15) +#define IPFT_CTD BIT(17) +#define IPFT_HR GENMASK(21, 18) +#define IPFT_TIMECAPE BIT(22) +#define IPFT_RRT BIT(23) +#define IPFT_BL2F BIT(24) +#define IPFT_EVMEID GENMASK(31, 28) + __le32 flta_tgt; +}; + +struct ipft_entry_data { + u32 entry_id; /* hardware assigns entry ID */ + struct ipft_keye_data keye; + struct ipft_cfge_data cfge; +}; + +struct fdbt_keye_data { + u8 mac_addr[ETH_ALEN]; /* big-endian */ + __le16 resv0; + __le16 fid; +#define FDBT_FID GENMASK(11, 0) + __le16 resv1; +}; + +struct fdbt_cfge_data { + __le32 port_bitmap; +#define FDBT_PORT_BITMAP GENMASK(23, 0) + __le32 cfg; +#define FDBT_OETEID GENMASK(1, 0) +#define FDBT_EPORT GENMASK(6, 2) +#define FDBT_IMIRE BIT(7) +#define FDBT_CTD GENMASK(10, 9) +#define FDBT_DYNAMIC BIT(11) +#define FDBT_TIMECAPE BIT(12) + __le32 et_eid; +}; + +struct fdbt_entry_data { + u32 entry_id; + struct fdbt_keye_data keye; + struct fdbt_cfge_data cfge; + u8 acte; +#define FDBT_ACT_CNT GENMASK(6, 0) +#define FDBT_ACT_FLAG BIT(7) +}; + +struct vft_cfge_data { + __le32 bitmap_stg; +#define VFT_PORT_MEMBERSHIP GENMASK(23, 0) +#define VFT_STG_ID_MASK GENMASK(27, 24) +#define VFT_STG_ID(g) FIELD_PREP(VFT_STG_ID_MASK, (g)) + __le16 fid; +#define VFT_FID GENMASK(11, 0) + __le16 cfg; +#define VFT_MLO GENMASK(2, 0) +#define VFT_MFO GENMASK(4, 3) +#define VFT_IPMFE BIT(6) +#define VFT_IPMFLE BIT(7) +#define VFT_PGA BIT(8) +#define VFT_SFDA BIT(10) +#define VFT_OSFDA BIT(11) +#define VFT_FDBAFSS BIT(12) + __le32 eta_port_bitmap; +#define VFT_ETA_PORT_BITMAP GENMASK(23, 0) + __le32 et_eid; +}; + +struct ett_cfge_data { + __le16 efm_cfg; +#define ETT_EFM_MODE GENMASK(1, 0) +#define ETT_ESQA GENMASK(5, 4) +#define ETT_ECA GENMASK(8, 6) +#define ETT_ECA_INC 1 +#define ETT_EFM_LEN_CHANGE GENMASK(15, 9) +#define ETT_FRM_LEN_DEL_VLAN 0x7c +#define ETT_FRM_LEN_DEL_RTAG 0x7a +#define ETT_FRM_LEN_DEL_VLAN_RTAG 0x76 + __le16 efm_data_len; +#define ETT_EFM_DATA_LEN GENMASK(10, 0) + __le32 efm_eid; + __le32 ec_eid; + __le32 esqa_tgt_eid; +}; + +struct bpt_bpse_data { + __le32 amount_used; + __le32 amount_used_hwm; + u8 bpd_fc_state; +#define BPT_FC_STATE BIT(0) +#define BPT_BPD BIT(1) +} __packed; + +struct bpt_cfge_data { + u8 fccfg_sbpen; +#define BPT_FC_CFG GENMASK(2, 1) +#define BPT_FC_CFG_EN_BPFC 1 + u8 pfc_vector; + __le16 max_thresh; + __le16 fc_on_thresh; + __le16 fc_off_thresh; + __le16 sbp_thresh; + __le16 resv; + __le32 sbp_eid; + __le32 fc_ports; +}; + +union ntmp_fmt_eid { + __le32 index; +#define FMTEID_INDEX GENMASK(12, 0) + __le32 vuda_sqta; +#define FMTEID_VUDA GENMASK(1, 0) +#define FMTEID_VUDA_DEL_OTAG 2 +#define FMTEID_SQTA GENMASK(4, 2) +#define FMTEID_SQTA_DEL 2 +#define FMTEID_VUDA_SQTA BIT(13) + __le32 vara_vid; +#define FMTEID_VID GENMASK(11, 0) +#define FMTEID_VARA GENMASK(13, 12) +#define FMTEID_VARA_VID BIT(14) +}; + #if IS_ENABLED(CONFIG_NXP_NETC_LIB) int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, const struct netc_cbdr_regs *regs); void ntmp_free_cbdr(struct netc_cbdr *cbdr); +u32 ntmp_lookup_free_eid(unsigned long *bitmap, u32 size); +void ntmp_clear_eid_bitmap(unsigned long *bitmap, u32 entry_id); /* NTMP APIs */ int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id, @@ -83,6 +294,34 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, int count); int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count); +int ntmp_ipft_add_entry(struct ntmp_user *user, + struct ipft_entry_data *entry); +int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id); +int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id, + const struct fdbt_keye_data *keye, + const struct fdbt_cfge_data *cfge); +int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id, + const struct fdbt_cfge_data *cfge); +int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id); +int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port, + u32 *resume_entry_id, + struct fdbt_entry_data *entry); +int ntmp_fdbt_update_activity_element(struct ntmp_user *user); +int ntmp_fdbt_delete_ageing_entries(struct ntmp_user *user, u8 act_cnt); +int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port); +int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid, + const struct vft_cfge_data *cfge); +int ntmp_vft_update_entry(struct ntmp_user *user, u16 vid, + const struct vft_cfge_data *cfge); +int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid); +int ntmp_ett_add_entry(struct ntmp_user *user, u32 entry_id, + const struct ett_cfge_data *cfge); +int ntmp_ett_update_entry(struct ntmp_user *user, u32 entry_id, + const struct ett_cfge_data *cfge); +int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id); +int ntmp_ect_update_entry(struct ntmp_user *user, u32 entry_id); +int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id, + const struct bpt_cfge_data *cfge); #else static inline int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, const struct netc_cbdr_regs *regs) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index a97b1d01f3ac..18f9c662cf4c 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.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 - 2025 Intel Corporation + * Copyright (c) 2018 - 2026 Intel Corporation */ #ifndef LINUX_IEEE80211_EHT_H @@ -393,14 +393,24 @@ ieee80211_eht_oper_size_ok(const u8 *data, u8 len) return len >= needed; } +/* must validate ieee80211_eht_oper_size_ok() first */ +static inline const struct ieee80211_eht_operation_info * +ieee80211_eht_oper_info(const struct ieee80211_eht_operation *eht_oper) +{ + if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) + return NULL; + + return (const void *)eht_oper->optional; +} + /* must validate ieee80211_eht_oper_size_ok() first */ static inline u16 ieee80211_eht_oper_dis_subchan_bitmap(const struct ieee80211_eht_operation *eht_oper) { - const struct ieee80211_eht_operation_info *info = - (const void *)eht_oper->optional; + const struct ieee80211_eht_operation_info *info; - if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) + info = ieee80211_eht_oper_info(eht_oper); + if (!info) return 0; if (!(eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) @@ -485,26 +495,28 @@ struct ieee80211_multi_link_elem { #define IEEE80211_MED_SYNC_DELAY_DEFAULT 0x10ac #define IEEE80211_EML_CAP_EMLSR_SUPP 0x0001 -#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY 0x000e -#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_0US 0 -#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US 1 -#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_64US 2 -#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_128US 3 -#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US 4 -#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY 0x0070 +#define IEEE80211_EML_CAP_EML_PADDING_DELAY 0x000e +/* Described Tables 9-417i & 9-417k in 802.11be-2024, which have the same values */ +#define IEEE80211_EML_CAP_EML_PADDING_DELAY_0US 0 +#define IEEE80211_EML_CAP_EML_PADDING_DELAY_32US 1 +#define IEEE80211_EML_CAP_EML_PADDING_DELAY_64US 2 +#define IEEE80211_EML_CAP_EML_PADDING_DELAY_128US 3 +#define IEEE80211_EML_CAP_EML_PADDING_DELAY_256US 4 +#define IEEE80211_EML_CAP_EML_TRANSITION_DELAY 0x0070 +/* Described in Table 9-417j in 802.11be-2024 */ #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_0US 0 #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_16US 1 #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_32US 2 #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US 3 #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_128US 4 #define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US 5 +/* Described in Table 9-417l in 802.11be-2024 */ +#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_0US 0 +#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_32US 1 +#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_64US 2 +#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_128US 3 +#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_256US 4 #define IEEE80211_EML_CAP_EMLMR_SUPPORT 0x0080 -#define IEEE80211_EML_CAP_EMLMR_DELAY 0x0700 -#define IEEE80211_EML_CAP_EMLMR_DELAY_0US 0 -#define IEEE80211_EML_CAP_EMLMR_DELAY_32US 1 -#define IEEE80211_EML_CAP_EMLMR_DELAY_64US 2 -#define IEEE80211_EML_CAP_EMLMR_DELAY_128US 3 -#define IEEE80211_EML_CAP_EMLMR_DELAY_256US 4 #define IEEE80211_EML_CAP_TRANSITION_TIMEOUT 0x7800 #define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_0 0 #define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128US 1 @@ -517,7 +529,6 @@ struct ieee80211_multi_link_elem { #define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_16TU 8 #define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_32TU 9 #define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU 10 -#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU 11 #define IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS 0x000f #define IEEE80211_MLD_CAP_OP_SRS_SUPPORT 0x0010 @@ -749,11 +760,13 @@ static inline u16 ieee80211_mle_get_mld_capa_op(const u8 *data) } /* Defined in Figure 9-1074t in P802.11be_D7.0 */ -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE 0x0001 -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_RECO_MAX_LINKS_MASK 0x001e -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020 -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK 0x0040 -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE 0x0001 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_RECO_MAX_LINKS_MASK 0x001e +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK 0x0040 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080 +/* defined by UHR Draft P802.11bn_D1.3 Figure 9-1147 */ +#define IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM 0x0100 /** * ieee80211_mle_get_ext_mld_capa_op - returns the extended MLD capabilities @@ -1035,13 +1048,17 @@ ieee80211_mle_basic_sta_prof_bss_param_ch_cnt(const struct ieee80211_mle_per_sta #define IEEE80211_MLE_STA_RECONF_CONTROL_COMPLETE_PROFILE 0x0010 #define IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT 0x0020 #define IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT 0x0040 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE 0x0780 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_AP_REM 0 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_OP_PARAM_UPDATE 1 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_ADD_LINK 2 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_DEL_LINK 3 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_NSTR_STATUS 4 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_PARAMS_PRESENT 0x0800 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE 0x0780 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_AP_REM 0 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_OP_PARAM_UPDATE 1 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_ADD_LINK 2 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_DEL_LINK 3 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_NSTR_STATUS 4 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD 5 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_PARAMS_PRESENT 0x0800 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_NSTR_BMAP_SIZE 0x1000 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_NSTR_IND_BMAP_PRES 0x2000 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_DSO_INFO_PRESENT 0x4000 /** * ieee80211_mle_reconf_sta_prof_size_ok - validate reconfiguration multi-link @@ -1121,14 +1138,20 @@ static inline bool ieee80211_tid_to_link_map_size_ok(const u8 *data, size_t len) static inline u32 ieee80211_emlsr_pad_delay_in_us(u16 eml_cap) { + u32 emlsr_supp = + u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP); + + if (!emlsr_supp) + return 0; + /* IEEE Std 802.11be-2024 Table 9-417i—Encoding of the EMLSR * Padding Delay subfield. */ u32 pad_delay = u16_get_bits(eml_cap, - IEEE80211_EML_CAP_EMLSR_PADDING_DELAY); + IEEE80211_EML_CAP_EML_PADDING_DELAY); if (!pad_delay || - pad_delay > IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US) + pad_delay > IEEE80211_EML_CAP_EML_PADDING_DELAY_256US) return 0; return 32 * (1 << (pad_delay - 1)); @@ -1145,12 +1168,18 @@ static inline u32 ieee80211_emlsr_pad_delay_in_us(u16 eml_cap) static inline u32 ieee80211_emlsr_trans_delay_in_us(u16 eml_cap) { + u32 emlsr_supp = + u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP); + + if (!emlsr_supp) + return 0; + /* IEEE Std 802.11be-2024 Table 9-417j—Encoding of the EMLSR * Transition Delay subfield. */ u32 trans_delay = u16_get_bits(eml_cap, - IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY); + IEEE80211_EML_CAP_EML_TRANSITION_DELAY); /* invalid values also just use 0 */ if (!trans_delay || @@ -1161,11 +1190,73 @@ static inline u32 ieee80211_emlsr_trans_delay_in_us(u16 eml_cap) } /** - * ieee80211_eml_trans_timeout_in_us - Fetch the EMLSR Transition + * ieee80211_emlmr_pad_delay_in_us - Fetch the EMLMR Padding delay + * in microseconds + * @eml_cap: EML capabilities field value from common info field of + * the Multi-link element + * Return: the EMLMR Padding delay (in microseconds) encoded in the + * EML Capabilities field + */ + +static inline u32 ieee80211_emlmr_pad_delay_in_us(u16 eml_cap) +{ + u32 emlmr_supp = + u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLMR_SUPPORT); + + if (!emlmr_supp) + return 0; + + /* IEEE Std 802.11be-2024 Table 9-417k—Encoding of the EMLMR + * Padding Delay subfield. + */ + u32 pad_delay = u16_get_bits(eml_cap, + IEEE80211_EML_CAP_EML_PADDING_DELAY); + + if (!pad_delay || + pad_delay > IEEE80211_EML_CAP_EML_PADDING_DELAY_256US) + return 0; + + return 32 * (1 << (pad_delay - 1)); +} + +/** + * ieee80211_emlmr_trans_delay_in_us - Fetch the EMLMR Transition + * delay in microseconds + * @eml_cap: EML capabilities field value from common info field of + * the Multi-link element + * Return: the EMLMR Transition delay (in microseconds) encoded in the + * EML Capabilities field + */ + +static inline u32 ieee80211_emlmr_trans_delay_in_us(u16 eml_cap) +{ + u32 emlmr_supp = + u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLMR_SUPPORT); + + if (!emlmr_supp) + return 0; + + /* IEEE Std 802.11be-2024 Table 9-417l—Encoding of the EMLMR + * Transition Delay subfield. + */ + u32 trans_delay = + u16_get_bits(eml_cap, + IEEE80211_EML_CAP_EML_TRANSITION_DELAY); + + /* invalid values also just use 0 */ + if (!trans_delay || + trans_delay > IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_256US) + return 0; + + return 32 * (1 << (trans_delay - 1)); +} + +/** + * ieee80211_eml_trans_timeout_in_us - Fetch the EML Transition * timeout value in microseconds * @eml_cap: EML capabilities field value from common info field of * the Multi-link element - * Return: the EMLSR Transition timeout (in microseconds) encoded in + * Return: the EML Transition timeout (in microseconds) encoded in * the EML Capabilities field */ @@ -1178,7 +1269,7 @@ static inline u32 ieee80211_eml_trans_timeout_in_us(u16 eml_cap) IEEE80211_EML_CAP_TRANSITION_TIMEOUT); /* invalid values also just use 0 */ - if (!timeout || timeout > IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU) + if (!timeout || timeout > IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU) return 0; return 128 * (1 << (timeout - 1)); diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index 4b829bcb38b6..7eb15834531c 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -28,12 +28,73 @@ struct ieee80211s_hdr { u8 eaddr2[ETH_ALEN]; } __packed __aligned(2); +struct ieee80211_mesh_hwmp_preq_target { + u8 flags; + u8 addr[ETH_ALEN]; + __le32 sn; +} __packed; + +struct ieee80211_mesh_hwmp_preq_top { + u8 flags; + u8 hopcount; + u8 ttl; + __le32 preq_id; + u8 orig_addr[ETH_ALEN]; + __le32 orig_sn; + + /* optional AE, lifetime, metric, target */ + u8 variable[]; +} __packed; + +struct ieee80211_mesh_hwmp_preq_bottom { + __le32 lifetime; + __le32 metric; + u8 target_count; + struct ieee80211_mesh_hwmp_preq_target targets[]; +} __packed; + +struct ieee80211_mesh_hwmp_prep_top { + u8 flags; + u8 hopcount; + u8 ttl; + u8 target_addr[ETH_ALEN]; + __le32 target_sn; + + /* optional Target External Address */ + u8 variable[]; +} __packed; + +struct ieee80211_mesh_hwmp_prep_bottom { + __le32 lifetime; + __le32 metric; + u8 orig_addr[ETH_ALEN]; + __le32 orig_sn; +} __packed; + +struct ieee80211_mesh_hwmp_perr_dst { + u8 flags; + u8 addr[ETH_ALEN]; + __le32 sn; + /* optional Destination External Address */ + u8 variable[]; +} __packed; + +struct ieee80211_mesh_hwmp_perr { + u8 ttl; + u8 number_of_dst; + /* Destinations */ + u8 variable[]; +} __packed; + /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 #define MESH_FLAGS_AE_A5_A6 0x2 #define MESH_FLAGS_AE 0x3 #define MESH_FLAGS_PS_DEEP 0x4 +/* HWMP IE processing macros */ +#define AE_F (1<<6) + /** * enum ieee80211_preq_flags - mesh PREQ element flags * @@ -227,4 +288,155 @@ enum ieee80211_root_mode_identifier { IEEE80211_PROACTIVE_RANN = 4, }; +static inline bool ieee80211_mesh_preq_prep_ae_enabled(const u8 *ie) +{ + return ie[0] & AE_F; +} + +static inline struct ieee80211_mesh_hwmp_preq_bottom * +ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie) +{ + struct ieee80211_mesh_hwmp_preq_top *top = (void *)ie; + + return (void *)&top->variable[ + ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0]; +} + +static inline struct ieee80211_mesh_hwmp_prep_bottom * +ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie) +{ + struct ieee80211_mesh_hwmp_prep_top *top = (void *)ie; + + return (void *)&top->variable[ + ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0]; +} + +static inline struct ieee80211_mesh_hwmp_perr_dst * +ieee80211_mesh_hwmp_perr_get_dst(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr *perr_ie = (void *)ie; + struct ieee80211_mesh_hwmp_perr_dst *dst; + u8 *pos = perr_ie->variable; + int i; + + for (i = 0; i < dst_idx + 1; i++) { + dst = (void *)pos; + pos += sizeof(struct ieee80211_mesh_hwmp_perr_dst) + + ((dst->flags & AE_F) ? ETH_ALEN : 0) + /* Destination External Address */ + + 2 /* Reason Code */; + } + + return dst; +} + +static inline u8 * +ieee80211_mesh_hwmp_perr_get_addr(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx); + + return dst->addr; +} + +static inline u32 +ieee80211_mesh_hwmp_perr_get_sn(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx); + + return le32_to_cpu(dst->sn); +} + +static inline u16 +ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx); + + return get_unaligned_le16(&dst->variable[ + (dst->flags & AE_F) ? ETH_ALEN : 0]); +} + +/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */ +static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen) +{ + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(pos); + u8 target_count; + int needed; + + /* Check if the element contains flags */ + needed = sizeof(struct ieee80211_mesh_hwmp_preq_top); + if (elen < needed) + return false; + + /* Check if the element contains target_count */ + needed += (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0) + /* Originator External Address */ + + sizeof(struct ieee80211_mesh_hwmp_preq_bottom); + if (elen < needed) + return false; + + target_count = preq_elem_bottom->target_count; + /* IEEE Std 802.11-2016 Table 14-10 to 14-16 */ + if (target_count < 1) + return false; + + needed += target_count * sizeof(struct ieee80211_mesh_hwmp_preq_target); + return elen == needed; +} + +/* IEEE Std 802.11-2016 9.4.2.114 PREP element */ +static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen) +{ + u8 needed; + + /* Check if the element contains flags */ + needed = sizeof(struct ieee80211_mesh_hwmp_prep_top); + if (elen < needed) + return false; + + needed += (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0) + /* Target External Address */ + + sizeof(struct ieee80211_mesh_hwmp_prep_bottom); + return elen == needed; +} + +/* IEEE Std 802.11-2016 9.4.2.115 PERR element */ +static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen) +{ + struct ieee80211_mesh_hwmp_perr *perr_elem = (void *)pos; + const u8 *start = pos; + u8 number_of_dst; + int needed; + int i; + + needed = sizeof(struct ieee80211_mesh_hwmp_perr); + + /* Check if the element contains number of dst */ + if (elen < needed) + return false; + + pos += sizeof(struct ieee80211_mesh_hwmp_perr); + number_of_dst = perr_elem->number_of_dst; + + for (i = 0; i < number_of_dst; i++) { + struct ieee80211_mesh_hwmp_perr_dst *dst = (void *)pos; + u8 dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst); + + /* Check if the element contains flags */ + if (elen < pos - start + dst_len) + return false; + + dst_len += ((dst->flags & AE_F) ? ETH_ALEN : 0) + /* Destination External Address */ + + 2 /* Reason Code */; + needed += dst_len; + pos += dst_len; + } + + return elen == needed; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/include/linux/ieee80211-s1g.h b/include/linux/ieee80211-s1g.h index 22dde4cbc1b0..3f9626ad3d97 100644 --- a/include/linux/ieee80211-s1g.h +++ b/include/linux/ieee80211-s1g.h @@ -556,7 +556,7 @@ static inline bool ieee80211_s1g_check_tim(const struct ieee80211_tim_ie *tim, */ err = ieee80211_s1g_find_target_block(&enc_blk, &target_aid, tim->virtual_map, - (const u8 *)tim + tim_len + 2); + (const u8 *)tim + tim_len); if (err) return false; diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index d199f3ebdba0..597c9e559261 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -8,12 +8,15 @@ #define LINUX_IEEE80211_UHR_H #include +#include #include +#include "ieee80211-eht.h" #define IEEE80211_UHR_OPER_PARAMS_DPS_ENA 0x0001 #define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA 0x0002 #define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA 0x0004 #define IEEE80211_UHR_OPER_PARAMS_DBE_ENA 0x0008 +#define IEEE80211_UHR_OPER_PARAMS_DBE_BW 0x0070 struct ieee80211_uhr_operation { __le16 params; @@ -176,6 +179,29 @@ enum ieee80211_uhr_dbe_oper_bw { IEEE80211_UHR_DBE_OPER_BW_320_2 = 5, }; +/** + * ieee80211_uhr_dbe_bw_mhz - get bandwidth in MHz from UHR DBE bandwidth + * @bw: UHR DBE bandwidth + * + * Return: the bandwidth in MHz, or -1 for invalid values + */ +static inline int ieee80211_uhr_dbe_bw_mhz(enum ieee80211_uhr_dbe_oper_bw bw) +{ + switch (bw) { + case IEEE80211_UHR_DBE_OPER_BW_40: + return 40; + case IEEE80211_UHR_DBE_OPER_BW_80: + return 80; + case IEEE80211_UHR_DBE_OPER_BW_160: + return 160; + case IEEE80211_UHR_DBE_OPER_BW_320_1: + case IEEE80211_UHR_DBE_OPER_BW_320_2: + return 320; + default: + return -1; + } +} + /** * struct ieee80211_uhr_dbe_info - DBE operation information * @@ -334,6 +360,35 @@ ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_operation *oper) return npca->dis_subch_bmap; } +/* + * Note: cannot call this on the element coming from a beacon, + * must ensure ieee80211_uhr_oper_size_ok(..., false) first + */ +static inline const struct ieee80211_uhr_dbe_info * +ieee80211_uhr_oper_dbe_info(const struct ieee80211_uhr_operation *oper) +{ + const u8 *pos = oper->variable; + + if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DBE_ENA))) + return NULL; + + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_ENA)) + pos += sizeof(struct ieee80211_uhr_dps_info); + + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) { + const struct ieee80211_uhr_npca_info *npca = (const void *)pos; + + pos += sizeof(*npca); + if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)) + pos += sizeof(npca->dis_subch_bmap[0]); + } + + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA)) + pos += sizeof(struct ieee80211_uhr_p_edca_info); + + return (const void *)pos; +} + #define IEEE80211_UHR_MAC_CAP0_DPS_SUPP 0x01 #define IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP 0x02 #define IEEE80211_UHR_MAC_CAP0_DPS_AP_STATIC_HCM_SUPP 0x04 @@ -372,6 +427,12 @@ ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_operation *oper) #define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES 0x08 #define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES 0x10 +struct ieee80211_uhr_cap_dbe { + u8 cap; + /* present 0, 1 or 2 times depending on _PRES bits */ + struct ieee80211_eht_mcs_nss_supp_bw eht_mcs_map[]; +} __packed; + /** * enum ieee80211_uhr_dbe_max_supported_bw - DBE Maximum Supported Bandwidth * @@ -391,33 +452,56 @@ enum ieee80211_uhr_dbe_max_supported_bw { }; struct ieee80211_uhr_cap_mac { - u8 mac_cap[5]; + u8 mac_cap[6]; +} __packed; + +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x00000001 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x00000002 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x00000004 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x00000008 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x00000010 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x00000020 +#define IEEE80211_UHR_PHY_CAP_ELR_TX 0x00000040 +#define IEEE80211_UHR_PHY_CAP_ELR_RX 0x00000080 +#define IEEE80211_UHR_PHY_CAP_PART_BW_DL_MUMIMO 0x00000100 +#define IEEE80211_UHR_PHY_CAP_PART_BW_UL_MUMIMO 0x00000200 +#define IEEE80211_UHR_PHY_CAP_MCS15 0x00000400 +#define IEEE80211_UHR_PHY_CAP_2XLDPC_TX 0x00000800 +#define IEEE80211_UHR_PHY_CAP_2XLDPC_RX 0x00001000 +#define IEEE80211_UHR_PHY_CAP_UEQM_TX_MAX_NSS 0x00006000 +#define IEEE80211_UHR_PHY_CAP_UEQM_RX_MAX_NSS 0x00018000 +#define IEEE80211_UHR_PHY_CAP_CO_BF_JOINT_SOUNDING 0x00040000 +#define IEEE80211_UHR_PHY_CAP_IM_TX 0x00080000 +#define IEEE80211_UHR_PHY_CAP_IM_RX 0x00100000 +#define IEEE80211_UHR_PHY_CAP_CO_SR_MODE_1 0x00200000 +#define IEEE80211_UHR_PHY_CAP_CO_SR_MODE_2 0x00400000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_20_IN_PBW_20 0x00800000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_40_IN_PBW_40 0x01000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_80 0x02000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_160 0x04000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_320 0x08000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_20_IN_PBW_GE80 0x10000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_40_IN_PBW_GE80 0x20000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_60_IN_PBW_GE80 0x40000000 +#define IEEE80211_UHR_PHY_CAP_DRU_RRU_HYBRID_MODE 0x80000000 + +struct ieee80211_uhr_cap_phy { + __le32 cap; + u8 reserved; } __packed; struct ieee80211_uhr_cap { struct ieee80211_uhr_cap_mac mac; - /* DBE, PHY capabilities */ + struct ieee80211_uhr_cap_phy phy; + /* optional DBE capabilities */ u8 variable[]; } __packed; -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x01 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x02 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x04 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x08 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x10 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x20 -#define IEEE80211_UHR_PHY_CAP_ELR_RX 0x40 -#define IEEE80211_UHR_PHY_CAP_ELR_TX 0x80 - -struct ieee80211_uhr_cap_phy { - u8 cap; -} __packed; - static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len, bool from_ap) { const struct ieee80211_uhr_cap *cap = (const void *)data; - size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_cap_phy); + size_t needed = sizeof(*cap); if (len < needed) return false; @@ -427,42 +511,60 @@ static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len, * in the UHR MAC Capabilities Information field. */ if (from_ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) { - u8 dbe; + const struct ieee80211_uhr_cap_dbe *dbe; - needed += 1; + needed += sizeof(struct ieee80211_uhr_cap_dbe); if (len < needed) return false; - dbe = cap->variable[0]; + dbe = (const void *)cap->variable; - if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES) - needed += 3; + if (dbe->cap & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES) + needed += sizeof(dbe->eht_mcs_map[0]); - if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES) - needed += 3; + if (dbe->cap & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES) + needed += sizeof(dbe->eht_mcs_map[0]); } return len >= needed; } -static inline const struct ieee80211_uhr_cap_phy * -ieee80211_uhr_phy_cap(const struct ieee80211_uhr_cap *cap, bool from_ap) +#define IEEE80211_UHR_OM_PU_TO_128TU 11 + +/** + * ieee80211_uhr_capa_get_om_pu_to_us - get OM parameter update timeout in usec + * @cap: the UHR capability element, size must be validated + * + * Return: the OM parameter update timeout in usec, or -1 if it's not valid + */ +static inline int +ieee80211_uhr_capa_get_om_pu_to_us(const struct ieee80211_uhr_cap *cap) { - u8 offs = 0; + u8 timeout; - if (from_ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) { - u8 dbe = cap->variable[0]; + timeout = u8_get_bits(cap->mac.mac_cap[3], + IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH); + timeout <<= 2; + timeout |= u8_get_bits(cap->mac.mac_cap[2], + IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW); - offs += 1; + if (timeout > IEEE80211_UHR_OM_PU_TO_128TU) + return -1; - if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES) - offs += 3; + if (!timeout) + return 0; - if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES) - offs += 3; - } + return 128 << (timeout - 1); +} - return (const void *)&cap->variable[offs]; +/* only valid from AP, must check ieee80211_uhr_capa_size_ok(..., true) */ +static inline const struct ieee80211_uhr_cap_dbe * +ieee80211_uhr_dbe_cap(const struct ieee80211_uhr_cap *cap) +{ + if (!(cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP)) + return NULL; + + return (const void *)cap->variable; } #define IEEE80211_SMD_INFO_CAPA_DL_DATA_FWD 0x01 @@ -476,4 +578,70 @@ struct ieee80211_smd_info { __le16 timeout; } __packed; +enum ieee80211_protected_uhr_action { + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST = 0, + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_RESPONSE = 1, + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY = 2, +}; + +enum ieee80211_uhr_link_reconfig_request_type { + IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_PREP = 0, + IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_EXEC = 1, + IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST = 3, +}; + +enum ieee80211_uhr_link_reconfig_response_type { + IEEE80211_UHR_LINK_RECONFIG_RESPONSE_ST_PREP = 0, + IEEE80211_UHR_LINK_RECONFIG_RESPONSE_ST_EXEC = 1, +}; + +enum ieee80211_uhr_link_reconfig_notify_type { + IEEE80211_UHR_LINK_RECONFIG_NOTIFY_DL_DRAINED = 2, + IEEE80211_UHR_LINK_RECONFIG_NOTIFY_OMP_RESPONSE = 3, +}; + +enum ieee80211_uhr_mode_change_control { + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID = 0x003f, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE = 0x0040, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_UPDATE = 0x0080, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH = 0x0f00, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_SPECIFIC = 0xf000, +}; + +enum ieee80211_uhr_mode_change_mode_id { + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DPS = 0, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_NPCA = 1, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DUO = 2, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DSO = 3, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_P_EDCA = 4, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_ELR_RX = 5, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_AOM = 6, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_LLI = 7, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_CO_BF = 8, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_CO_SR = 9, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_EMLSR = 10, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE = 11, +}; + +struct ieee80211_uhr_mode_change_tuple { + __le16 control; + u8 variable[]; +} __packed; + +static inline int +ieee80211_uhr_mode_change_tuple_size(const struct ieee80211_uhr_mode_change_tuple *tuple) +{ + return sizeof(*tuple) + + le16_get_bits(tuple->control, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH); +} + +#define for_each_uhr_mode_change_tuple(data, len, tuple) \ + for (tuple = (const void *)(data); \ + (len) - ((const u8 *)tuple - (data)) >= sizeof(*tuple) && \ + (len) - ((const u8 *)tuple - (data)) >= \ + ieee80211_uhr_mode_change_tuple_size(tuple); \ + tuple = (const void *)((const u8 *)tuple + \ + ieee80211_uhr_mode_change_tuple_size(tuple))) + #endif /* LINUX_IEEE80211_UHR_H */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 23f9df9be837..d40484451e9a 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1174,6 +1174,22 @@ struct ieee80211_mgmt { u8 control; u8 variable[]; } __packed eml_omn; + struct { + u8 dialog_token; + u8 type; + u8 variable[]; + } __packed uhr_link_reconf_req; + struct { + u8 dialog_token; + u8 type; + u8 count; + u8 variable[]; + } __packed uhr_link_reconf_resp; + struct { + u8 dialog_token; + u8 type; + u8 variable[]; + } __packed uhr_link_reconf_notif; }; } __packed action; DECLARE_FLEX_ARRAY(u8, body); /* Generic frame body */ @@ -1837,6 +1853,7 @@ enum ieee80211_category { WLAN_CATEGORY_VHT = 21, WLAN_CATEGORY_S1G = 22, WLAN_CATEGORY_PROTECTED_EHT = 37, + WLAN_CATEGORY_PROTECTED_UHR = 43, WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, WLAN_CATEGORY_VENDOR_SPECIFIC = 127, }; @@ -2236,6 +2253,7 @@ struct ieee80211_multiple_bssid_configuration { #define WLAN_AKM_SUITE_WFA_DPP SUITE(WLAN_OUI_WFA, 2) #define WLAN_MAX_KEY_LEN 32 +#define WLAN_MAX_SECURE_LTF_KEYSEED_LEN 48 #define WLAN_PMK_NAME_LEN 16 #define WLAN_PMKID_LEN 16 diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index c5fe3b2a53e8..75673b8bffcb 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -36,31 +36,60 @@ struct br_ip_list { struct br_ip addr; }; -#define BR_HAIRPIN_MODE BIT(0) -#define BR_BPDU_GUARD BIT(1) -#define BR_ROOT_BLOCK BIT(2) -#define BR_MULTICAST_FAST_LEAVE BIT(3) -#define BR_ADMIN_COST BIT(4) -#define BR_LEARNING BIT(5) -#define BR_FLOOD BIT(6) +enum bridge_flags_bit { + BR_HAIRPIN_MODE_BIT, + BR_BPDU_GUARD_BIT, + BR_ROOT_BLOCK_BIT, + BR_MULTICAST_FAST_LEAVE_BIT, + BR_ADMIN_COST_BIT, + BR_LEARNING_BIT, + BR_FLOOD_BIT, + BR_PROMISC_BIT, + BR_PROXYARP_BIT, + BR_LEARNING_SYNC_BIT, + BR_PROXYARP_WIFI_BIT, + BR_MCAST_FLOOD_BIT, + BR_MULTICAST_TO_UNICAST_BIT, + BR_VLAN_TUNNEL_BIT, + BR_BCAST_FLOOD_BIT, + BR_NEIGH_SUPPRESS_BIT, + BR_ISOLATED_BIT, + BR_MRP_AWARE_BIT, + BR_MRP_LOST_CONT_BIT, + BR_MRP_LOST_IN_CONT_BIT, + BR_TX_FWD_OFFLOAD_BIT, + BR_PORT_LOCKED_BIT, + BR_PORT_MAB_BIT, + BR_NEIGH_VLAN_SUPPRESS_BIT, + BR_NEIGH_FORWARD_GRAT_BIT, +}; + +#define BR_HAIRPIN_MODE BIT(BR_HAIRPIN_MODE_BIT) +#define BR_BPDU_GUARD BIT(BR_BPDU_GUARD_BIT) +#define BR_ROOT_BLOCK BIT(BR_ROOT_BLOCK_BIT) +#define BR_MULTICAST_FAST_LEAVE BIT(BR_MULTICAST_FAST_LEAVE_BIT) +#define BR_ADMIN_COST BIT(BR_ADMIN_COST_BIT) +#define BR_LEARNING BIT(BR_LEARNING_BIT) +#define BR_FLOOD BIT(BR_FLOOD_BIT) #define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING) -#define BR_PROMISC BIT(7) -#define BR_PROXYARP BIT(8) -#define BR_LEARNING_SYNC BIT(9) -#define BR_PROXYARP_WIFI BIT(10) -#define BR_MCAST_FLOOD BIT(11) -#define BR_MULTICAST_TO_UNICAST BIT(12) -#define BR_VLAN_TUNNEL BIT(13) -#define BR_BCAST_FLOOD BIT(14) -#define BR_NEIGH_SUPPRESS BIT(15) -#define BR_ISOLATED BIT(16) -#define BR_MRP_AWARE BIT(17) -#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_PORT_MAB BIT(22) -#define BR_NEIGH_VLAN_SUPPRESS BIT(23) +#define BR_PROMISC BIT(BR_PROMISC_BIT) +#define BR_PROXYARP BIT(BR_PROXYARP_BIT) +#define BR_LEARNING_SYNC BIT(BR_LEARNING_SYNC_BIT) +#define BR_PROXYARP_WIFI BIT(BR_PROXYARP_WIFI_BIT) +#define BR_MCAST_FLOOD BIT(BR_MCAST_FLOOD_BIT) +#define BR_MULTICAST_TO_UNICAST BIT(BR_MULTICAST_TO_UNICAST_BIT) +#define BR_VLAN_TUNNEL BIT(BR_VLAN_TUNNEL_BIT) +#define BR_BCAST_FLOOD BIT(BR_BCAST_FLOOD_BIT) +#define BR_NEIGH_SUPPRESS BIT(BR_NEIGH_SUPPRESS_BIT) +#define BR_ISOLATED BIT(BR_ISOLATED_BIT) +#define BR_MRP_AWARE BIT(BR_MRP_AWARE_BIT) +#define BR_MRP_LOST_CONT BIT(BR_MRP_LOST_CONT_BIT) +#define BR_MRP_LOST_IN_CONT BIT(BR_MRP_LOST_IN_CONT_BIT) +#define BR_TX_FWD_OFFLOAD BIT(BR_TX_FWD_OFFLOAD_BIT) +#define BR_PORT_LOCKED BIT(BR_PORT_LOCKED_BIT) +#define BR_PORT_MAB BIT(BR_PORT_MAB_BIT) +#define BR_NEIGH_VLAN_SUPPRESS BIT(BR_NEIGH_VLAN_SUPPRESS_BIT) +#define BR_NEIGH_FORWARD_GRAT BIT(BR_NEIGH_FORWARD_GRAT_BIT) #define BR_DEFAULT_AGEING_TIME (300 * HZ) diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 80166eb62f41..5f3e206c7a73 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -22,6 +22,7 @@ struct tun_msg_ctl { #if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE) struct socket *tun_get_socket(struct file *); struct ptr_ring *tun_get_tx_ring(struct file *file); +void tun_wake_queue(struct file *file, int consumed); static inline bool tun_is_xdp_frame(void *ptr) { @@ -55,6 +56,8 @@ static inline struct ptr_ring *tun_get_tx_ring(struct file *f) return ERR_PTR(-EINVAL); } +static inline void tun_wake_queue(struct file *f, int consumed) {} + static inline bool tun_is_xdp_frame(void *ptr) { return false; diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 073b30a9b850..3a2d35a9f307 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -92,15 +92,166 @@ struct ip_mc_list { struct rcu_head rcu; }; -/* V3 exponential field decoding */ -#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value)) -#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \ - ((value) < (thresh) ? (value) : \ - ((IGMPV3_MASK(value, nbmant) | (1<<(nbmant))) << \ - (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp)))) +/* RFC3376, relevant sections: + * - 4.1.1. Maximum Response Code + * - 4.1.7. QQIC (Querier's Query Interval Code) + * + * For both MRC and QQIC, values >= 128 use the same floating-point + * encoding as follows: + * + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |1| exp | mant | + * +-+-+-+-+-+-+-+-+ + */ +#define IGMPV3_FP_EXP(value) (((value) >> 4) & 0x07) +#define IGMPV3_FP_MAN(value) ((value) & 0x0f) -#define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value) -#define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value) +/* IGMPv3 floating-point exponential field min threshold */ +#define IGMPV3_EXP_MIN_THRESHOLD 128 +/* IGMPv3 FP max threshold (mant = 0xF, exp = 7) -> 31744 */ +#define IGMPV3_EXP_MAX_THRESHOLD 31744 + +/* V3 exponential field encoding */ + +/* IGMPv3 MRC/QQIC 8-bit exponential field encode + * + * RFC3376, 4.1.1 & 4.1.7. defines only the decoding formula: + * MRT/QQI = (mant | 0x10) << (exp + 3) + * + * but does NOT define the encoding procedure. To derive exponent: + * + * For any value of mantissa and exponent, the decoding formula + * indicates that the "hidden bit" (0x10) is shifted 4 bits left + * to sit above the 4-bit mantissa. The RFC again shifts this + * entire block left by (exp + 3) to reconstruct the value. + * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3). + * + * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7. + * This is the MSB at the 0-based bit position: (exp + 7). + * Since fls() is 1-based, fls(value) - 1 = exp + 7. + * + * Therefore: + * exp = fls(value) - 8 + * mant = (value >> (exp + 3)) & 0x0F + * + * Final encoding formula: + * 0x80 | (exp << 4) | mant + * + * Example (value = 3200): + * 0 1 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200) + * | ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Encoded: + * 0x80 | (4 << 4) | 9 = 0xC9 + */ +static inline u8 igmpv3_exp_field_encode(unsigned long value) +{ + u8 mc_exp, mc_man; + + /* MRC/QQIC < 128 is literal */ + if (value < IGMPV3_EXP_MIN_THRESHOLD) + return value; + + /* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */ + if (value >= IGMPV3_EXP_MAX_THRESHOLD) + return 0xFF; + + mc_exp = fls(value) - 8; + mc_man = (value >> (mc_exp + 3)) & 0x0F; + + return 0x80 | (mc_exp << 4) | mc_man; +} + +/* Calculate Maximum Response Code from Max Resp Time + * + * RFC3376, relevant sections: + * - 4.1.1. Maximum Response Code + * - 8.3. Query Response Interval + * + * MRC represents the encoded form of Max Resp Time (MRT); once + * decoded, the resulting value is in units of 0.1 seconds (100 ms). + */ +static inline u8 igmpv3_mrc(unsigned long mrt) +{ + return igmpv3_exp_field_encode(mrt); +} + +/* Calculate Querier's Query Interval Code from Querier's Query Interval + * + * RFC3376, relevant sections: + * - 4.1.7. QQIC (Querier's Query Interval Code) + * - 8.2. Query Interval + * - 8.12. Older Version Querier Present Timeout + * (the [Query Interval] in the last Query received) + * + * QQIC represents the encoded form of Querier's Query Interval (QQI); + * once decoded, the resulting value is in units of seconds. + */ +static inline u8 igmpv3_qqic(unsigned long qi) +{ + return igmpv3_exp_field_encode(qi); +} + +/* V3 exponential field decoding */ + +/* IGMPv3 MRC/QQIC 8-bit exponential field decode + * + * RFC3376, 4.1.1 & 4.1.7. defines the decoding formula: + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |1| exp | mant | + * +-+-+-+-+-+-+-+-+ + * Max Resp Time = (mant | 0x10) << (exp + 3) + * QQI = (mant | 0x10) << (exp + 3) + */ +static inline unsigned long igmpv3_exp_field_decode(const u8 code) +{ + if (code < IGMPV3_EXP_MIN_THRESHOLD) { + return code; + } else { + unsigned long mc_man, mc_exp; + + mc_exp = IGMPV3_FP_EXP(code); + mc_man = IGMPV3_FP_MAN(code); + + return (mc_man | 0x10) << (mc_exp + 3); + } +} + +/* Calculate Max Resp Time from Maximum Response Code + * + * RFC3376, relevant sections: + * - 4.1.1. Maximum Response Code + * - 8.3. Query Response Interval + * + * After decode, MRC represents the Maximum Response Time (MRT) in + * units of 0.1 seconds (100 ms). + */ +static inline unsigned long igmpv3_mrt(const struct igmpv3_query *ih3) +{ + return igmpv3_exp_field_decode(ih3->code); +} + +/* Calculate Querier's Query Interval from Querier's Query Interval Code + * + * RFC3376, relevant sections: + * - 4.1.7. QQIC (Querier's Query Interval Code) + * - 8.2. Query Interval + * - 8.12. Older Version Querier Present Timeout + * (the [Query Interval] in the last Query received) + * + * After decode, QQIC represents the Querier's Query Interval in units + * of seconds. + */ +static inline unsigned long igmpv3_qqi(const struct igmpv3_query *ih3) +{ + return igmpv3_exp_field_decode(ih3->qqic); +} static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len) { diff --git a/include/linux/ks8851_mll.h b/include/linux/ks8851_mll.h deleted file mode 100644 index 57c0a39ed796..000000000000 --- a/include/linux/ks8851_mll.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * ks8861_mll platform data struct definition - * Copyright (c) 2012 BTicino S.p.A. - */ - -#ifndef _LINUX_KS8851_MLL_H -#define _LINUX_KS8851_MLL_H - -#include - -/** - * struct ks8851_mll_platform_data - Platform data of the KS8851_MLL network driver - * @macaddr: The MAC address of the device, set to all 0:s to use the on in - * the chip. - */ -struct ks8851_mll_platform_data { - u8 mac_addr[ETH_ALEN]; -}; - -#endif diff --git a/include/linux/llc.h b/include/linux/llc.h index b965314d017f..944e9e213112 100644 --- a/include/linux/llc.h +++ b/include/linux/llc.h @@ -1,14 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * IEEE 802.2 User Interface SAPs for Linux, data structures and indicators. * * Copyright (c) 2001 by Jay Schulist - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #ifndef __LINUX_LLC_H #define __LINUX_LLC_H diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 04b96c5abb57..b1871c0821d0 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -343,9 +343,11 @@ struct mlx5_cmd_mailbox { struct mlx5_cmd_mailbox *next; }; +struct mlx5_dma_pool_page; struct mlx5_buf_list { void *buf; dma_addr_t map; + struct mlx5_dma_pool_page *frag_page; }; struct mlx5_frag_buf { @@ -545,6 +547,7 @@ struct mlx5_debugfs_entries { struct dentry *eq_debugfs; struct dentry *cq_debugfs; struct dentry *cmdif_debugfs; + struct dentry *frag_buf_dma_pools_debugfs; struct dentry *pages_debugfs; struct dentry *lag_debugfs; }; @@ -554,10 +557,18 @@ enum mlx5_func_type { MLX5_VF, MLX5_SF, MLX5_HOST_PF, + MLX5_SPF, MLX5_EC_VF, MLX5_FUNC_TYPE_NUM, + MLX5_FUNC_TYPE_NONE = MLX5_FUNC_TYPE_NUM, }; +enum mlx5_page_mgt_mode { + MLX5_PAGE_MGT_MODE_FUNC_ID, + MLX5_PAGE_MGT_MODE_VHCA_ID, +}; + +struct mlx5_frag_buf_node_pools; struct mlx5_ft_pool; struct mlx5_priv { /* IRQ table valid only for real pci devices PF or VF */ @@ -575,20 +586,23 @@ struct mlx5_priv { u32 fw_pages_alloc_failed; u32 give_pages_dropped; u32 reclaim_pages_discard; + enum mlx5_page_mgt_mode page_mgt_mode; struct mlx5_core_health health; struct list_head traps; struct mlx5_debugfs_entries dbg; - /* start: alloc staff */ + /* start: alloc stuff */ /* protect buffer allocation according to numa node */ struct mutex alloc_mutex; int numa_node; struct mutex pgdir_mutex; struct list_head pgdir_list; - /* end: alloc staff */ + + struct mlx5_frag_buf_node_pools **frag_buf_node_pools; + /* end: alloc stuff */ struct mlx5_adev **adev; int adev_idx; @@ -1034,6 +1048,8 @@ 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_pages_by_func_type_debugfs_init(struct mlx5_core_dev *dev); +void mlx5_pages_by_func_type_debugfs_cleanup(struct mlx5_core_dev *dev); int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot); int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev); void mlx5_register_debugfs(void); diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h index 67256e776566..a0dd162baa78 100644 --- a/include/linux/mlx5/eswitch.h +++ b/include/linux/mlx5/eswitch.h @@ -63,7 +63,13 @@ struct mlx5_eswitch_rep { void mlx5_eswitch_register_vport_reps(struct mlx5_eswitch *esw, const struct mlx5_eswitch_rep_ops *ops, u8 rep_type); +void +mlx5_eswitch_register_vport_reps_nested(struct mlx5_eswitch *esw, + const struct mlx5_eswitch_rep_ops *ops, + u8 rep_type); void mlx5_eswitch_unregister_vport_reps(struct mlx5_eswitch *esw, u8 rep_type); +void mlx5_eswitch_unregister_vport_reps_nested(struct mlx5_eswitch *esw, + u8 rep_type); void *mlx5_eswitch_get_proto_dev(struct mlx5_eswitch *esw, u16 vport_num, u8 rep_type); @@ -217,7 +223,7 @@ static inline bool is_mdev_switchdev_mode(struct mlx5_core_dev *dev) static inline u16 mlx5_eswitch_manager_vport(struct mlx5_core_dev *dev) { return mlx5_core_is_ecpf_esw_manager(dev) ? - MLX5_VPORT_ECPF : MLX5_VPORT_PF; + MLX5_VPORT_ECPF : MLX5_VPORT_HOST_PF; } #endif diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 49f3ad4b1a7c..4f59b7e8a3d5 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1935,7 +1935,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 max_flow_counter_31_16[0x10]; u8 max_wqe_sz_sq_dc[0x10]; - u8 reserved_at_2e0[0x7]; + u8 query_host_net_function_num_max[0x5]; + u8 reserved_at_2e5[0x2]; u8 max_qp_mcg[0x19]; u8 reserved_at_300[0x10]; @@ -1985,7 +1986,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 basic_cyclic_rcv_wqe[0x1]; u8 reserved_at_381[0x2]; u8 log_max_rmp[0x5]; - u8 reserved_at_388[0x3]; + u8 sd_group_size[0x1]; + u8 reserved_at_389[0x2]; u8 log_max_rqt[0x5]; u8 reserved_at_390[0x3]; u8 log_max_rqt_size[0x5]; @@ -2027,7 +2029,7 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 log_max_current_mc_list[0x5]; u8 reserved_at_3f8[0x1]; u8 silent_mode_query[0x1]; - u8 reserved_at_3fa[0x1]; + u8 query_host_net_function_v1[0x1]; u8 log_max_current_uc_list[0x5]; u8 general_obj_types[0x40]; @@ -4468,7 +4470,9 @@ struct mlx5_ifc_nic_vport_context_bits { u8 reserved_at_100[0x1]; u8 sd_group[0x3]; - u8 reserved_at_104[0x1c]; + u8 reserved_at_104[0x4]; + u8 sd_group_size[0x8]; + u8 reserved_at_110[0x10]; u8 reserved_at_120[0x10]; u8 mtu[0x10]; @@ -4486,8 +4490,8 @@ struct mlx5_ifc_nic_vport_context_bits { u8 promisc_all[0x1]; u8 reserved_at_783[0x2]; u8 allowed_list_type[0x3]; - u8 reserved_at_788[0xc]; - u8 allowed_list_size[0xc]; + u8 reserved_at_788[0x8]; + u8 allowed_list_size[0x10]; struct mlx5_ifc_mac_address_layout_bits permanent_address; @@ -8452,7 +8456,9 @@ struct mlx5_ifc_enable_hca_in_bits { u8 op_mod[0x10]; u8 embedded_cpu_function[0x1]; - u8 reserved_at_41[0xf]; + u8 reserved_at_41[0x2]; + u8 function_id_type[0x1]; + u8 reserved_at_44[0xc]; u8 function_id[0x10]; u8 reserved_at_60[0x20]; @@ -8497,7 +8503,9 @@ struct mlx5_ifc_disable_hca_in_bits { u8 op_mod[0x10]; u8 embedded_cpu_function[0x1]; - u8 reserved_at_41[0xf]; + u8 reserved_at_41[0x2]; + u8 function_id_type[0x1]; + u8 reserved_at_44[0xc]; u8 function_id[0x10]; u8 reserved_at_60[0x20]; @@ -12700,6 +12708,54 @@ struct mlx5_ifc_host_params_context_bits { u8 reserved_at_80[0x180]; }; +enum mlx5_ifc_vhca_state { + MLX5_VHCA_STATE_INVALID = 0x0, + MLX5_VHCA_STATE_ALLOCATED = 0x1, + MLX5_VHCA_STATE_ACTIVE = 0x2, + MLX5_VHCA_STATE_IN_USE = 0x3, + MLX5_VHCA_STATE_TEARDOWN_REQUEST = 0x4, +}; + +enum { + MLX5_PCI_PF_TYPE_EXTERNAL_HOST_PF = 0x0, + MLX5_PCI_PF_TYPE_SATELLITE_PF = 0x1, +}; + +struct mlx5_ifc_network_function_params_bits { + u8 host_number[0x8]; + u8 pci_pf_type[0x4]; + u8 reserved_at_c[0x4]; + u8 pci_num_vfs[0x10]; + + u8 pci_total_vfs[0x10]; + u8 pci_bus[0x8]; + u8 pci_device_function[0x8]; + + u8 vhca_id[0x10]; + u8 vhca_state[0x4]; + u8 reserved_at_54[0xc]; + + u8 reserved_at_60[0xa]; + u8 esw_vport_manual[0x1]; + u8 pci_bus_assigned[0x1]; + u8 pci_vf_info_valid[0x1]; + u8 reserved_at_6d[0x13]; + + u8 pci_vf_stride[0x10]; + u8 pci_first_vf_offset[0x10]; + + u8 reserved_at_a0[0x160]; +}; + +union mlx5_ifc_net_function_params_bits { + struct mlx5_ifc_host_params_context_bits host_params_context; + struct mlx5_ifc_network_function_params_bits network_function_params; +}; + +enum { + MLX5_QUERY_ESW_FUNC_OP_MOD_LAYOUT_V1 = BIT(14), +}; + struct mlx5_ifc_query_esw_functions_in_bits { u8 opcode[0x10]; u8 reserved_at_10[0x10]; @@ -12716,12 +12772,16 @@ struct mlx5_ifc_query_esw_functions_out_bits { u8 syndrome[0x20]; - u8 reserved_at_40[0x40]; + u8 reserved_at_40[0x20]; - struct mlx5_ifc_host_params_context_bits host_params_context; + u8 net_function_num[0x8]; + u8 reserved_at_68[0x18]; - u8 reserved_at_280[0x180]; - u8 host_sf_enable[][0x40]; + union { + u8 reserved_at_80[0x380]; + DECLARE_FLEX_ARRAY(union mlx5_ifc_net_function_params_bits, + net_function_params); + }; }; struct mlx5_ifc_sf_partition_bits { diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 282ed5442282..ee34d3ed335f 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -51,8 +51,8 @@ enum { /* Vport number for each function must keep unchanged */ enum { - MLX5_VPORT_PF = 0x0, - MLX5_VPORT_FIRST_VF = 0x1, + MLX5_VPORT_HOST_PF = 0x0, + MLX5_VPORT_FIRST_HOST_VF = 0x1, MLX5_VPORT_ECPF = 0xfffe, MLX5_VPORT_UPLINK = 0xffff }; diff --git a/include/linux/mroute_base.h b/include/linux/mroute_base.h index 5d75cc5b057e..4d55827e9705 100644 --- a/include/linux/mroute_base.h +++ b/include/linux/mroute_base.h @@ -256,7 +256,7 @@ struct mr_table { struct rhltable mfc_hash; struct list_head mfc_cache_list; int maxvif; - atomic_t cache_resolve_queue_len; + u32 cache_resolve_queue_len; bool mroute_do_assert; bool mroute_do_pim; bool mroute_do_wrvifwhole; diff --git a/include/linux/net/intel/libie/adminq.h b/include/linux/net/intel/libie/adminq.h index ab13bd777a28..839114d8975a 100644 --- a/include/linux/net/intel/libie/adminq.h +++ b/include/linux/net/intel/libie/adminq.h @@ -196,6 +196,7 @@ LIBIE_CHECK_STRUCT_LEN(16, libie_aqc_list_caps); #define LIBIE_AQC_BIT_ROCEV2_LAG BIT(0) #define LIBIE_AQC_BIT_SRIOV_LAG BIT(1) #define LIBIE_AQC_BIT_SRIOV_AA_LAG BIT(2) +#define LIBIE_AQC_CAPS_EEE 0x009B #define LIBIE_AQC_CAPS_FLEX10 0x00F1 #define LIBIE_AQC_CAPS_CEM 0x00F2 diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 93e4da7046a1..8eb6b8033606 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -79,7 +79,7 @@ enum { NETIF_F_HW_TLS_RX_BIT, /* Hardware TLS RX offload */ NETIF_F_GRO_HW_BIT, /* Hardware Generic receive offload */ - NETIF_F_HW_TLS_RECORD_BIT, /* Offload TLS record */ + __UNUSED_NETIF_F_56, NETIF_F_GRO_FRAGLIST_BIT, /* Fraglist GRO */ NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */ @@ -153,7 +153,6 @@ enum { #define NETIF_F_HW_ESP __NETIF_F(HW_ESP) #define NETIF_F_HW_ESP_TX_CSUM __NETIF_F(HW_ESP_TX_CSUM) #define NETIF_F_RX_UDP_TUNNEL_PORT __NETIF_F(RX_UDP_TUNNEL_PORT) -#define NETIF_F_HW_TLS_RECORD __NETIF_F(HW_TLS_RECORD) #define NETIF_F_GSO_UDP_L4 __NETIF_F(GSO_UDP_L4) #define NETIF_F_HW_TLS_TX __NETIF_F(HW_TLS_TX) #define NETIF_F_HW_TLS_RX __NETIF_F(HW_TLS_RX) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0e1e581efc5a..b67a12541eac 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1122,13 +1122,14 @@ struct netdev_net_notifier { * Cannot sleep, called with netif_addr_lock_bh held. * Deprecated in favor of ndo_set_rx_mode_async. * - * void (*ndo_set_rx_mode_async)(struct net_device *dev, - * struct netdev_hw_addr_list *uc, - * struct netdev_hw_addr_list *mc); + * int (*ndo_set_rx_mode_async)(struct net_device *dev, + * struct netdev_hw_addr_list *uc, + * struct netdev_hw_addr_list *mc); * Async version of ndo_set_rx_mode which runs in process context * with rtnl_lock and netdev_lock_ops(dev) held. The uc/mc parameters * are snapshots of the address lists - iterate with - * netdev_hw_addr_list_for_each(ha, uc). + * netdev_hw_addr_list_for_each(ha, uc). Return 0 on success or a + * negative errno to request a retry via the core backoff. * * int (*ndo_set_mac_address)(struct net_device *dev, void *addr); * This function is called when the Media Access Control address @@ -1149,8 +1150,8 @@ struct netdev_net_notifier { * SIOCBONDSLAVEINFOQUERY, and SIOCBONDINFOQUERY * * * int (*ndo_eth_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd); - * Called for ethernet specific ioctls: SIOCGMIIPHY, SIOCGMIIREG, - * SIOCSMIIREG, SIOCSHWTSTAMP and SIOCGHWTSTAMP. + * Called for ethernet specific ioctls: SIOCGMIIPHY, SIOCGMIIREG and + * SIOCSMIIREG. * * int (*ndo_set_config)(struct net_device *dev, struct ifmap *map); * Used to set network devices bus interface parameters. This interface @@ -1223,6 +1224,12 @@ struct netdev_net_notifier { * tx queues stopped. This allows the netdevice to perform queue * management safely. * + * NB: Returning -EOPNOTSUPP for whatever commands means "this qdisc + * is not offloaded (anymore, offloading may have silently stopped)", + * and the offloading flag is cleared. Notably, this is also true for + * dump queries (e.g. TC_*_STATS commands). If the underlying device does + * not report any statistics but is still offloading, return 0 instead. + * * Fiber Channel over Ethernet (FCoE) offload functions. * int (*ndo_fcoe_enable)(struct net_device *dev); * Called when the FCoE protocol stack wants to start using LLD for FCoE @@ -1449,7 +1456,7 @@ struct net_device_ops { void (*ndo_change_rx_flags)(struct net_device *dev, int flags); void (*ndo_set_rx_mode)(struct net_device *dev); - void (*ndo_set_rx_mode_async)( + int (*ndo_set_rx_mode_async)( struct net_device *dev, struct netdev_hw_addr_list *uc, struct netdev_hw_addr_list *mc); @@ -1788,6 +1795,12 @@ enum netdev_stat_type { NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */ }; +enum netmem_tx_mode { + NETMEM_TX_NONE, /* no netmem TX support */ + NETMEM_TX_DMA, /* DMA-capable netmem TX (real HW) */ + NETMEM_TX_NO_DMA, /* no DMA, e.g. passthrough for virtual devs */ +}; + enum netdev_reg_state { NETREG_UNINITIALIZED = 0, NETREG_REGISTERED, /* completed register_netdevice */ @@ -1809,7 +1822,7 @@ enum netdev_reg_state { * @lltx: device supports lockless Tx. Deprecated for real HW * drivers. Mainly used by logical interfaces, such as * bonding and tunnels - * @netmem_tx: device support netmem_tx. + * @netmem_tx: device netmem TX mode * * @name: This is the first field of the "visible" part of this structure * (i.e. as seen by users in the "Space.c" file). It is the name @@ -1920,6 +1933,8 @@ enum netdev_reg_state { * @rx_mode_node: List entry for rx_mode work processing * @rx_mode_tracker: Refcount tracker for rx_mode work * @rx_mode_addr_cache: Recycled snapshot entries for rx_mode work + * @rx_mode_retry_timer: Timer that re-queues rx_mode work after failure + * @rx_mode_retry_count: Number of consecutive retries already scheduled * @uc: unicast mac addresses * @mc: multicast mac addresses * @dev_addrs: list of device hw addresses @@ -1932,10 +1947,8 @@ enum netdev_reg_state { * @vlan_info: VLAN info * @dsa_ptr: dsa specific data * @tipc_ptr: TIPC specific data - * @atalk_ptr: AppleTalk link * @ip_ptr: IPv4 specific data * @ip6_ptr: IPv6 specific data - * @ax25_ptr: AX.25 specific data * @ieee80211_ptr: IEEE 802.11 specific data, assign before registering * @ieee802154_ptr: IEEE 802.15.4 low-rate Wireless Personal Area Network * device struct @@ -1980,6 +1993,8 @@ enum netdev_reg_state { * @qdisc_hash: qdisc hash table * @watchdog_timeo: Represents the timeout that is used by * the watchdog (see dev_watchdog()) + * @watchdog_lock: protect watchdog_ref_held + * @watchdog_ref_held: True if the watchdog device ref is taken. * @watchdog_timer: List of timers * * @proto_down_reason: reason a netdev interface is held down @@ -2132,7 +2147,7 @@ struct net_device { struct_group(priv_flags_fast, unsigned long priv_flags:32; unsigned long lltx:1; - unsigned long netmem_tx:1; + unsigned long netmem_tx:2; ); const struct net_device_ops *netdev_ops; const struct header_ops *header_ops; @@ -2314,6 +2329,8 @@ struct net_device { struct list_head rx_mode_node; netdevice_tracker rx_mode_tracker; struct netdev_hw_addr_list rx_mode_addr_cache; + struct timer_list rx_mode_retry_timer; + unsigned int rx_mode_retry_count; #ifdef CONFIG_LOCKDEP unsigned char nested_level; #endif @@ -2333,9 +2350,6 @@ struct net_device { #if IS_ENABLED(CONFIG_TIPC) struct tipc_bearer __rcu *tipc_ptr; #endif -#if IS_ENABLED(CONFIG_ATALK) - void *atalk_ptr; -#endif #if IS_ENABLED(CONFIG_CFG80211) struct wireless_dev *ieee80211_ptr; #endif @@ -2392,6 +2406,8 @@ struct net_device { /* These may be needed for future network-power-down code. */ struct timer_list watchdog_timer; int watchdog_timeo; + spinlock_t watchdog_lock; + bool watchdog_ref_held; u32 proto_down_reason; @@ -2572,6 +2588,9 @@ struct net_device { * Double protects: * @up, @moving_ns, @nd_net, @xdp_features * + * Ops protects: + * @cfg, @cfg_pending, @ethtool, @hwprov + * * Double ops protects: * @real_num_rx_queues, @real_num_tx_queues * @@ -3378,7 +3397,6 @@ static inline struct net_device *first_net_device(struct net *net) net_device_entry(net->dev_base_head.next); } -int netdev_boot_setup_check(struct net_device *dev); struct net_device *dev_getbyhwaddr(struct net *net, unsigned short type, const char *hwaddr); struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type, @@ -5138,6 +5156,7 @@ static inline void __dev_mc_unsync(struct net_device *dev, /* Functions used for secondary unicast and multicast support */ void dev_set_rx_mode(struct net_device *dev); +void netif_rx_mode_schedule_retry(struct net_device *dev); int netif_set_promiscuity(struct net_device *dev, int inc); int dev_set_promiscuity(struct net_device *dev, int inc); int netif_set_allmulti(struct net_device *dev, int inc, bool notify); diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 5a1c5c336fa4..20d70dddbe50 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -534,4 +534,21 @@ int xt_compat_check_entry_offsets(const void *base, const char *elems, unsigned int next_offset); #endif /* CONFIG_NETFILTER_XTABLES_COMPAT */ + +static inline bool xt_compat_check(void) +{ +#ifdef CONFIG_NETFILTER_XTABLES_COMPAT + if (!in_compat_syscall()) + return true; + + pr_warn_once("%s %s\n", + "xtables 32bit compat interface no longer supported", + "in namespaces and will be removed soon."); + + if (!capable(CAP_NET_ADMIN)) + return false; +#endif + return true; +} + #endif /* _X_TABLES_H */ diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index f22eec466040..88f7daa8560e 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -13,12 +13,28 @@ #include #include #include +#include +#include union inet_addr { __be32 ip; struct in6_addr in6; }; +/* + * Maximum payload netpoll's preallocated skb pool can carry. Keep this in + * sync with the buffer size used by refill_skbs() in net/core/netpoll.c; + * callers (e.g. netconsole) use it to detect requests the pool can never + * satisfy and avoid dequeuing a pooled skb that would later trip + * skb_over_panic() in skb_put(). + */ +#define MAX_UDP_CHUNK 1460 +#define MAX_SKB_SIZE \ + (sizeof(struct ethhdr) + \ + sizeof(struct iphdr) + \ + sizeof(struct udphdr) + \ + MAX_UDP_CHUNK) + struct netpoll { struct net_device *dev; netdevice_tracker dev_tracker; @@ -67,13 +83,13 @@ static inline void netpoll_poll_disable(struct net_device *dev) { return; } static inline void netpoll_poll_enable(struct net_device *dev) { return; } #endif -int netpoll_send_udp(struct netpoll *np, const char *msg, int len); int __netpoll_setup(struct netpoll *np, struct net_device *ndev); int netpoll_setup(struct netpoll *np); void __netpoll_free(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); void do_netpoll_cleanup(struct netpoll *np); netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); +void netpoll_zap_completion_queue(void); #ifdef CONFIG_NETPOLL static inline void *netpoll_poll_lock(struct napi_struct *napi) diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_topology.h index 68a59e25821c..95575f68d5bc 100644 --- a/include/linux/phy_link_topology.h +++ b/include/linux/phy_link_topology.h @@ -36,6 +36,11 @@ struct phy_device_node { struct phy_device *phy; }; +static inline bool phy_link_topo_empty(struct net_device *dev) +{ + return !dev->link_topo; +} + #if IS_ENABLED(CONFIG_PHYLIB) int phy_link_topo_add_phy(struct net_device *dev, struct phy_device *phy, diff --git a/include/linux/platform_data/wiznet.h b/include/linux/platform_data/wiznet.h deleted file mode 100644 index 1154c4db8a13..000000000000 --- a/include/linux/platform_data/wiznet.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Ethernet driver for the WIZnet W5x00 chip. - */ - -#ifndef PLATFORM_DATA_WIZNET_H -#define PLATFORM_DATA_WIZNET_H - -#include - -struct wiznet_platform_data { - int link_gpio; - u8 mac_addr[ETH_ALEN]; -}; - -#ifndef CONFIG_WIZNET_BUS_SHIFT -#define CONFIG_WIZNET_BUS_SHIFT 0 -#endif - -#define W5100_BUS_DIRECT_SIZE (0x8000 << CONFIG_WIZNET_BUS_SHIFT) -#define W5300_BUS_DIRECT_SIZE (0x0400 << CONFIG_WIZNET_BUS_SHIFT) - -#endif /* PLATFORM_DATA_WIZNET_H */ diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h index d2c3629bbe45..c95e891903f0 100644 --- a/include/linux/ptr_ring.h +++ b/include/linux/ptr_ring.h @@ -96,6 +96,20 @@ static inline bool ptr_ring_full_bh(struct ptr_ring *r) return ret; } +/* Note: callers invoking this in a loop must use a compiler barrier, + * for example cpu_relax(). Callers must hold producer_lock. + */ +static inline int __ptr_ring_check_produce(struct ptr_ring *r) +{ + if (unlikely(!r->size)) + return -EINVAL; + + if (data_race(r->queue[r->producer])) + return -ENOSPC; + + return 0; +} + /* Note: callers invoking this in a loop must use a compiler barrier, * for example cpu_relax(). Callers must hold producer_lock. * Callers are responsible for making sure pointer that is being queued @@ -103,8 +117,10 @@ static inline bool ptr_ring_full_bh(struct ptr_ring *r) */ static inline int __ptr_ring_produce(struct ptr_ring *r, void *ptr) { - if (unlikely(!r->size) || data_race(r->queue[r->producer])) - return -ENOSPC; + int p = __ptr_ring_check_produce(r); + + if (p) + return p; /* Make sure the pointer we are storing points to a valid data. */ /* Pairs with the dependency ordering in __ptr_ring_consume. */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 3f06254ab1b7..115db8c44db2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1315,7 +1315,8 @@ static inline bool skb_data_unref(const struct sk_buff *skb, return true; } -void __fix_address sk_skb_reason_drop(struct sock *sk, struct sk_buff *skb, +void __fix_address sk_skb_reason_drop(const struct sock *sk, + struct sk_buff *skb, enum skb_drop_reason reason); static inline void diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 19f4f253b4f9..a8553401b1c9 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -4,6 +4,7 @@ #ifndef _LINUX_SKMSG_H #define _LINUX_SKMSG_H +#include #include #include #include @@ -199,11 +200,14 @@ static inline void sk_msg_xfer(struct sk_msg *dst, struct sk_msg *src, int which, u32 size) { dst->sg.data[which] = src->sg.data[which]; + __assign_bit(which, dst->sg.copy, test_bit(which, src->sg.copy)); dst->sg.data[which].length = size; dst->sg.size += size; src->sg.size -= size; src->sg.data[which].length -= size; src->sg.data[which].offset += size; + if (!src->sg.data[which].length) + __clear_bit(which, src->sg.copy); } static inline void sk_msg_xfer_full(struct sk_msg *dst, struct sk_msg *src) @@ -273,16 +277,19 @@ static inline void sk_msg_page_add(struct sk_msg *msg, struct page *page, 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); - else - __clear_bit(i, msg->sg.copy); + __assign_bit(i, msg->sg.copy, copy_state); sk_msg_iter_var_next(i); if (i == msg->sg.end) break; } while (1); } +static inline void sk_msg_sg_copy_assign(struct sk_msg *dst, u32 dst_i, + const struct sk_msg *src, u32 src_i) +{ + __assign_bit(dst_i, dst->sg.copy, test_bit(src_i, src->sg.copy)); +} + static inline void sk_msg_sg_copy_set(struct sk_msg *msg, u32 start) { sk_msg_sg_copy(msg, start, true); @@ -544,15 +551,6 @@ static inline void psock_progs_drop(struct sk_psock_progs *progs) psock_set_prog(&progs->skb_verdict, NULL); } -int sk_psock_tls_strp_read(struct sk_psock *psock, struct sk_buff *skb); - -static inline bool sk_psock_strp_enabled(struct sk_psock *psock) -{ - if (!psock) - return false; - return !!psock->saved_data_ready; -} - /* for tcp only, sk is locked */ static inline ssize_t sk_psock_msg_inq(struct sock *sk) { diff --git a/include/linux/socket.h b/include/linux/socket.h index 3ffdfe184b23..2a8d7b14f1d1 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -356,7 +356,7 @@ struct ucred { /* Flags to be cleared on entry by sendmsg and sendmmsg syscalls */ #define MSG_INTERNAL_SENDMSG_FLAGS \ - (MSG_SPLICE_PAGES | MSG_SENDPAGE_NOPOLICY | MSG_SENDPAGE_DECRYPTED) + (MSG_SPLICE_PAGES | MSG_SENDPAGE_NOPOLICY | MSG_SENDPAGE_DECRYPTED | MSG_NO_SHARED_FRAGS) /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */ #define SOL_IP 0 diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 6982f10e826b..8a6807082672 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -253,15 +253,20 @@ struct tcp_sock { /* TX read-write hotpath cache lines */ __cacheline_group_begin(tcp_sock_write_tx) ____cacheline_aligned; - u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut - * The total number of segments sent. - */ - u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut - * total number of data segments sent. + u32 delivered; /* Total data packets delivered incl. rexmits */ + u32 delivered_ce; /* Like the above but only ECE marked packets */ + u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked + * sum(delta(snd_una)), or how many bytes + * were acked. */ u64 bytes_sent; /* RFC4898 tcpEStatsPerfHCDataOctetsOut * total number of data bytes sent. */ + u64 first_tx_mstamp; /* start of window send phase */ + u64 delivered_mstamp; /* time we reached "delivered" */ + u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut + * total number of data segments sent. + */ u32 snd_sml; /* Last byte of the most recently transmitted small packet */ u8 chrono_type; /* current chronograph type */ u32 chrono_start; /* Start time in jiffies of a TCP chrono */ @@ -271,6 +276,10 @@ struct tcp_sock { u32 lsndtime; /* timestamp of last sent data packet (for restart window) */ u32 mdev_us; /* medium deviation */ u32 rtt_seq; /* sequence number to update rttvar */ + u32 max_packets_out; /* max packets_out in last window */ + u32 cwnd_usage_seq; /* right edge of cwnd usage tracking flight */ + u32 rate_delivered; /* saved rate sample: packets delivered */ + u32 rate_interval_us; /* saved rate sample: time elapsed */ u64 tcp_wstamp_ns; /* departure time for next sent data packet */ u64 accecn_opt_tstamp; /* Last AccECN option sent timestamp */ struct list_head tsorted_sent_queue; /* time-sorted sent but un-SACKed skbs */ @@ -307,8 +316,6 @@ struct tcp_sock { u32 srtt_us; /* smoothed round trip time << 3 in usecs */ u32 packets_out; /* Packets which are "in flight" */ u32 snd_up; /* Urgent pointer */ - u32 delivered; /* Total data packets delivered incl. rexmits */ - u32 delivered_ce; /* Like the above but only ECE marked packets */ u32 received_ce; /* Like the above but for rcvd CE marked pkts */ u32 received_ecn_bytes[3]; /* received byte counters for three ECN * types: INET_ECN_ECT_1, INET_ECN_ECT_0, @@ -324,6 +331,12 @@ struct tcp_sock { * Options received (usually on last packet, some only on SYN packets). */ struct tcp_options_received rx_opt; + u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn + * total number of segments in. + */ + u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut + * The total number of segments sent. + */ __cacheline_group_end(tcp_sock_write_txrx); /* RX read-write hotpath cache lines */ @@ -333,26 +346,13 @@ struct tcp_sock { * sum(delta(rcv_nxt)), or how many bytes * were acked. */ - u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn - * total number of segments in. - */ u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn * total number of data segments in. */ u32 rcv_wup; /* rcv_nxt on last window update sent */ - u32 max_packets_out; /* max packets_out in last window */ - u32 cwnd_usage_seq; /* right edge of cwnd usage tracking flight */ - u32 rate_delivered; /* saved rate sample: packets delivered */ - u32 rate_interval_us; /* saved rate sample: time elapsed */ u32 rcv_rtt_last_tsecr; u32 delivered_ecn_bytes[3]; u16 pkts_acked_ewma;/* Pkts acked EWMA for AccECN cep heuristic */ - u64 first_tx_mstamp; /* start of window send phase */ - u64 delivered_mstamp; /* time we reached "delivered" */ - u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked - * sum(delta(snd_una)), or how many bytes - * were acked. - */ struct { u32 rtt_us; u32 seq; diff --git a/include/net/Space.h b/include/net/Space.h deleted file mode 100644 index 6a0b6674d930..000000000000 --- a/include/net/Space.h +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* A unified ethernet device probe. This is the easiest way to have every - * ethernet adaptor have the name "eth[0123...]". - */ - -struct net_device *ne_probe(int unit); -struct net_device *cs89x0_probe(int unit); diff --git a/include/net/act_api.h b/include/net/act_api.h index fd2967ee08f7..20d9e55f8564 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -242,7 +242,7 @@ static inline void tcf_action_update_bstats(struct tc_action *a, static inline void tcf_action_inc_drop_qstats(struct tc_action *a) { if (likely(a->cpu_qstats)) { - qstats_drop_inc(this_cpu_ptr(a->cpu_qstats)); + qstats_cpu_drop_inc(a->cpu_qstats); return; } atomic_inc(&a->tcfa_drops); @@ -251,7 +251,7 @@ static inline void tcf_action_inc_drop_qstats(struct tc_action *a) static inline void tcf_action_inc_overlimit_qstats(struct tc_action *a) { if (likely(a->cpu_qstats)) { - qstats_overlimit_inc(this_cpu_ptr(a->cpu_qstats)); + qstats_cpu_overlimit_inc(a->cpu_qstats); return; } atomic_inc(&a->tcfa_overlimits); diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 9e96776945e5..539bbbe54b14 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -369,8 +369,11 @@ static inline struct inet6_dev *__in6_dev_get_rtnl_net(const struct net_device * static inline struct inet6_dev *__in6_dev_stats_get(const struct net_device *dev, const struct sk_buff *skb) { - if (netif_is_l3_master(dev)) + if (netif_is_l3_master(dev)) { dev = dev_get_by_index_rcu(dev_net(dev), inet6_iif(skb)); + if (!dev) + return NULL; + } return __in6_dev_get(dev); } diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index 4e40063adab4..30046a3c20f7 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -220,6 +220,7 @@ static inline bool __vsock_in_connected_table(struct vsock_sock *vsk) void vsock_add_pending(struct sock *listener, struct sock *pending); void vsock_remove_pending(struct sock *listener, struct sock *pending); void vsock_enqueue_accept(struct sock *listener, struct sock *connected); +void vsock_pending_to_accept(struct sock *listener, struct sock *pending); void vsock_insert_connected(struct vsock_sock *vsk); void vsock_remove_bound(struct vsock_sock *vsk); void vsock_remove_connected(struct vsock_sock *vsk); diff --git a/include/net/amt.h b/include/net/amt.h index c881bc8b673b..a0255491f5b0 100644 --- a/include/net/amt.h +++ b/include/net/amt.h @@ -331,7 +331,7 @@ struct amt_dev { enum amt_status status; /* Generated key */ siphash_key_t key; - struct socket __rcu *sock; + struct sock __rcu *sk; u32 max_groups; u32 max_sources; u32 hash_buckets; diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 3faea66b1979..b624da5026f5 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -5,10 +6,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 572b1c620c5d..38186a245f14 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -5,10 +6,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -2486,6 +2483,12 @@ struct hci_rp_le_cs_test { #define HCI_OP_LE_CS_TEST_END 0x2096 +#define HCI_OP_LE_SET_HOST_FEATURE_V2 0x2097 +struct hci_cp_le_set_host_feature_v2 { + __le16 bit_number; + __u8 bit_value; +} __packed; + /* ---- HCI Events ---- */ struct hci_ev_status { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index aa600fbf9a53..7e15da47fe3a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. @@ -5,10 +6,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h index bbd752494ef9..4b2a0af4ed58 100644 --- a/include/net/bluetooth/hci_mon.h +++ b/include/net/bluetooth/hci_mon.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2011-2012 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h index 13e8cd4414a1..16f150b861be 100644 --- a/include/net/bluetooth/hci_sock.h +++ b/include/net/bluetooth/hci_sock.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e0a1f2293679..1640cc9bf83a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -6,10 +7,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -748,6 +745,7 @@ enum { FLAG_ECRED_CONN_REQ_SENT, FLAG_PENDING_SECURITY, FLAG_HOLD_HCI_CONN, + FLAG_DEL, }; /* Lock nesting levels for L2CAP channels. We need these because lockdep diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 8234915854b6..08daed7a96d5 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2010 Nokia Corporation Copyright (C) 2011-2012 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index c05882476900..feb6b3ae5e69 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* RFCOMM implementation for Linux Bluetooth stack (BlueZ) Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index f40ddb4264fc..214409527b35 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/include/net/bond_options.h b/include/net/bond_options.h index e6eedf23aea1..52b966e92793 100644 --- a/include/net/bond_options.h +++ b/include/net/bond_options.h @@ -79,6 +79,7 @@ enum { BOND_OPT_COUPLED_CONTROL, BOND_OPT_BROADCAST_NEIGH, BOND_OPT_ACTOR_PORT_PRIO, + BOND_OPT_LACP_STRICT, BOND_OPT_LAST }; diff --git a/include/net/bonding.h b/include/net/bonding.h index edd1942dcd73..2c54a36a8477 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -129,6 +129,7 @@ struct bond_params { int peer_notif_delay; int lacp_active; int lacp_fast; + int lacp_strict; unsigned int min_links; int ad_select; char primary[IFNAMSIZ]; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9d3639ff9c28..8188ad200de5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -830,6 +830,8 @@ struct vif_params { * @seq_len: length of @seq. * @vlan_id: vlan_id for VLAN group key (if nonzero) * @mode: key install mode (RX_TX, NO_TX or SET_TX) + * @ltf_keyseed: LTF key seed material + * @ltf_keyseed_len: length of LTF key seed material */ struct key_params { const u8 *key; @@ -839,11 +841,18 @@ struct key_params { u16 vlan_id; u32 cipher; enum nl80211_key_mode mode; + const u8 *ltf_keyseed; + size_t ltf_keyseed_len; }; /** * struct cfg80211_chan_def - channel definition * @chan: the (control) channel + * @npca_chan: the NPCA primary channel + * Note that if DBE is in use, this channel may appear to be + * inside the primary half of the chandef. Implementations + * can use the position of this channel to understand how + * NPCA is used. * @width: channel width * @center_freq1: center frequency of first segment * @center_freq2: center frequency of second segment @@ -856,18 +865,24 @@ struct key_params { * @punctured: mask of the punctured 20 MHz subchannels, with * bits turned on being disabled (punctured); numbered * from lower to higher frequency (like in the spec) + * @npca_punctured: NPCA puncturing bitmap, like @punctured but for + * NPCA transmissions. If NPCA is used (@npca_chan is not %NULL) + * this will be a superset of the @punctured bimap. + * Note that if DBE is used, this bitmap is also shifted to be in + * accordance with the overall chandef bandwidth. * @s1g_primary_2mhz: Indicates if the control channel pointed to * by 'chan' exists as a 1MHz primary subchannel within an * S1G 2MHz primary channel. */ struct cfg80211_chan_def { struct ieee80211_channel *chan; + struct ieee80211_channel *npca_chan; enum nl80211_chan_width width; u32 center_freq1; u32 center_freq2; struct ieee80211_edmg edmg; u16 freq1_offset; - u16 punctured; + u16 punctured, npca_punctured; bool s1g_primary_2mhz; }; @@ -1014,7 +1029,9 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1, chandef1->freq1_offset == chandef2->freq1_offset && chandef1->center_freq2 == chandef2->center_freq2 && chandef1->punctured == chandef2->punctured && - chandef1->s1g_primary_2mhz == chandef2->s1g_primary_2mhz); + chandef1->s1g_primary_2mhz == chandef2->s1g_primary_2mhz && + chandef1->npca_chan == chandef2->npca_chan && + chandef1->npca_punctured == chandef2->npca_punctured); } /** @@ -1143,6 +1160,46 @@ int cfg80211_chandef_primary(const struct cfg80211_chan_def *chandef, enum nl80211_chan_width primary_chan_width, u16 *punctured); +/** + * cfg80211_chandef_npca_valid - check that NPCA information is valid + * @wiphy: the wiphy to check for, for channel pointer lookup + * @chandef: the BSS channel chandef to check against + * @npca: NPCA information, can be %NULL in which case this + * always returns %true + * + * Note that DBE must not have been configured into the chandef yet + * before checking NPCA, i.e. @chandef must represent the BSS channel. + * + * Returns: %true if the NPCA channel and puncturing bitmap are valid + * according to the chandef, %false otherwise + */ +bool cfg80211_chandef_npca_valid(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + const struct ieee80211_uhr_npca_info *npca); + +/** + * cfg80211_chandef_add_npca - parse and add NPCA information to chandef + * @wiphy: the wiphy this will be used for, for channel pointer lookup + * @chandef: the chandef to modify, must be a valid chandef without NPCA + * @npca: the NPCA information, can be %NULL + * + * Returns: 0 if the NPCA information was added and the resulting + * chandef is valid, a negative error code on errors + */ +int cfg80211_chandef_add_npca(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + const struct ieee80211_uhr_npca_info *npca); + +/** + * cfg80211_chandef_add_dbe - parse and add DBE information to chandef + * @chandef: the chandef to expand + * @dbe: the DBE information, must be size-checked if not %NULL + * + * Returns: 0 for success, a negative error code otherwise + */ +int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef, + const struct ieee80211_uhr_dbe_info *dbe); + /** * nl80211_send_chandef - sends the channel definition. * @msg: the msg to send channel definition @@ -1396,6 +1453,13 @@ struct cfg80211_rnr_elems { * @he_bss_color: BSS Color settings * @he_bss_color_valid: indicates whether bss color * attribute is present in beacon data or not. + * @ht_required: stations must support HT + * @vht_required: stations must support VHT + * @ht_oper: HT operation element (or %NULL if HT isn't enabled) + * @vht_oper: VHT operation element (or %NULL if VHT isn't enabled) + * @he_oper: HE operation IE (or %NULL if HE isn't enabled) + * @eht_oper: EHT operation IE (or %NULL if EHT isn't enabled) + * @uhr_oper: UHR operation (or %NULL if UHR isn't enabled) */ struct cfg80211_beacon_data { unsigned int link_id; @@ -1420,6 +1484,13 @@ struct cfg80211_beacon_data { size_t civicloc_len; struct cfg80211_he_bss_color he_bss_color; bool he_bss_color_valid; + + bool ht_required, vht_required; + 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_uhr_operation *uhr_oper; }; struct mac_address { @@ -1524,16 +1595,9 @@ struct cfg80211_s1g_short_beacon { * @vht_cap: VHT capabilities (or %NULL if VHT isn't enabled) * @he_cap: HE capabilities (or %NULL if HE isn't enabled) * @eht_cap: EHT capabilities (or %NULL if EHT isn't enabled) - * @eht_oper: EHT operation IE (or %NULL if EHT isn't enabled) - * @uhr_oper: UHR operation (or %NULL if UHR isn't enabled) - * @ht_required: stations must support HT - * @vht_required: stations must support VHT * @twt_responder: Enable Target Wait Time - * @he_required: stations must support HE - * @sae_h2e_required: stations must support direct H2E technique in SAE * @flags: flags, as defined in &enum nl80211_ap_settings_flags * @he_obss_pd: OBSS Packet Detection settings - * @he_oper: HE operation IE (or %NULL if HE isn't enabled) * @fils_discovery: FILS discovery transmission parameters * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters * @mbssid_config: AP settings for multiple bssid @@ -1562,11 +1626,7 @@ struct cfg80211_ap_settings { const struct ieee80211_ht_cap *ht_cap; const struct ieee80211_vht_cap *vht_cap; const struct ieee80211_he_cap_elem *he_cap; - const struct ieee80211_he_operation *he_oper; const struct ieee80211_eht_cap_elem *eht_cap; - const struct ieee80211_eht_operation *eht_oper; - const struct ieee80211_uhr_operation *uhr_oper; - bool ht_required, vht_required, he_required, sae_h2e_required; bool twt_responder; u32 flags; struct ieee80211_he_obss_pd he_obss_pd; @@ -4433,6 +4493,25 @@ struct cfg80211_ftm_responder_stats { * (must have either this or @rtt_avg) * @dist_variance: variance of distances measured (see also @rtt_variance) * @dist_spread: spread of distances measured (see also @rtt_spread) + * @tx_ltf_repetition_count: negotiated value of number of tx ltf repetitions + * in NDP frames + * @rx_ltf_repetition_count: negotiated value of number of rx ltf repetitions + * in NDP frames + * @max_time_between_measurements: the negotiated maximum interval (in units of + * 10 ms) by which the ISTA must complete the next measurement cycle. + * @min_time_between_measurements: the negotiated minimum interval (in units of + * 100 us) between two consecutive range measurements initiated by the + * ISTA. + * @num_tx_spatial_streams: number of Tx space-time streams used in the NDP + * frame during the measurement sounding phase. + * @num_rx_spatial_streams: number of Rx space-time streams used in the NDP + * frame during the measurement sounding phase. + * @nominal_time: negotiated nominal duration between adjacent availability + * windows in units of milliseconds (u32). + * @availability_window: negotiated availability window time used in this + * session in units of milliseconds (u8). + * @chan_width: band width used for measurement. + * @preamble: preamble used for measurement. * @num_ftmr_attempts_valid: @num_ftmr_attempts is valid * @num_ftmr_successes_valid: @num_ftmr_successes is valid * @rssi_avg_valid: @rssi_avg is valid @@ -4445,6 +4524,18 @@ struct cfg80211_ftm_responder_stats { * @dist_avg_valid: @dist_avg is valid * @dist_variance_valid: @dist_variance is valid * @dist_spread_valid: @dist_spread is valid + * @tx_ltf_repetition_count_valid: @tx_ltf_repetition_count is valid + * @rx_ltf_repetition_count_valid: @rx_ltf_repetition_count is valid + * @max_time_between_measurements_valid: @max_time_between_measurements is valid + * @min_time_between_measurements_valid: @min_time_between_measurements is valid + * @num_tx_spatial_streams_valid: @num_tx_spatial_streams is valid + * @num_rx_spatial_streams_valid: @num_rx_spatial_streams is valid + * @nominal_time_valid: @nominal_time is valid + * @availability_window_valid: @availability_window is valid + * @chan_width_valid: @chan_width is valid. + * @preamble_valid: @preamble is valid. + * @is_delayed_lmr: indicates if the reported LMR is of the current burst or the + * previous burst, flag. */ struct cfg80211_pmsr_ftm_result { const u8 *lci; @@ -4468,8 +4559,18 @@ struct cfg80211_pmsr_ftm_result { s64 dist_avg; s64 dist_variance; s64 dist_spread; + u32 tx_ltf_repetition_count; + u32 rx_ltf_repetition_count; + u32 max_time_between_measurements; + u32 min_time_between_measurements; + u8 num_tx_spatial_streams; + u8 num_rx_spatial_streams; + u32 nominal_time; + u8 availability_window; + enum nl80211_chan_width chan_width; + enum nl80211_preamble preamble; - u16 num_ftmr_attempts_valid:1, + u32 num_ftmr_attempts_valid:1, num_ftmr_successes_valid:1, rssi_avg_valid:1, rssi_spread_valid:1, @@ -4480,7 +4581,18 @@ struct cfg80211_pmsr_ftm_result { rtt_spread_valid:1, dist_avg_valid:1, dist_variance_valid:1, - dist_spread_valid:1; + dist_spread_valid:1, + tx_ltf_repetition_count_valid:1, + rx_ltf_repetition_count_valid:1, + max_time_between_measurements_valid:1, + min_time_between_measurements_valid:1, + num_tx_spatial_streams_valid:1, + num_rx_spatial_streams_valid:1, + nominal_time_valid:1, + availability_window_valid:1, + chan_width_valid:1, + preamble_valid:1, + is_delayed_lmr:1; }; /** @@ -4524,7 +4636,8 @@ struct cfg80211_pmsr_result { * @burst_duration: burst duration. If @trigger_based or @non_trigger_based is * set, this is the burst duration in milliseconds, and zero means the * device should pick an appropriate value based on @ftms_per_burst. - * @ftms_per_burst: number of FTMs per burst + * @ftms_per_burst: number of FTMs per burst. If set to 0, the firmware or + * driver can automatically select an appropriate value. * @ftmr_retries: number of retries for FTM request * @request_lci: request LCI information * @request_civicloc: request civic location information @@ -4541,6 +4654,48 @@ struct cfg80211_pmsr_result { * @bss_color: the bss color of the responder. Optional. Set to zero to * indicate the driver should set the BSS color. Only valid if * @non_trigger_based or @trigger_based is set. + * @request_type: ranging request type, one of + * &enum nl80211_peer_measurement_ftm_req_type. Defaults to + * %NL80211_PMSR_FTM_REQ_TYPE_INFRA if not specified. + * @min_time_between_measurements: minimum time between two consecutive range + * measurements in units of 100 microseconds, for non-trigger based + * ranging. Should be set as short as possible to minimize turnaround + * time, since two-way ranging with delayed LMR requires two measurements. + * Only valid if @non_trigger_based is set. + * @max_time_between_measurements: maximum time between two consecutive range + * measurements in units of 10 milliseconds, for non-trigger based + * ranging. Acts as a session timeout; if exceeded, the ranging session + * should be terminated. Only valid if @non_trigger_based is set. + * @availability_window: duration of the availability window (AW) in units of + * 1 millisecond (0-255 ms). Only valid if @non_trigger_based is set. + * If set to 0, the firmware or driver can automatically select an + * appropriate value. + * @nominal_time: Nominal duration between adjacent availability windows + * in units of milli seconds. Only valid if @non_trigger_based is set. + * If set to 0, the firmware or driver can automatically select an + * appropriate value. + * @num_measurements: number of Availability Windows (AWs) to schedule + * for non-trigger-based ranging. Each AW may contain multiple FTM + * exchanges as configured by @ftms_per_burst. Only valid if + * @non_trigger_based is set. If set to 0, the firmware or driver + * can automatically select an appropriate value. + * @ingress_distance: optional ingress threshold in units of mm. When set, + * the measurement result of the peer needs to be indicated if the device + * moves into this range. Measurement results need to be sent on a burst + * index basis in this case. + * @egress_distance: optional egress threshold in units of mm. When set, + * the measurement result of the peer needs to be indicated if the device + * moves out of this range. Measurement results need to be sent on a burst + * index basis in this case. + * If neither or only one of @ingress_distance and @egress_distance + * is set, only the specified threshold is used. If both are set, both + * thresholds are applied. If neither is set, results are reported without + * threshold filtering. + * @pd_suppress_range_results: flag to suppress ranging results for PD + * requests. When set, the device performs ranging measurements to + * provide ranging services to a peer (e.g. in RSTA role) but does + * not report the measurement results to userspace. Only valid when + * @request_type is %NL80211_PMSR_FTM_REQ_TYPE_PD. * * See also nl80211 for the respective attribute documentation. */ @@ -4560,6 +4715,16 @@ struct cfg80211_pmsr_ftm_request_peer { u8 ftms_per_burst; u8 ftmr_retries; u8 bss_color; + + u32 request_type; + u32 min_time_between_measurements; + u32 max_time_between_measurements; + u8 availability_window; + u32 nominal_time; + u32 num_measurements; + u64 ingress_distance; + u64 egress_distance; + u8 pd_suppress_range_results:1; }; /** @@ -5085,6 +5250,9 @@ struct mgmt_frame_regs { * links by calling cfg80211_mlo_reconf_add_done(). When calling * cfg80211_mlo_reconf_add_done() the bss pointer must be given for each * link for which MLO reconfiguration 'add' operation was requested. + * + * @start_pd: Start the PD interface. + * @stop_pd: Stop the PD interface. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -5267,7 +5435,7 @@ struct cfg80211_ops { struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, - u64 *cookie); + u64 *cookie, const u8 *rx_addr); int (*cancel_remain_on_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); @@ -5461,6 +5629,8 @@ struct cfg80211_ops { struct cfg80211_ml_reconf_req *req); int (*set_epcs)(struct wiphy *wiphy, struct net_device *dev, bool val); + int (*start_pd)(struct wiphy *wiphy, struct wireless_dev *wdev); + void (*stop_pd)(struct wiphy *wiphy, struct wireless_dev *wdev); }; /* @@ -5506,7 +5676,6 @@ struct cfg80211_ops { * responds to probe-requests in hardware. * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX. * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call. - * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels. * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in * beaconing mode (AP, IBSS, Mesh, ...). * @WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK: The device supports bigger kek and kck keys @@ -5546,7 +5715,6 @@ enum wiphy_flags { WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19), WIPHY_FLAG_OFFCHAN_TX = BIT(20), WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21), - WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22), WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23), WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER = BIT(24), WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON = BIT(25), @@ -5852,6 +6020,7 @@ struct wiphy_vendor_command { * @extended_capabilities_len: length of the extended capabilities * @eml_capabilities: EML capabilities (for MLO) * @mld_capa_and_ops: MLD capabilities and operations (for MLO) + * @ext_mld_capa_and_ops: Extended MLD capabilities and operations (for MLO) */ struct wiphy_iftype_ext_capab { enum nl80211_iftype iftype; @@ -5860,6 +6029,7 @@ struct wiphy_iftype_ext_capab { u8 extended_capabilities_len; u16 eml_capabilities; u16 mld_capa_and_ops; + u16 ext_mld_capa_and_ops; }; /** @@ -5903,7 +6073,53 @@ cfg80211_get_iftype_ext_capa(struct wiphy *wiphy, enum nl80211_iftype type); * (0 means unknown) * @ftm.max_total_ltf_rx: maximum total number of LTFs that can be received * (0 means unknown) - * @ftm.support_rsta: supports operating as RSTA in PMSR FTM request + * @ftm.ista: initiator role capabilities + * @ftm.ista.support_ntb: supports operating as ISTA in PMSR FTM request for + * NTB ranging. + * @ftm.ista.support_tb: supports operating as ISTA in PMSR FTM request for + * TB ranging. + * @ftm.ista.support_edca: supports operating as ISTA in PMSR FTM request for + * EDCA based ranging. + * @ftm.ista.max_peers: maximum number of peers supported in the ISTA role. + * If zero, no role-specific peer limit applies. + * @ftm.rsta: responder role capabilities + * @ftm.rsta.support_ntb: supports operating as RSTA in PMSR FTM request for + * NTB ranging. + * @ftm.rsta.support_tb: supports operating as RSTA in PMSR FTM request for + * TB ranging. + * @ftm.rsta.support_edca: supports operating as RSTA in PMSR FTM request for + * EDCA based ranging. + * @ftm.rsta.max_peers: maximum number of peers supported in the RSTA role. + * If zero, no role-specific peer limit applies. + * @ftm.max_no_of_tx_antennas: maximum number of transmit antennas supported for + * EDCA based ranging (0 means unknown) + * @ftm.max_no_of_rx_antennas: maximum number of receive antennas supported for + * EDCA based ranging (0 means unknown) + * @ftm.min_allowed_ranging_interval_edca: Minimum EDCA ranging + * interval supported by the device in milli seconds. (0 means unknown). + * Applications can use this value to estimate the burst period to be + * given in the FTM request for the EDCA based ranging case. If + * non-zero, this value will be used to validate the burst period in + * the FTM request. + * @ftm.min_allowed_ranging_interval_ntb: Minimum NTB ranging + * interval supported by the device in milli seconds. (0 means unknown). + * Applications can use this value to estimate the burst period to be + * given in the FTM request for the NTB ranging case. If non-zero, + * this value will be used to validate the nominal time in the FTM + * request. + * @ftm.type: ranging type capabilities + * @ftm.type.infra_support: supports infrastructure ranging (STA-to-AP or + * AP-to-STA) as part of Proximity Detection + * @ftm.type.pd_support: supports peer-to-peer ranging as mentioned in the + * specification "PR Implementation Consideration Draft 1.9 rev 1" where + * PD stands for proximity detection + * @ftm.concurrent_ista_rsta_support: indicates if the device can + * simultaneously act as initiator and responder in a multi-peer + * measurement request. Only valid if @ftm.rsta_support is set. + * @ftm.pd_preambles: bitmap of preambles supported (&enum nl80211_preamble) + * for PD ranging requests. Ignored if @ftm.type.pd_support is not set. + * @ftm.pd_bandwidths: bitmap of bandwidths supported (&enum nl80211_chan_width) + * for PD ranging requests. Ignored if @ftm.type.pd_support is not set. */ struct cfg80211_pmsr_capabilities { unsigned int max_peers; @@ -5929,7 +6145,29 @@ struct cfg80211_pmsr_capabilities { u8 max_rx_sts; u8 max_total_ltf_tx; u8 max_total_ltf_rx; - u8 support_rsta:1; + struct { + u8 support_ntb:1, + support_tb:1, + support_edca:1; + u32 max_peers; + } ista; + struct { + u8 support_ntb:1, + support_tb:1, + support_edca:1; + u32 max_peers; + } rsta; + u8 max_no_of_tx_antennas; + u8 max_no_of_rx_antennas; + u32 min_allowed_ranging_interval_edca; + u32 min_allowed_ranging_interval_ntb; + struct { + u8 infra_support:1, + pd_support:1; + } type; + u8 concurrent_ista_rsta_support:1; + u32 pd_preambles; + u32 pd_bandwidths; } ftm; }; @@ -6024,7 +6262,7 @@ enum wiphy_nan_flags { * nibble indicates the number of TX antennas and upper nibble indicates the * number of RX antennas. Value 0 indicates the information is not * available. - * @max_channel_switch_time: maximum channel switch time in milliseconds. + * @max_channel_switch_time: maximum channel switch time in microseconds. * @dev_capabilities: NAN device capabilities as defined in Wi-Fi Aware (TM) * specification Table 79 (Capabilities field). * @phy: Band-agnostic capabilities for NAN data interfaces. Since NAN @@ -8302,6 +8540,7 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr); * as the AC bitmap in the QoS info field * @req_ies: information elements from the (Re)Association Request frame * @req_ies_len: length of req_ies data + * @assoc_encrypted: indicate if the (re)association exchange is encrypted. * @ap_mld_addr: AP MLD address (in case of MLO) * @links: per-link information indexed by link ID, use links[0] for * non-MLO connections @@ -8316,6 +8555,7 @@ struct cfg80211_rx_assoc_resp_data { const u8 *req_ies; size_t req_ies_len; int uapsd_queues; + bool assoc_encrypted; const u8 *ap_mld_addr; struct { u8 addr[ETH_ALEN] __aligned(2); @@ -8445,7 +8685,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, * cfg80211 then sends a notification to userspace. */ void cfg80211_notify_new_peer_candidate(struct net_device *dev, - const u8 *macaddr, const u8 *ie, u8 ie_len, + const u8 *macaddr, const u8 *ie, size_t ie_len, int sig_dbm, gfp_t gfp); /** @@ -8835,6 +9075,9 @@ struct cfg80211_fils_resp_params { * @links.status: per-link status code, to report a status code that's not * %WLAN_STATUS_SUCCESS for a given link, it must also be in the * @valid_links bitmap and may have a BSS pointer (which is then released) + * @assoc_encrypted: The driver should set this flag to indicate that the + * (Re)Association Request/Response frames are transmitted encrypted over + * the air. */ struct cfg80211_connect_resp_params { int status; @@ -8844,6 +9087,7 @@ struct cfg80211_connect_resp_params { size_t resp_ie_len; struct cfg80211_fils_resp_params fils; enum nl80211_timeout_reason timeout_reason; + bool assoc_encrypted; const u8 *ap_mld_addr; u16 valid_links; diff --git a/include/net/devlink.h b/include/net/devlink.h index bcd31de1f890..dd546dbd57cf 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -433,6 +433,13 @@ enum devlink_param_type { DEVLINK_PARAM_TYPE_U64 = DEVLINK_VAR_ATTR_TYPE_U64, DEVLINK_PARAM_TYPE_STRING = DEVLINK_VAR_ATTR_TYPE_STRING, DEVLINK_PARAM_TYPE_BOOL = DEVLINK_VAR_ATTR_TYPE_FLAG, + DEVLINK_PARAM_TYPE_U64_ARRAY = DEVLINK_VAR_ATTR_TYPE_U64_ARRAY, +}; + +#define __DEVLINK_PARAM_MAX_ARRAY_SIZE 32 +struct devlink_param_u64_array { + u64 size; + u64 val[__DEVLINK_PARAM_MAX_ARRAY_SIZE]; }; union devlink_param_value { @@ -442,6 +449,7 @@ union devlink_param_value { u64 vu64; char vstr[__DEVLINK_PARAM_MAX_STRING_VALUE]; bool vbool; + struct devlink_param_u64_array u64arr; }; struct devlink_param_gset_ctx { @@ -501,7 +509,7 @@ struct devlink_param { struct devlink_param_gset_ctx *ctx, struct netlink_ext_ack *extack); int (*validate)(struct devlink *devlink, u32 id, - union devlink_param_value val, + union devlink_param_value *val, struct netlink_ext_ack *extack); int (*get_default)(struct devlink *devlink, u32 id, struct devlink_param_gset_ctx *ctx, @@ -1923,7 +1931,7 @@ void devlink_params_unregister(struct devlink *devlink, int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id, union devlink_param_value *val); void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id, - union devlink_param_value init_val); + union devlink_param_value *init_val); void devl_param_value_changed(struct devlink *devlink, u32 param_id); struct devlink_region *devl_region_create(struct devlink *devlink, const struct devlink_region_ops *ops, diff --git a/include/net/dsa.h b/include/net/dsa.h index 8b6d34e8a6f0..8c16ef23cc10 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -58,6 +58,7 @@ struct tc_action; #define DSA_TAG_PROTO_YT921X_VALUE 30 #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31 #define DSA_TAG_PROTO_MXL862_VALUE 32 +#define DSA_TAG_PROTO_NETC_VALUE 33 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -93,6 +94,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE, DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE, DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, + DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE, }; struct dsa_switch; @@ -1122,7 +1124,8 @@ struct dsa_switch_ops { 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, - const struct flow_action_police *policer); + const struct flow_action_police *policer, + struct netlink_ext_ack *extack); void (*port_policer_del)(struct dsa_switch *ds, int port); int (*port_setup_tc)(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 6e68e359ad18..7dee0ae616e3 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -111,6 +111,11 @@ static inline void fib_rule_get(struct fib_rule *rule) refcount_inc(&rule->refcnt); } +static inline bool fib_rule_get_safe(struct fib_rule *rule) +{ + return refcount_inc_not_zero(&rule->refcnt); +} + static inline void fib_rule_put(struct fib_rule *rule) { if (refcount_dec_and_test(&rule->refcnt)) diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h index 7aa2b8e1fb29..856b5e90fbdb 100644 --- a/include/net/gen_stats.h +++ b/include/net/gen_stats.h @@ -47,19 +47,19 @@ int gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int padattr); int gnet_stats_copy_basic(struct gnet_dump *d, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, bool running); + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running); void gnet_stats_add_basic(struct gnet_stats_basic_sync *bstats, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, bool running); + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running); int gnet_stats_copy_basic_hw(struct gnet_dump *d, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, bool running); + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running); int gnet_stats_copy_rate_est(struct gnet_dump *d, struct net_rate_estimator __rcu **ptr); int gnet_stats_copy_queue(struct gnet_dump *d, - struct gnet_stats_queue __percpu *cpu_q, - struct gnet_stats_queue *q, __u32 qlen); + const struct gnet_stats_queue __percpu *cpu_q, + const struct gnet_stats_queue *q, __u32 qlen); void gnet_stats_add_queue(struct gnet_stats_queue *qstats, const struct gnet_stats_queue __percpu *cpu_q, const struct gnet_stats_queue *q); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 318593743b6e..a71a98505650 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -269,7 +269,6 @@ struct fib_dump_filter { bool filter_set; bool dump_routes; bool dump_exceptions; - bool rtnl_held; unsigned char protocol; unsigned char rt_type; unsigned int flags; @@ -629,6 +628,11 @@ static inline void fib_info_hold(struct fib_info *fi) refcount_inc(&fi->fib_clntref); } +static inline bool fib_info_hold_safe(struct fib_info *fi) +{ + return refcount_inc_not_zero(&fi->fib_clntref); +} + static inline void fib_info_put(struct fib_info *fi) { if (refcount_dec_and_test(&fi->fib_clntref)) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e517eaaa177b..49297fec448a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -44,6 +44,14 @@ #define IP_VS_CONN_TAB_MAX_BITS 20 #endif +/* conn_max limits */ +#if BITS_PER_LONG > 32 +/* Limit of atomic_t but restricted by roundup_pow_of_two() in ip_vs_core.c */ +#define IP_VS_CONN_MAX (1 << 30) +#else +#define IP_VS_CONN_MAX (1 << 24) +#endif + /* svc_table limits */ #define IP_VS_SVC_TAB_MIN_BITS 4 #define IP_VS_SVC_TAB_MAX_BITS 20 @@ -1220,6 +1228,10 @@ struct netns_ipvs { /* sysctl variables */ int sysctl_amemthresh; int sysctl_am_droprate; +#ifdef CONFIG_SYSCTL + int sysctl_conn_max;/* soft limit for conns */ + int conn_max_limit; /* hard limit for conn_max */ +#endif int sysctl_drop_entry; int sysctl_drop_packet; int sysctl_secure_tcp; @@ -1317,6 +1329,11 @@ struct netns_ipvs { #ifdef CONFIG_SYSCTL +static inline int sysctl_conn_max(struct netns_ipvs *ipvs) +{ + return READ_ONCE(ipvs->sysctl_conn_max); +} + static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs) { return ipvs->sysctl_sync_threshold[0]; @@ -1436,6 +1453,11 @@ static inline int sysctl_est_nice(struct netns_ipvs *ipvs) #else +static inline int sysctl_conn_max(struct netns_ipvs *ipvs) +{ + return IP_VS_CONN_MAX; +} + static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs) { return DEFAULT_SYNC_THRESHOLD; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 1dec81faff28..3de07e738538 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -955,6 +955,18 @@ static inline u32 ip6_multipath_hash_fields(const struct net *net) } #endif +/* Derive the IPv6 ECMP hash from txhash so a rehash may pick a different path; + * policy 0 only, and only when txhash is set. >> 1 clears the top bit + * (fib6_select_path() uses mp_hash as a signed 31-bit value); ?: 1 keeps the + * result non-zero, since mp_hash 0 falls back to rt6_multipath_hash(). + */ +static inline void ip6_ecmp_set_mp_hash(const struct net *net, + struct flowi6 *fl6, u32 txhash) +{ + if (ip6_multipath_hash_policy(net) == 0 && txhash) + fl6->mp_hash = (txhash >> 1) ?: 1; +} + /* * Header manipulation */ diff --git a/include/net/llc.h b/include/net/llc.h index e250dca03963..029ba8a22319 100644 --- a/include/net/llc.h +++ b/include/net/llc.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_H #define LLC_H /* * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/include/net/llc_c_ac.h b/include/net/llc_c_ac.h index 7620a9196922..af31a49bebb0 100644 --- a/include/net/llc_c_ac.h +++ b/include/net/llc_c_ac.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_C_AC_H #define LLC_C_AC_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ /* Connection component state transition actions */ /* diff --git a/include/net/llc_c_ev.h b/include/net/llc_c_ev.h index 241889955157..d45140d4289a 100644 --- a/include/net/llc_c_ev.h +++ b/include/net/llc_c_ev.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_C_EV_H #define LLC_C_EV_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/include/net/llc_c_st.h b/include/net/llc_c_st.h index a4bea0f33188..f52a4cc4880d 100644 --- a/include/net/llc_c_st.h +++ b/include/net/llc_c_st.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_C_ST_H #define LLC_C_ST_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h index 374411b3066c..e1a302696723 100644 --- a/include/net/llc_conn.h +++ b/include/net/llc_conn.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_CONN_H #define LLC_CONN_H /* * Copyright (c) 1997 by Procom Technology, Inc. * 2001, 2002 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/include/net/llc_if.h b/include/net/llc_if.h index c72570a21a4f..a333259613ab 100644 --- a/include/net/llc_if.h +++ b/include/net/llc_if.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_IF_H #define LLC_IF_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ /* Defines LLC interface to network layer */ /* Available primitives */ diff --git a/include/net/llc_pdu.h b/include/net/llc_pdu.h index 86681f29bda7..f165237a3723 100644 --- a/include/net/llc_pdu.h +++ b/include/net/llc_pdu.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_PDU_H #define LLC_PDU_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/include/net/llc_s_ac.h b/include/net/llc_s_ac.h index f71790305bc9..076adc5217fd 100644 --- a/include/net/llc_s_ac.h +++ b/include/net/llc_s_ac.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_S_AC_H #define LLC_S_AC_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ struct llc_sap; diff --git a/include/net/llc_s_ev.h b/include/net/llc_s_ev.h index fb7df1d70af3..7b9c94093336 100644 --- a/include/net/llc_s_ev.h +++ b/include/net/llc_s_ev.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_S_EV_H #define LLC_S_EV_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/include/net/llc_s_st.h b/include/net/llc_s_st.h index fca49d483d20..85ce230fd946 100644 --- a/include/net/llc_s_st.h +++ b/include/net/llc_s_st.h @@ -1,15 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_S_ST_H #define LLC_S_ST_H /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/include/net/llc_sap.h b/include/net/llc_sap.h index 1e4df9fd9fb2..b259f5993c46 100644 --- a/include/net/llc_sap.h +++ b/include/net/llc_sap.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LLC_SAP_H #define LLC_SAP_H @@ -6,13 +7,6 @@ /* * Copyright (c) 1997 by Procom Technology,Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ struct llc_sap; struct net_device; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 40cb20d9309c..4f95da023746 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -218,6 +218,8 @@ struct ieee80211_low_level_stats { * bandwidth) OFDMA settings need to be changed * @IEEE80211_CHANCTX_CHANGE_PUNCTURING: The punctured channel(s) bitmap * was changed. + * @IEEE80211_CHANCTX_CHANGE_NPCA: NPCA configuration changed + * @IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT: NPCA puncturing changed */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), @@ -227,6 +229,8 @@ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_MIN_DEF = BIT(4), IEEE80211_CHANCTX_CHANGE_AP = BIT(5), IEEE80211_CHANCTX_CHANGE_PUNCTURING = BIT(6), + IEEE80211_CHANCTX_CHANGE_NPCA = BIT(7), + IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT = BIT(8), }; /** @@ -234,10 +238,13 @@ enum ieee80211_chanctx_change { * @oper: channel definition to use for operation * @ap: the channel definition of the AP, if any * (otherwise the chan member is %NULL) + * @require_npca: If NPCA is configured, require it to + * remain, this is used by AP interfaces */ struct ieee80211_chan_req { struct cfg80211_chan_def oper; struct cfg80211_chan_def ap; + bool require_npca; }; /** @@ -366,6 +373,7 @@ struct ieee80211_vif_chanctx_switch { * @BSS_CHANGED_MLD_TTLM: negotiated TID to link mapping was changed * @BSS_CHANGED_TPE: transmit power envelope changed * @BSS_CHANGED_NAN_LOCAL_SCHED: NAN local schedule changed (NAN mode only) + * @BSS_CHANGED_NPCA: NPCA parameters changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -404,6 +412,7 @@ enum ieee80211_bss_change { BSS_CHANGED_MLD_TTLM = BIT_ULL(34), BSS_CHANGED_TPE = BIT_ULL(35), BSS_CHANGED_NAN_LOCAL_SCHED = BIT_ULL(36), + BSS_CHANGED_NPCA = BIT_ULL(37), /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -589,6 +598,26 @@ struct ieee80211_parsed_tpe { struct ieee80211_parsed_tpe_psd psd_local[2], psd_reg_client[2]; }; +/** + * struct ieee80211_bss_npca_params - NPCA parameters + * @min_dur_thresh: NPCA minimum duration threshold (512 + 128*n usec) + * @switch_delay: NPCA switch delay (units of 4 usec) + * @switch_back_delay: NPCA switch back delay (units of 4 usec) + * @init_qsrc: initial QSRC value + * @moplen: indicates MOPLEN NPCA is permitted in the BSS + * @enabled: NPCA is enabled for this link + * + * Note: the individual values (except @enabled) are in spec representation. + */ +struct ieee80211_bss_npca_params { + u32 min_dur_thresh:4, + switch_delay:6, + switch_back_delay:6, + init_qsrc:2, + moplen:1, + enabled:1; +}; + /** * struct ieee80211_bss_conf - holds the BSS's changing parameters * @@ -763,6 +792,7 @@ struct ieee80211_parsed_tpe { * (as opposed to hearing its value from another link's beacon). * @s1g_long_beacon_period: number of beacon intervals between each long * beacon transmission. + * @npca: NPCA parameters */ struct ieee80211_bss_conf { struct ieee80211_vif *vif; @@ -866,6 +896,8 @@ struct ieee80211_bss_conf { u8 bss_param_ch_cnt_link_id; u8 s1g_long_beacon_period; + + struct ieee80211_bss_npca_params npca; }; #define IEEE80211_NAN_MAX_CHANNELS 3 @@ -1672,6 +1704,7 @@ enum mac80211_rx_encoding { RX_ENC_HE, RX_ENC_EHT, RX_ENC_UHR, + RX_ENC_S1G, }; /** @@ -1747,18 +1780,18 @@ struct ieee80211_rx_status { u8 he_ru:3; u8 he_gi:2; u8 he_dcm:1; - }; + } __packed; struct { u8 ru:4; u8 gi:2; - } eht; + } __packed eht; struct { u8 ru:4; u8 gi:2; u8 elr:1; u8 im:1; - } uhr; - }; + } __packed uhr; + } __packed; u8 rate_idx; u8 nss; u8 rx_flags; @@ -1771,6 +1804,8 @@ struct ieee80211_rx_status { u8 link_valid:1, link_id:4; }; +static_assert(sizeof(struct ieee80211_rx_status) <= sizeof_field(struct sk_buff, cb)); + static inline u32 ieee80211_rx_status_to_khz(struct ieee80211_rx_status *rx_status) { @@ -1979,12 +2014,16 @@ enum ieee80211_vif_flags { * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled * The driver supports passing received 802.11 frames as 802.3 frames to * mac80211. + * @IEEE80211_OFFLOAD_ENCAP_MCAST: tx multicast encapsulation offload is enabled + * The driver supports sending multicast frames passed as 802.3 frames + * by mac80211. */ enum ieee80211_offload_flags { IEEE80211_OFFLOAD_ENCAP_ENABLED = BIT(0), IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1), IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2), + IEEE80211_OFFLOAD_ENCAP_MCAST = BIT(3), }; #define IEEE80211_NAN_AVAIL_BLOB_MAX_LEN 54 @@ -2672,6 +2711,8 @@ struct ieee80211_link_sta { * @epp_peer: indicates that the peer is an EPP peer. * @nmi: For NDI stations, pointer to the NMI station of the peer. * @nan_sched: NAN peer schedule for this station. Valid only for NMI stations. + * @ext_mld_capa_ops: the MLD's extended MLD capabilities and operations + * NOTE: currently only tracked for AP STAs */ struct ieee80211_sta { u8 addr[ETH_ALEN] __aligned(2); @@ -2696,6 +2737,7 @@ struct ieee80211_sta { struct ieee80211_txq *txq[IEEE80211_NUM_TIDS + 1]; u16 valid_links; + u16 ext_mld_capa_ops; bool epp_peer; struct ieee80211_link_sta deflink; struct ieee80211_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -7889,6 +7931,35 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif, */ void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif); +/** + * ieee80211_nan_cluster_joined - notify about NAN cluster join. + * + * This function is used to notify mac80211 about NAN cluster join. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @cluster_id: the cluster ID that was joined + * @new_cluster: true if this is a new cluster + * @gfp: allocation flags + */ +void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif, + const u8 *cluster_id, bool new_cluster, + gfp_t gfp); + +/** + * ieee80211_nan_try_evacuate - try to evacuate a NAN channel + * + * This function tries to evacuate a NAN channel that is using the given + * channel context, to free up channel context resources. + * + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @conf: the channel context configuration to try to evacuate. If %NULL, + * the NAN channel that has the fewest slots scheduled will be evacuated. + * + * Return: %true if a channel was evacuated, %false otherwise + */ +bool ieee80211_nan_try_evacuate(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); + /** * ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX. * diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h index 6d836060976a..0c395917b214 100644 --- a/include/net/mana/gdma.h +++ b/include/net/mana/gdma.h @@ -342,6 +342,7 @@ struct gdma_queue { void *context; unsigned int msix_index; + unsigned int irq; u32 log2_throttle_limit; } eq; @@ -388,6 +389,11 @@ struct gdma_irq_context { spinlock_t lock; struct list_head eq_list; char name[MANA_IRQ_NAME_SZ]; + unsigned int msi; + unsigned int irq; + refcount_t refcount; + unsigned int bitmap_refs; + bool dyn_msix; }; enum gdma_context_flags { @@ -399,8 +405,10 @@ struct gdma_context { struct device *dev; struct dentry *mana_pci_debugfs; - /* Per-vPort max number of queues */ + /* Hardware max number of queues */ unsigned int max_num_queues; + /* Per-vPort max number of queues */ + unsigned int max_num_queues_vport; unsigned int max_num_msix; unsigned int num_msix_usable; struct xarray irq_contexts; @@ -418,6 +426,7 @@ struct gdma_context { u32 test_event_eq_id; bool is_pf; + bool is_pf2; phys_addr_t bar0_pa; void __iomem *bar0_va; @@ -442,10 +451,20 @@ struct gdma_context { struct gdma_dev mana_ib; u64 pf_cap_flags1; + u64 gdma_protocol_ver; struct workqueue_struct *service_wq; unsigned long flags; + + /* Protect access to GIC context */ + struct mutex gic_mutex; + + /* Indicate if this device is sharing MSI for EQs on MANA */ + bool msi_sharing; + + /* Bitmap tracks where MSI is allocated when it is not shared for EQs */ + unsigned long *msi_bitmap; }; static inline bool mana_gd_is_mana(struct gdma_dev *gd) @@ -570,6 +589,7 @@ struct gdma_eqe { #define GDMA_SRIOV_REG_CFG_BASE_OFF 0x108 #define MANA_PF_DEVICE_ID 0x00B9 +#define MANA_PF2_DEVICE_ID 0x00C1 #define MANA_VF_DEVICE_ID 0x00BA struct gdma_posted_wqe_info { @@ -598,6 +618,7 @@ enum { #define GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG BIT(3) #define GDMA_DRV_CAP_FLAG_1_GDMA_PAGES_4MB_1GB_2GB BIT(4) #define GDMA_DRV_CAP_FLAG_1_VARIABLE_INDIRECTION_TABLE_SUPPORT BIT(5) +#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6) /* Driver can handle holes (zeros) in the device list */ #define GDMA_DRV_CAP_FLAG_1_DEV_LIST_HOLES_SUP BIT(11) @@ -614,7 +635,8 @@ enum { /* Driver detects stalled send queues and recovers them */ #define GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY BIT(18) -#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6) +/* Driver supports separate EQ/MSIs for each vPort */ +#define GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT BIT(19) /* Driver supports linearizing the skb when num_sge exceeds hardware limit */ #define GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE BIT(20) @@ -642,7 +664,8 @@ enum { GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE | \ GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \ GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY | \ - GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY) + GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY | \ + GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT) #define GDMA_DRV_CAP_FLAGS2 0 @@ -1018,4 +1041,11 @@ int mana_gd_resume(struct pci_dev *pdev); bool mana_need_log(struct gdma_context *gc, int err); +struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc, + bool use_msi_bitmap, + int *msi_requested); +void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi); +int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver, + u32 proto_minor_ver, u32 proto_micro_ver, + u16 *max_num_vports, u8 *bm_hostmode); #endif /* _GDMA_H */ diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 8f721cd4e4a7..13c87baf018e 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -480,8 +480,6 @@ struct mana_context { u8 bm_hostmode; struct mana_ethtool_hc_stats hc_stats; - struct mana_eq *eqs; - struct dentry *mana_eqs_debugfs; struct workqueue_struct *per_port_queue_reset_wq; /* Workqueue for querying hardware stats */ struct delayed_work gf_stats_work; @@ -501,13 +499,16 @@ struct mana_port_context { u8 mac_addr[ETH_ALEN]; + struct mana_eq *eqs; + struct dentry *mana_eqs_debugfs; + enum TRI_STATE rss_state; mana_handle_t default_rxobj; bool tx_shortform_allowed; u16 tx_vp_offset; - struct mana_tx_qp *tx_qp; + struct mana_tx_qp **tx_qp; /* Indirection Table for RX & TX. The values are queue indexes */ u32 *indir_table; @@ -547,6 +548,12 @@ struct mana_port_context { struct mutex vport_mutex; int vport_use_count; + /* Set by mana_set_channels() under vport_mutex to block RDMA + * from grabbing the vport during the detach/attach window. + * Checked by mana_cfg_vport() when called from the RDMA path. + */ + bool channel_changing; + /* Net shaper handle*/ struct net_shaper_handle handle; @@ -555,6 +562,10 @@ struct mana_port_context { u32 speed; /* Maximum speed supported by the SKU (mbps) */ u32 max_speed; + /* 1 = not queried, 0 = cached success, negative = permanent error. + * Protected by the netdev instance lock. + */ + int link_cfg_error; bool port_is_up; bool port_st_save; /* Saved port state */ @@ -568,6 +579,14 @@ struct mana_port_context { /* Debugfs */ struct dentry *mana_port_debugfs; + + /* Cached vport/steering config for debugfs */ + u32 vport_max_sq; + u32 vport_max_rq; + u32 steer_rx; + u32 steer_rss; + bool steer_update_tab; + u32 steer_cqe_coalescing; }; netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); @@ -1032,8 +1051,10 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, mana_handle_t wq_obj); int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, - u32 doorbell_pg_id); + u32 doorbell_pg_id, bool check_channel_changing); void mana_uncfg_vport(struct mana_port_context *apc); +int mana_create_eq(struct mana_port_context *apc); +void mana_destroy_eq(struct mana_port_context *apc); struct net_device *mana_get_primary_netdev(struct mana_context *ac, u32 port_index, diff --git a/include/net/mld.h b/include/net/mld.h index c07359808493..23255cc47ced 100644 --- a/include/net/mld.h +++ b/include/net/mld.h @@ -89,29 +89,196 @@ struct mld2_query { #define MLDV2_QQIC_EXP(value) (((value) >> 4) & 0x07) #define MLDV2_QQIC_MAN(value) ((value) & 0x0f) -#define MLD_EXP_MIN_LIMIT 32768UL -#define MLDV1_MRD_MAX_COMPAT (MLD_EXP_MIN_LIMIT - 1) +/* MLDv2 QQIC floating-point exponential field min threshold */ +#define MLD_QQIC_MIN_THRESHOLD 128 +/* MLDv2 QQIC FP max threshold (mant = 0xF, exp = 7) -> 31744 */ +#define MLD_QQIC_MAX_THRESHOLD 31744 +/* MLDv2 MRC floating-point exponential field min threshold */ +#define MLD_MRC_MIN_THRESHOLD 32768UL +/* MLDv2 MRC FP max threshold (mant = 0xFFF, exp = 7) -> 8387584 */ +#define MLD_MRC_MAX_THRESHOLD 8387584 +#define MLDV1_MRD_MAX_COMPAT (MLD_MRC_MIN_THRESHOLD - 1) #define MLD_MAX_QUEUE 8 #define MLD_MAX_SKBS 32 -static inline unsigned long mldv2_mrc(const struct mld2_query *mlh2) -{ - /* RFC3810, 5.1.3. Maximum Response Code */ - unsigned long ret, mc_mrc = ntohs(mlh2->mld2q_mrc); +/* V2 exponential field encoding */ - if (mc_mrc < MLD_EXP_MIN_LIMIT) { - ret = mc_mrc; +/* + * Calculate Maximum Response Code from Maximum Response Delay + * + * MRC represents the 16-bit encoded form of Maximum Response Delay (MRD); + * once decoded, the resulting value is in milliseconds. + * + * RFC3810, 5.1.3. defines only the decoding formula: + * Maximum Response Delay = (mant | 0x1000) << (exp + 3) + * + * but does NOT define the encoding procedure. To derive exponent: + * + * For the 16-bit MRC, the "hidden bit" (0x1000) is left shifted by 12 to + * sit above the 12-bit mantissa. The RFC then shifts this entire block + * left by (exp + 3) to reconstruct the value. So, 'hidden bit' is the + * MSB which is shifted by (12 + exp + 3). + * + * Total left shift of the hidden bit = 12 + (exp + 3) = exp + 15. + * This is the MSB at the 0-based bit position: (exp + 15). + * Since fls() is 1-based, fls(value) - 1 = exp + 15. + * + * Therefore: + * exp = fls(value) - 16 + * mant = (value >> (exp + 3)) & 0x0FFF + * + * Final encoding formula: + * 0x8000 | (exp << 12) | mant + * + * Example (value = 1311744): + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0| 1311744 + * | ^-^--------mant---------^ ^...(exp+3)...^| exp=5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Encoded: + * 0x8000 | (5 << 12) | 0x404 = 0xD404 + */ +static inline u16 mldv2_mrc(unsigned long mrd) +{ + u16 mc_man, mc_exp; + + /* MRC < 32768 is literal */ + if (mrd < MLD_MRC_MIN_THRESHOLD) + return mrd; + + /* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */ + if (mrd >= MLD_MRC_MAX_THRESHOLD) + return 0xFFFF; + + mc_exp = fls(mrd) - 16; + mc_man = (mrd >> (mc_exp + 3)) & 0x0FFF; + + return 0x8000 | (mc_exp << 12) | mc_man; +} + +/* + * Calculate Querier's Query Interval Code from Querier's Query Interval + * + * QQIC represents the 8-bit encoded form of Querier's Query Interval (QQI); + * once decoded, the resulting value is in seconds. + * + * RFC3810, 5.1.9. defines only the decoding formula: + * QQI = (mant | 0x10) << (exp + 3) + * + * but does NOT define the encoding procedure. To derive exponent: + * + * For any value of mantissa and exponent, the decoding formula indicates + * that the "hidden bit" (0x10) is shifted 4 bits left to sit above the + * 4-bit mantissa. The RFC again shifts this entire block left by (exp + 3) + * to reconstruct the value. So, 'hidden bit' is the MSB which is shifted + * by (4 + exp + 3). + * + * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7. + * This is the MSB at the 0-based bit position: (exp + 7). + * Since fls() is 1-based, fls(value) - 1 = exp + 7. + * + * Therefore: + * exp = fls(value) - 8 + * mant = (value >> (exp + 3)) & 0x0F + * + * Final encoding formula: + * 0x80 | (exp << 4) | mant + * + * Example (value = 3200): + * 0 1 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200) + * | ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Encoded: + * 0x80 | (4 << 4) | 9 = 0xC9 + */ +static inline u8 mldv2_qqic(unsigned long value) +{ + u8 mc_man, mc_exp; + + /* QQIC < 128 is literal */ + if (value < MLD_QQIC_MIN_THRESHOLD) + return value; + + /* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */ + if (value >= MLD_QQIC_MAX_THRESHOLD) + return 0xFF; + + mc_exp = fls(value) - 8; + mc_man = (value >> (mc_exp + 3)) & 0x0F; + + return 0x80 | (mc_exp << 4) | mc_man; +} + +/* V2 exponential field decoding */ + +/* Calculate Maximum Response Delay from Maximum Response Code + * + * RFC3810, relevant sections: + * - 5.1.3. Maximum Response Code defines the decoding formula: + * 0 1 2 3 4 5 6 7 8 9 A B C D E F + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |1| exp | mant | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * Maximum Response Delay = (mant | 0x1000) << (exp+3) + * - 9.3. Query Response Interval + * + * After decode, MRC represents the Maximum Response Delay (MRD) in + * units of milliseconds. + */ +static inline unsigned long mldv2_mrd(const struct mld2_query *mlh2) +{ + unsigned long mc_mrc = ntohs(mlh2->mld2q_mrc); + + if (mc_mrc < MLD_MRC_MIN_THRESHOLD) { + return mc_mrc; } else { unsigned long mc_man, mc_exp; mc_exp = MLDV2_MRC_EXP(mc_mrc); mc_man = MLDV2_MRC_MAN(mc_mrc); - ret = (mc_man | 0x1000) << (mc_exp + 3); + return (mc_man | 0x1000) << (mc_exp + 3); } +} - return ret; +/* Calculate Querier's Query Interval from Querier's Query Interval Code + * + * RFC3810, relevant sections: + * - 5.1.9. QQIC (Querier's Query Interval Code) defines the decoding formula: + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |1| exp | mant | + * +-+-+-+-+-+-+-+-+ + * QQI = (mant | 0x10) << (exp + 3) + * - 9.2. Query Interval + * - 9.12. Older Version Querier Present Timeout + * (the [Query Interval] in the last Query received) + * + * After decode, QQIC represents the Querier's Query Interval in units + * of seconds. + */ +static inline unsigned long mldv2_qqi(const struct mld2_query *mlh2) +{ + unsigned long qqic = mlh2->mld2q_qqic; + + if (qqic < MLD_QQIC_MIN_THRESHOLD) { + return qqic; + } else { + unsigned long mc_man, mc_exp; + + mc_exp = MLDV2_QQIC_EXP(qqic); + mc_man = MLDV2_QQIC_MAN(qqic); + + return (mc_man | 0x10) << (mc_exp + 3); + } } #endif diff --git a/include/net/mptcp.h b/include/net/mptcp.h index ee70f597a4de..71b9fc5a5796 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -72,7 +72,8 @@ struct mptcp_out_options { u8 reset_reason:4, reset_transient:1, csum_reqd:1, - allow_join_id0:1; + allow_join_id0:1, + drop_ts:1; union { struct { u64 sndr_key; @@ -152,9 +153,9 @@ bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb, unsigned int *size, struct mptcp_out_options *opts); bool mptcp_synack_options(const struct request_sock *req, unsigned int *size, struct mptcp_out_options *opts); -bool mptcp_established_options(struct sock *sk, struct sk_buff *skb, - unsigned int *size, unsigned int remaining, - struct mptcp_out_options *opts); +int mptcp_established_options(struct sock *sk, struct sk_buff *skb, + unsigned int remaining, bool has_ts, + struct mptcp_out_options *opts); bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb); void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp, @@ -269,15 +270,6 @@ static inline bool mptcp_synack_options(const struct request_sock *req, return false; } -static inline bool mptcp_established_options(struct sock *sk, - struct sk_buff *skb, - unsigned int *size, - unsigned int remaining, - struct mptcp_out_options *opts) -{ - return false; -} - static inline bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) { diff --git a/include/net/ncsi.h b/include/net/ncsi.h index 08a50d9acb0a..e886358207aa 100644 --- a/include/net/ncsi.h +++ b/include/net/ncsi.h @@ -60,7 +60,7 @@ static inline int ncsi_start_dev(struct ncsi_dev *nd) return -ENOTTY; } -static void ncsi_stop_dev(struct ncsi_dev *nd) +static inline void ncsi_stop_dev(struct ncsi_dev *nd) { } diff --git a/include/net/netdev_lock.h b/include/net/netdev_lock.h index 3d3aef80beac..9fb3e93857c3 100644 --- a/include/net/netdev_lock.h +++ b/include/net/netdev_lock.h @@ -64,7 +64,7 @@ static inline void netdev_unlock_full_to_ops(struct net_device *dev) netdev_unlock(dev); } -static inline void netdev_ops_assert_locked(const struct net_device *dev) +static inline void netdev_assert_locked_ops_compat(const struct net_device *dev) { if (netdev_need_ops_lock(dev)) lockdep_assert_held(&dev->lock); @@ -73,11 +73,17 @@ static inline void netdev_ops_assert_locked(const struct net_device *dev) } static inline void -netdev_ops_assert_locked_or_invisible(const struct net_device *dev) +netdev_assert_locked_ops_compat_or_invisible(const struct net_device *dev) { if (dev->reg_state == NETREG_REGISTERED || dev->reg_state == NETREG_UNREGISTERING) - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); +} + +static inline void netdev_assert_locked_ops(const struct net_device *dev) +{ + if (netdev_need_ops_lock(dev)) + netdev_assert_locked(dev); } static inline void netdev_lock_ops_compat(struct net_device *dev) @@ -96,6 +102,14 @@ static inline void netdev_unlock_ops_compat(struct net_device *dev) rtnl_unlock(); } +/* Matching "ops protected" category from netdevice.h */ +static inline int netdev_is_locked_ops_compat(const struct net_device *dev) +{ + if (netdev_need_ops_lock(dev)) + return lockdep_is_held(&dev->lock); + return lockdep_rtnl_is_held(); +} + static inline int netdev_lock_cmp_fn(const struct lockdep_map *a, const struct lockdep_map *b) { @@ -132,6 +146,9 @@ static inline int netdev_lock_cmp_fn(const struct lockdep_map *a, #define netdev_lock_dereference(p, dev) \ rcu_dereference_protected(p, lockdep_is_held(&(dev)->lock)) +#define netdev_ops_lock_dereference(p, dev) \ + rcu_dereference_protected(p, netdev_is_locked_ops_compat(dev)) + int netdev_debug_event(struct notifier_block *nb, unsigned long event, void *ptr); diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index b39417ad955e..0b07d5e69c15 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -20,4 +20,8 @@ extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp; extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_gre; #endif +#if IS_ENABLED(CONFIG_NF_CONNTRACK_PPTP) +void gre_pptp_destroy_siblings(struct nf_conn *ct); +#endif + #endif /*_NF_CONNTRACK_IPV4_H*/ diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 0b247248b032..fd5c4dbf72ca 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -38,7 +38,6 @@ enum nf_ct_ext_id { struct nf_ct_ext { u8 offset[NF_CT_EXT_NUM]; u8 len; - unsigned int gen_id; char data[] __aligned(8); }; @@ -52,8 +51,6 @@ static inline bool nf_ct_ext_exist(const struct nf_conn *ct, u8 id) return (ct->ext && __nf_ct_ext_exist(ct->ext, id)); } -void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id); - static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id) { struct nf_ct_ext *ext = ct->ext; @@ -61,19 +58,10 @@ static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id) if (!ext || !__nf_ct_ext_exist(ext, id)) return NULL; - if (unlikely(ext->gen_id)) - return __nf_ct_ext_find(ext, id); - return (void *)ct->ext + ct->ext->offset[id]; } /* 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); -/* ext genid. if ext->id != ext_genid, extensions cannot be used - * anymore unless conntrack has CONFIRMED bit set. - */ -extern atomic_t nf_conntrack_ext_genid; -void nf_ct_ext_bump_genid(void); - #endif /* _NF_CONNTRACK_EXTEND_H */ diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 24cf3d2d9745..81025101f86d 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -29,23 +29,28 @@ enum nf_ct_helper_flags { #define NF_CT_HELPER_NAME_LEN 16 +/* Must be kept in sync with the classes defined by helpers */ +#define NF_CT_MAX_EXPECT_CLASSES 4 + struct nf_conntrack_helper { struct hlist_node hnode; /* Internal use. */ + struct rcu_head rcu; + char name[NF_CT_HELPER_NAME_LEN]; /* name of the module */ - refcount_t refcnt; struct module *me; /* pointer to self */ - const struct nf_conntrack_expect_policy *expect_policy; + struct nf_conntrack_expect_policy expect_policy[NF_CT_MAX_EXPECT_CLASSES]; + + refcount_t ct_refcnt; /* Tuple of things we will help (compared against server response) */ struct nf_conntrack_tuple tuple; /* Function to call when data passes; return verdict, or -1 to invalidate. */ - int (*help)(struct sk_buff *skb, - unsigned int protoff, - struct nf_conn *ct, - enum ip_conntrack_info conntrackinfo); + int __rcu (*help)(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info conntrackinfo); void (*destroy)(struct nf_conn *ct); @@ -63,9 +68,6 @@ struct nf_conntrack_helper { char nat_mod_name[NF_CT_HELPER_NAME_LEN]; }; -/* Must be kept in sync with the classes defined by helpers */ -#define NF_CT_MAX_EXPECT_CLASSES 4 - /* nf_conn feature for connections that have a helper */ struct nf_conn_help { /* Helper. if any */ @@ -103,11 +105,13 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, struct nf_conn *ct), struct module *module); -int nf_conntrack_helper_register(struct nf_conntrack_helper *); +int nf_conntrack_helper_register(struct nf_conntrack_helper *, struct nf_conntrack_helper **); +int __nf_conntrack_helper_register(struct nf_conntrack_helper *); void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); -int nf_conntrack_helpers_register(struct nf_conntrack_helper *, unsigned int); -void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *, +int nf_conntrack_helpers_register(struct nf_conntrack_helper *, unsigned int, + struct nf_conntrack_helper **); +void nf_conntrack_helpers_unregister(struct nf_conntrack_helper **, unsigned int); struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); @@ -132,10 +136,26 @@ static inline void *nfct_help_data(const struct nf_conn *ct) struct nf_conn_help *help; help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); + if (!help) + return NULL; return (void *)help->data; } +static inline void nf_ct_help_put(const struct nf_conn *ct) +{ + struct nf_conntrack_helper *helper; + struct nf_conn_help *help; + + help = nfct_help(ct); + if (!help) + return; + + helper = rcu_dereference(help->helper); + if (helper && refcount_dec_and_test(&helper->ct_refcnt)) + kfree_rcu(helper, rcu); +} + int nf_conntrack_helper_init(void); void nf_conntrack_helper_fini(void); diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index 3a66d4abb6d6..d60aa86be019 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -12,6 +12,7 @@ #define CTNL_TIMEOUT_NAME_MAX 32 struct nf_ct_timeout { + refcount_t refcnt; __u16 l3num; const struct nf_conntrack_l4proto *l4proto; struct rcu_head rcu; @@ -22,6 +23,22 @@ struct nf_conn_timeout { struct nf_ct_timeout __rcu *timeout; }; +static inline void nf_ct_timeout_put(const struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + struct nf_conn_timeout *timeout_ext; + struct nf_ct_timeout *timeout; + + timeout_ext = nf_ct_ext_find(ct, NF_CT_EXT_TIMEOUT); + if (!timeout_ext) + return; + + timeout = rcu_dereference(timeout_ext->timeout); + if (timeout && refcount_dec_and_test(&timeout->refcnt)) + kfree_rcu(timeout, rcu); +#endif +} + static inline unsigned int * nf_ct_timeout_data(const struct nf_conn_timeout *t) { @@ -56,8 +73,14 @@ struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct, #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct nf_conn_timeout *timeout_ext; + if (!timeout) + return NULL; + timeout_ext = nf_ct_ext_add(ct, NF_CT_EXT_TIMEOUT, gfp); - if (timeout_ext == NULL) + if (!timeout_ext || timeout_ext->timeout) + return NULL; + + if (!refcount_inc_not_zero(&timeout->refcnt)) return NULL; rcu_assign_pointer(timeout_ext->timeout, timeout); @@ -75,7 +98,7 @@ static inline unsigned int *nf_ct_timeout_lookup(const struct nf_conn *ct) struct nf_conn_timeout *timeout_ext; timeout_ext = nf_ct_timeout_find(ct); - if (timeout_ext) + if (timeout_ext && rcu_access_pointer(timeout_ext->timeout)) timeouts = nf_ct_timeout_data(timeout_ext); #endif return timeouts; diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h index 609bcf422a9b..f6b05bd80c3f 100644 --- a/include/net/netfilter/nf_dup_netdev.h +++ b/include/net/netfilter/nf_dup_netdev.h @@ -11,15 +11,39 @@ void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif); #define NF_RECURSION_LIMIT 2 -static inline u8 *nf_get_nf_dup_skb_recursion(void) -{ #ifndef CONFIG_PREEMPT_RT - return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion); -#else - return ¤t->net_xmit.nf_dup_skb_recursion; -#endif +static inline bool nf_dev_xmit_recursion(void) +{ + return unlikely(__this_cpu_read(softnet_data.xmit.nf_dup_skb_recursion) > + NF_RECURSION_LIMIT); } +static inline void nf_dev_xmit_recursion_inc(void) +{ + __this_cpu_inc(softnet_data.xmit.nf_dup_skb_recursion); +} + +static inline void nf_dev_xmit_recursion_dec(void) +{ + __this_cpu_dec(softnet_data.xmit.nf_dup_skb_recursion); +} +#else +static inline bool nf_dev_xmit_recursion(void) +{ + return unlikely(current->net_xmit.nf_dup_skb_recursion > NF_RECURSION_LIMIT); +} + +static inline void nf_dev_xmit_recursion_inc(void) +{ + current->net_xmit.nf_dup_skb_recursion++; +} + +static inline void nf_dev_xmit_recursion_dec(void) +{ + current->net_xmit.nf_dup_skb_recursion--; +} +#endif + struct nft_offload_ctx; struct nft_flow_rule; diff --git a/include/net/netlink.h b/include/net/netlink.h index 546d10586576..b5048dd1d511 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -2260,7 +2260,10 @@ static inline struct nlattr *nla_nest_start(struct sk_buff *skb, int attrtype) */ static inline int nla_nest_end(struct sk_buff *skb, struct nlattr *start) { - start->nla_len = skb_tail_pointer(skb) - (unsigned char *)start; + unsigned long diff = skb_tail_pointer(skb) - (unsigned char *)start; + + DEBUG_NET_WARN_ON_ONCE(diff > U16_MAX); + start->nla_len = diff; return skb->len; } diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 875916d60bfe..ffda15318be5 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -112,13 +112,14 @@ struct netns_ipv6 { struct list_head mr6_tables; struct fib_rules_ops *mr6_rules_ops; #endif + struct fib_notifier_ops *ip6mr_notifier_ops; + atomic_t ipmr_seq; + struct mutex mfc_mutex; #endif atomic_t dev_addr_genid; atomic_t fib6_sernum; struct seg6_pernet_data *seg6_data; struct fib_notifier_ops *notifier_ops; - struct fib_notifier_ops *ip6mr_notifier_ops; - atomic_t ipmr_seq; int flowlabel_count; struct { struct hlist_head head; diff --git a/include/net/page_pool/helpers.h b/include/net/page_pool/helpers.h index 3247026e096a..e2730dd273b2 100644 --- a/include/net/page_pool/helpers.h +++ b/include/net/page_pool/helpers.h @@ -64,7 +64,7 @@ int page_pool_ethtool_stats_get_count(void); u8 *page_pool_ethtool_stats_get_strings(u8 *data); u64 *page_pool_ethtool_stats_get(u64 *data, const void *stats); -bool page_pool_get_stats(const struct page_pool *pool, +void page_pool_get_stats(const struct page_pool *pool, struct page_pool_stats *stats); #else static inline int page_pool_ethtool_stats_get_count(void) diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 99ac747b7906..3bd08d7f39c1 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -1046,6 +1046,7 @@ struct tc_tbf_qopt_offload_replace_params { }; struct tc_tbf_qopt_offload { + struct netlink_ext_ack *extack; enum tc_tbf_command command; u32 handle; u32 parent; diff --git a/include/net/psp/types.h b/include/net/psp/types.h index 25a9096d4e7d..87991a1ea02d 100644 --- a/include/net/psp/types.h +++ b/include/net/psp/types.h @@ -5,6 +5,7 @@ #include #include +#include struct netlink_ext_ack; @@ -43,9 +44,29 @@ struct psp_dev_config { u32 versions; }; +/* Max number of devices that can be associated with a single PSP device. + * Each entry consumes ~24 bytes in the netlink dev-get response, and the + * response must fit in GENLMSG_DEFAULT_SIZE (~3.7KB). + */ +#define PSP_ASSOC_DEV_MAX 128 + +/** + * struct psp_assoc_dev - wrapper for associated net_device + * @dev_list: list node for psp_dev::assoc_dev_list + * @assoc_dev: the associated net_device + * @dev_tracker: tracker for the net_device reference + */ +struct psp_assoc_dev { + struct list_head dev_list; + struct net_device *assoc_dev; + netdevice_tracker dev_tracker; +}; + /** * struct psp_dev - PSP device struct * @main_netdev: original netdevice of this PSP device + * @assoc_dev_list: list of psp_assoc_dev entries associated with this PSP device + * @assoc_dev_cnt: number of entries in @assoc_dev_list * @ops: driver callbacks * @caps: device capabilities * @drv_priv: driver priv pointer @@ -67,6 +88,8 @@ struct psp_dev_config { */ struct psp_dev { struct net_device *main_netdev; + struct list_head assoc_dev_list; + int assoc_dev_cnt; struct psp_dev_ops *ops; struct psp_dev_caps *caps; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 11159a50d6a1..45a1e8c78222 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -86,7 +86,7 @@ struct Qdisc { #define TCQ_F_WARN_NONWC (1 << 16) #define TCQ_F_CPUSTATS 0x20 /* run using percpu statistics */ #define TCQ_F_NOPARENT 0x40 /* root of its hierarchy : - * qdisc_tree_decrease_qlen() should stop. + * qdisc_tree_reduce_backlog() should stop. */ #define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */ #define TCQ_F_NOLOCK 0x100 /* qdisc does not require locking */ @@ -542,6 +542,21 @@ static inline int qdisc_qlen(const struct Qdisc *q) return q->q.qlen; } +static inline int qdisc_qlen_lockless(const struct Qdisc *q) +{ + return READ_ONCE(q->q.qlen); +} + +static inline void qdisc_qlen_inc(struct Qdisc *q) +{ + WRITE_ONCE(q->q.qlen, q->q.qlen + 1); +} + +static inline void qdisc_qlen_dec(struct Qdisc *q) +{ + WRITE_ONCE(q->q.qlen, q->q.qlen - 1); +} + static inline int qdisc_qlen_sum(const struct Qdisc *q) { __u32 qlen = q->qstats.qlen; @@ -549,9 +564,9 @@ static inline int qdisc_qlen_sum(const struct Qdisc *q) if (qdisc_is_percpu_stats(q)) { for_each_possible_cpu(i) - qlen += per_cpu_ptr(q->cpu_qstats, i)->qlen; + qlen += READ_ONCE(per_cpu_ptr(q->cpu_qstats, i)->qlen); } else { - qlen += q->q.qlen; + qlen += qdisc_qlen_lockless(q); } return qlen; @@ -937,6 +952,15 @@ static inline void _bstats_update(struct gnet_stats_basic_sync *bstats, u64_stats_update_end(&bstats->syncp); } +static inline void _bstats_set(struct gnet_stats_basic_sync *bstats, + u64 bytes, u64 packets) +{ + u64_stats_update_begin(&bstats->syncp); + u64_stats_set(&bstats->bytes, bytes); + u64_stats_set(&bstats->packets, packets); + u64_stats_update_end(&bstats->syncp); +} + static inline void bstats_update(struct gnet_stats_basic_sync *bstats, const struct sk_buff *skb) { @@ -955,10 +979,15 @@ static inline void qdisc_bstats_update(struct Qdisc *sch, bstats_update(&sch->bstats, skb); } +static inline void qstats_backlog_sub(struct Qdisc *sch, u32 val) +{ + WRITE_ONCE(sch->qstats.backlog, sch->qstats.backlog - val); +} + static inline void qdisc_qstats_backlog_dec(struct Qdisc *sch, const struct sk_buff *skb) { - sch->qstats.backlog -= qdisc_pkt_len(skb); + qstats_backlog_sub(sch, qdisc_pkt_len(skb)); } static inline void qdisc_qstats_cpu_backlog_dec(struct Qdisc *sch, @@ -967,10 +996,15 @@ static inline void qdisc_qstats_cpu_backlog_dec(struct Qdisc *sch, this_cpu_sub(sch->cpu_qstats->backlog, qdisc_pkt_len(skb)); } +static inline void qstats_backlog_add(struct Qdisc *sch, u32 val) +{ + WRITE_ONCE(sch->qstats.backlog, sch->qstats.backlog + val); +} + static inline void qdisc_qstats_backlog_inc(struct Qdisc *sch, const struct sk_buff *skb) { - sch->qstats.backlog += qdisc_pkt_len(skb); + qstats_backlog_add(sch, qdisc_pkt_len(skb)); } static inline void qdisc_qstats_cpu_backlog_inc(struct Qdisc *sch, @@ -996,17 +1030,22 @@ static inline void qdisc_qstats_cpu_requeues_inc(struct Qdisc *sch) static inline void __qdisc_qstats_drop(struct Qdisc *sch, int count) { - sch->qstats.drops += count; + WRITE_ONCE(sch->qstats.drops, sch->qstats.drops + count); } static inline void qstats_drop_inc(struct gnet_stats_queue *qstats) { - qstats->drops++; + WRITE_ONCE(qstats->drops, qstats->drops + 1); } -static inline void qstats_overlimit_inc(struct gnet_stats_queue *qstats) +static inline void qstats_cpu_drop_inc(struct gnet_stats_queue __percpu *qstats) { - qstats->overlimits++; + this_cpu_inc(qstats->drops); +} + +static inline void qstats_cpu_overlimit_inc(struct gnet_stats_queue __percpu *qstats) +{ + this_cpu_inc(qstats->overlimits); } static inline void qdisc_qstats_drop(struct Qdisc *sch) @@ -1021,23 +1060,23 @@ static inline void qdisc_qstats_cpu_drop(struct Qdisc *sch) static inline void qdisc_qstats_overlimit(struct Qdisc *sch) { - sch->qstats.overlimits++; + WRITE_ONCE(sch->qstats.overlimits, sch->qstats.overlimits + 1); } -static inline int qdisc_qstats_copy(struct gnet_dump *d, struct Qdisc *sch) +static inline int qdisc_qstats_copy(struct gnet_dump *d, const struct Qdisc *sch) { __u32 qlen = qdisc_qlen_sum(sch); return gnet_stats_copy_queue(d, sch->cpu_qstats, &sch->qstats, qlen); } -static inline void qdisc_qstats_qlen_backlog(struct Qdisc *sch, __u32 *qlen, - __u32 *backlog) +static inline void qdisc_qstats_qlen_backlog(const struct Qdisc *sch, + u32 *qlen, u32 *backlog) { struct gnet_stats_queue qstats = { 0 }; gnet_stats_add_queue(&qstats, sch->cpu_qstats, &sch->qstats); - *qlen = qstats.qlen + qdisc_qlen(sch); + *qlen = qstats.qlen + qdisc_qlen_lockless(sch); *backlog = qstats.backlog; } @@ -1063,7 +1102,7 @@ static inline void __qdisc_enqueue_tail(struct sk_buff *skb, qh->tail = skb; qh->head = skb; } - qh->qlen++; + WRITE_ONCE(qh->qlen, qh->qlen + 1); } static inline int qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch) @@ -1081,7 +1120,7 @@ static inline void __qdisc_enqueue_head(struct sk_buff *skb, if (!qh->head) qh->tail = skb; qh->head = skb; - qh->qlen++; + WRITE_ONCE(qh->qlen, qh->qlen + 1); } static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh) @@ -1090,7 +1129,7 @@ static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh) if (likely(skb != NULL)) { qh->head = skb->next; - qh->qlen--; + WRITE_ONCE(qh->qlen, qh->qlen - 1); if (qh->head == NULL) qh->tail = NULL; skb->next = NULL; @@ -1105,7 +1144,7 @@ static inline struct sk_buff *qdisc_dequeue_internal(struct Qdisc *sch, bool dir skb = __skb_dequeue(&sch->gso_skb); if (skb) { - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); return skb; } @@ -1261,7 +1300,7 @@ static inline struct sk_buff *qdisc_peek_dequeued(struct Qdisc *sch) __skb_queue_head(&sch->gso_skb, skb); /* it's still part of the queue */ qdisc_qstats_backlog_inc(sch, skb); - sch->q.qlen++; + qdisc_qlen_inc(sch); } } @@ -1278,7 +1317,7 @@ static inline void qdisc_update_stats_at_dequeue(struct Qdisc *sch, } else { qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } } @@ -1289,8 +1328,8 @@ static inline void qdisc_update_stats_at_enqueue(struct Qdisc *sch, qdisc_qstats_cpu_qlen_inc(sch); this_cpu_add(sch->cpu_qstats->backlog, pkt_len); } else { - sch->qstats.backlog += pkt_len; - sch->q.qlen++; + qstats_backlog_add(sch, pkt_len); + qdisc_qlen_inc(sch); } } @@ -1306,7 +1345,7 @@ static inline struct sk_buff *qdisc_dequeue_peeked(struct Qdisc *sch) qdisc_qstats_cpu_qlen_dec(sch); } else { qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } } else { skb = sch->dequeue(sch); @@ -1327,7 +1366,7 @@ static inline void __qdisc_reset_queue(struct qdisc_skb_head *qh) qh->head = NULL; qh->tail = NULL; - qh->qlen = 0; + WRITE_ONCE(qh->qlen, 0); } } diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 58242b37b47a..60b073fd3ed8 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -303,7 +303,7 @@ void sctp_dbg_objcnt_init(struct net *); static inline void sctp_dbg_objcnt_init(struct net *net) { return; } -#endif /* CONFIG_SCTP_DBG_OBJCOUNT */ +#endif /* CONFIG_SCTP_DBG_OBJCNT */ #if defined CONFIG_SYSCTL void sctp_sysctl_register(void); diff --git a/include/net/smc.h b/include/net/smc.h index bfdc4c41f019..a2bc3ab88075 100644 --- a/include/net/smc.h +++ b/include/net/smc.h @@ -40,7 +40,6 @@ struct smcd_dev { struct dibs_dev *dibs; struct list_head list; spinlock_t lock; - struct smc_connection **conn; struct list_head vlan; struct workqueue_struct *event_wq; u8 pnetid[SMC_MAX_PNETID_LEN]; @@ -50,6 +49,7 @@ struct smcd_dev { atomic_t lgr_cnt; wait_queue_head_t lgrs_deleted; u8 going_away : 1; + struct smc_connection *conn[]; }; #define SMC_HS_CTRL_NAME_MAX 16 diff --git a/include/net/sock.h b/include/net/sock.h index 95e157eee8d9..51185222aac2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1850,8 +1850,17 @@ static inline struct sock *sk_clone_lock(const struct sock *sk, const gfp_t prio struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, gfp_t priority); -void __sock_wfree(struct sk_buff *skb); void sock_wfree(struct sk_buff *skb); +void __sock_wfree(struct sk_buff *skb); +void tcp_wfree(struct sk_buff *skb); + +static inline bool is_skb_wmem(const struct sk_buff *skb) +{ + return skb->destructor == sock_wfree || + (IS_ENABLED(CONFIG_INET) && skb->destructor == __sock_wfree) || + (IS_ENABLED(CONFIG_INET) && skb->destructor == tcp_wfree); +} + struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size, gfp_t priority); void skb_orphan_partial(struct sk_buff *skb); @@ -2253,6 +2262,20 @@ sk_dst_reset(struct sock *sk) sk_dst_set(sk, NULL); } +/* Re-roll the socket txhash. On a rehash, IPv6 also drops the cached route + * so the next transmit re-selects an ECMP path; IPv4 keeps its route, since + * IPv4 ECMP path selection does not use sk_txhash. + */ +static inline bool __sk_rethink_txhash_reset_dst(struct sock *sk) +{ + if (sk_rethink_txhash(sk)) { + if (sk->sk_family == AF_INET6) + __sk_dst_reset(sk); + return true; + } + return false; +} + struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie); struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie); diff --git a/include/net/tcp.h b/include/net/tcp.h index 98848db62894..6d376ea4d1c0 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -388,7 +388,6 @@ static inline bool tcp_release_cb_cond(struct sock *sk) return false; } -void tcp_wfree(struct sk_buff *skb); void tcp_write_timer_handler(struct sock *sk); void tcp_delack_timer_handler(struct sock *sk); int tcp_ioctl(struct sock *sk, int cmd, int *karg); @@ -1013,6 +1012,16 @@ static inline u32 tcp_time_stamp_ts(const struct tcp_sock *tp) return tcp_time_stamp_ms(tp); } +/* Refresh clocks of a TCP socket, + * ensuring monotically increasing values. + */ +static inline void tcp_mstamp_refresh_inline(struct tcp_sock *tp) +{ + u64 val = tcp_clock_ns(); + + tp->tcp_clock_cache = val; + tp->tcp_mstamp = div_u64(val, NSEC_PER_USEC); +} void tcp_mstamp_refresh(struct tcp_sock *tp); static inline u32 tcp_stamp_us_delta(u64 t1, u64 t0) @@ -2018,40 +2027,6 @@ struct tcp6_pseudohdr { __be32 protocol; /* including padding */ }; -/* - * struct tcp_sigpool - per-CPU pool of ahash_requests - * @scratch: per-CPU temporary area, that can be used between - * tcp_sigpool_start() and tcp_sigpool_end() to perform - * crypto request - * @req: pre-allocated ahash request - */ -struct tcp_sigpool { - void *scratch; - struct ahash_request *req; -}; - -int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size); -void tcp_sigpool_get(unsigned int id); -void tcp_sigpool_release(unsigned int id); -int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp, - const struct sk_buff *skb, - unsigned int header_len); - -/** - * tcp_sigpool_start - disable bh and start using tcp_sigpool_ahash - * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() - * @c: returned tcp_sigpool for usage (uninitialized on failure) - * - * Returns: 0 on success, error otherwise. - */ -int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c); -/** - * tcp_sigpool_end - enable bh and stop using tcp_sigpool - * @c: tcp_sigpool context that was returned by tcp_sigpool_start() - */ -void tcp_sigpool_end(struct tcp_sigpool *c); -size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len); -/* - functions */ void tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, const struct sock *sk, const struct sk_buff *skb); int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, @@ -2511,9 +2486,9 @@ struct tcp_sock_af_ops { struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); - int (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key, - const struct sock *sk, - __be32 sisn, __be32 disn, bool send); + void (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send); int (*calc_ao_hash)(char *location, struct tcp_ao_key *ao, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne); @@ -2534,7 +2509,7 @@ struct tcp_request_sock_ops { struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, struct request_sock *req, int sndid, int rcvid); - int (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *sk); + void (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *sk); int (*ao_synack_hash)(char *ao_hash, struct tcp_ao_key *mkt, struct request_sock *req, const struct sk_buff *skb, int hash_offset, u32 sne); @@ -2565,22 +2540,30 @@ extern const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops; #ifdef CONFIG_SYN_COOKIES static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops, - const struct sock *sk, struct sk_buff *skb, - __u16 *mss) + struct sk_buff *skb, __u16 *mss) { - tcp_synq_overflow(sk); - __NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); return ops->cookie_init_seq(skb, mss); } #else static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops, - const struct sock *sk, struct sk_buff *skb, - __u16 *mss) + struct sk_buff *skb, __u16 *mss) { return 0; } #endif +#ifdef CONFIG_SYN_COOKIES +static inline void cookie_record_sent(const struct sock *sk) +{ + tcp_synq_overflow(sk); + __NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); +} +#else +static inline void cookie_record_sent(const struct sock *sk) +{ +} +#endif + struct tcp_key { union { struct { @@ -2942,6 +2925,11 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, return tcp_call_bpf(sk, op, 3, args); } +static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) +{ + tcp_sk(sk)->bpf_sock_ops_cb_flags = 0; +} + #else static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args) { @@ -2959,6 +2947,10 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2, return -EPERM; } +static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk) +{ +} + #endif static inline u32 tcp_timeout_init(struct sock *sk) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1e9e27d6e06b..29fd7b735afa 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -2,8 +2,7 @@ #ifndef _TCP_AO_H #define _TCP_AO_H -#define TCP_AO_KEY_ALIGN 1 -#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN) +#include /* for SHA256_DIGEST_SIZE */ union tcp_ao_addr { struct in_addr a4; @@ -32,11 +31,27 @@ struct tcp_ao_counters { atomic64_t dropped_icmp; }; +enum tcp_ao_algo_id { + TCP_AO_ALGO_HMAC_SHA1 = 1, /* specified by RFC 5926 */ + TCP_AO_ALGO_HMAC_SHA256, /* Linux extension */ + TCP_AO_ALGO_AES_128_CMAC, /* specified by RFC 5926 */ +}; + +/* + * This is the maximum untruncated MAC length, in bytes. Note that the MACs + * actually get truncated to 20 or fewer bytes to fit in the TCP options space. + */ +#define TCP_AO_MAX_MAC_LEN SHA256_DIGEST_SIZE + +#define TCP_AO_MAX_TRAFFIC_KEY_LEN SHA256_DIGEST_SIZE + +struct tcp_ao_mac_ctx; + struct tcp_ao_key { struct hlist_node node; union tcp_ao_addr addr; - u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align; - unsigned int tcp_sigpool_id; + u8 key[TCP_AO_MAXKEYLEN]; + enum tcp_ao_algo_id algo; unsigned int digest_size; int l3index; u8 prefixlen; @@ -168,14 +183,15 @@ struct tcp6_ao_context { __be32 disn; }; -struct tcp_sigpool; /* Established states are fast-path and there always is current_key/rnext_key */ #define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \ TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING) -int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, - struct tcp_ao_key *key, struct tcphdr *th, - __u8 *hash_location); +void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, + struct tcp_ao_key *key, struct tcphdr *th, + __u8 *hash_location); +void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data, + size_t data_len); int tcp_ao_hash_skb(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, @@ -188,8 +204,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, struct request_sock *req, struct sk_buff *skb, int family); -int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, - unsigned int len, struct tcp_sigpool *hp); +void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key, + const void *input, unsigned int input_len); void tcp_ao_destroy_sock(struct sock *sk, bool twsk); void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code); @@ -222,11 +238,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *mkt, struct request_sock *req, const struct sk_buff *skb, int hash_offset, u32 sne); -int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, - const struct sock *sk, - __be32 sisn, __be32 disn, bool send); -int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, - struct request_sock *req); +void tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send); +void tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req); struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, struct request_sock *req, int sndid, int rcvid); @@ -234,16 +250,17 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne); /* ipv6 specific functions */ -int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, - const struct in6_addr *daddr, - const struct in6_addr *saddr, int nbytes); -int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, - const struct sk_buff *skb, __be32 sisn, __be32 disn); -int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, - const struct sock *sk, __be32 sisn, - __be32 disn, bool send); -int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, - struct request_sock *req); +void tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx, + const struct in6_addr *daddr, + const struct in6_addr *saddr, int nbytes); +void tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, + __be32 disn); +void tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, __be32 sisn, + __be32 disn, bool send); +void tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req); struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, @@ -263,11 +280,10 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, struct request_sock *req, unsigned short int family); #else /* CONFIG_TCP_AO */ -static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, - struct tcp_ao_key *key, struct tcphdr *th, - __u8 *hash_location) +static inline void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, + struct tcp_ao_key *key, + struct tcphdr *th, __u8 *hash_location) { - return 0; } static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, diff --git a/include/net/tls.h b/include/net/tls.h index ebd2550280ae..e57bef58851e 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -85,7 +85,6 @@ enum { TLS_BASE, TLS_SW, TLS_HW, - TLS_HW_RECORD, TLS_NUM_CONFIG, }; @@ -111,11 +110,16 @@ struct tls_sw_context_tx { struct tls_strparser { struct sock *sk; + /* Bitfield word and msg_ready are serialized by the lower + * socket lock; BH and worker contexts both acquire it. + */ u32 mark : 8; u32 stopped : 1; u32 copy_mode : 1; u32 mixed_decrypted : 1; + u32 msg_announced : 1; + bool msg_ready; struct strp_msg stm; diff --git a/include/net/tls_toe.h b/include/net/tls_toe.h deleted file mode 100644 index b3aa7593ce2c..000000000000 --- a/include/net/tls_toe.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. - * Copyright (c) 2016-2017, Dave Watson . All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -struct sock; - -#define TLS_TOE_DEVICE_NAME_MAX 32 - -/* - * This structure defines the routines for Inline TLS driver. - * The following routines are optional and filled with a - * null pointer if not defined. - * - * @name: Its the name of registered Inline tls device - * @dev_list: Inline tls device list - * int (*feature)(struct tls_toe_device *device); - * Called to return Inline TLS driver capability - * - * int (*hash)(struct tls_toe_device *device, struct sock *sk); - * This function sets Inline driver for listen and program - * device specific functioanlity as required - * - * void (*unhash)(struct tls_toe_device *device, struct sock *sk); - * This function cleans listen state set by Inline TLS driver - * - * void (*release)(struct kref *kref); - * Release the registered device and allocated resources - * @kref: Number of reference to tls_toe_device - */ -struct tls_toe_device { - char name[TLS_TOE_DEVICE_NAME_MAX]; - struct list_head dev_list; - int (*feature)(struct tls_toe_device *device); - int (*hash)(struct tls_toe_device *device, struct sock *sk); - void (*unhash)(struct tls_toe_device *device, struct sock *sk); - void (*release)(struct kref *kref); - struct kref kref; -}; - -int tls_toe_bypass(struct sock *sk); -int tls_toe_hash(struct sock *sk); -void tls_toe_unhash(struct sock *sk); - -void tls_toe_register_device(struct tls_toe_device *device); -void tls_toe_unregister_device(struct tls_toe_device *device); diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index 47c23d4a1740..498b7b262fa9 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -94,7 +94,7 @@ struct udp_tunnel_sock_cfg { }; /* Setup the given (UDP) sock to receive UDP encapsulated packets */ -void setup_udp_tunnel_sock(struct net *net, struct socket *sock, +void setup_udp_tunnel_sock(struct net *net, struct sock *sk, struct udp_tunnel_sock_cfg *sock_cfg); /* -- List of parsable UDP tunnel types -- @@ -127,12 +127,12 @@ struct udp_tunnel_info { }; /* Notify network devices of offloadable types */ -void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, +void udp_tunnel_push_rx_port(struct net_device *dev, struct sock *sk, unsigned short type); -void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock, +void udp_tunnel_drop_rx_port(struct net_device *dev, struct sock *sk, unsigned short type); -void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type); -void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type); +void udp_tunnel_notify_add_rx_port(struct sock *sk, unsigned short type); +void udp_tunnel_notify_del_rx_port(struct sock *sk, unsigned short type); /* Transmit the skb using UDP encapsulation. */ void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, @@ -176,7 +176,7 @@ static inline void udp_tunnel_set_inner_protocol(struct sk_buff *skb, skb_set_inner_protocol(skb, inner_proto); } -void udp_tunnel_sock_release(struct socket *sock); +void udp_tunnel_sock_release(struct sock *sk); struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb, struct net_device *dev, @@ -188,7 +188,7 @@ struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb, struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb, struct net_device *dev, struct net *net, - struct socket *sock, int oif, + struct sock *sk, int oif, struct in6_addr *saddr, const struct ip_tunnel_key *key, __be16 sport, __be16 dport, u8 dsfield, diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 0ee50785f4f1..dfba89695efc 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -185,7 +185,8 @@ struct vxlan_metadata { /* per UDP socket information */ struct vxlan_sock { struct hlist_node hlist; - struct socket *sock; + struct sock *sk; + struct rcu_head rcu; struct hlist_head vni_list[VNI_HASH_SIZE]; refcount_t refcnt; u32 flags; @@ -448,7 +449,7 @@ static inline __be32 vxlan_compute_rco(unsigned int start, unsigned int offset) static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) { - return vs->sock->sk->sk_family; + return vs->sk->sk_family; } #if IS_ENABLED(CONFIG_IPV6) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 874409127e29..46c1e499e955 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -682,12 +682,22 @@ struct xfrm_migrate { xfrm_address_t old_saddr; xfrm_address_t new_daddr; xfrm_address_t new_saddr; + struct xfrm_encap_tmpl *encap; + struct xfrm_user_offload *xuo; + struct xfrm_mark old_mark; + const struct xfrm_mark *new_mark; + struct xfrm_mark smark; u8 proto; u8 mode; - u16 reserved; - u32 reqid; + u16 msg_type; /* XFRM_MSG_MIGRATE or XFRM_MSG_MIGRATE_STATE */ + u32 flags; + u32 old_reqid; + u32 new_reqid; + u32 nat_keepalive_interval; + u32 mapping_maxage; u16 old_family; u16 new_family; + const struct xfrm_selector *new_sel; }; #define XFRM_KM_TIMEOUT 30 @@ -1775,7 +1785,7 @@ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); int xfrm_init_replay(struct xfrm_state *x, struct netlink_ext_ack *extack); u32 xfrm_state_mtu(struct xfrm_state *x, int mtu); int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack); -int xfrm_init_state(struct xfrm_state *x); +int xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); int xfrm_input_resume(struct sk_buff *skb, int nexthdr); int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb, @@ -1896,11 +1906,17 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_encap_tmpl *encap); struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net, u32 if_id); +struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x, + const struct xfrm_migrate *m, + struct net *net, + struct netlink_ext_ack *extack); +int xfrm_state_migrate_install(const struct xfrm_state *x, + struct xfrm_state *xc, + const struct xfrm_migrate *m, + struct netlink_ext_ack *extack); struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, struct xfrm_migrate *m, - struct xfrm_encap_tmpl *encap, struct net *net, - struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack); int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, @@ -2014,23 +2030,51 @@ static inline unsigned int xfrm_replay_state_esn_len(struct xfrm_replay_state_es #ifdef CONFIG_XFRM_MIGRATE static inline int xfrm_replay_clone(struct xfrm_state *x, - struct xfrm_state *orig) + const struct xfrm_state *orig) { + /* Counters synced later in xfrm_replay_sync() */ - x->replay_esn = kmemdup(orig->replay_esn, + x->replay = orig->replay; + x->preplay = orig->preplay; + + if (orig->replay_esn) { + x->replay_esn = kmemdup(orig->replay_esn, xfrm_replay_state_esn_len(orig->replay_esn), GFP_KERNEL); - if (!x->replay_esn) - return -ENOMEM; - x->preplay_esn = kmemdup(orig->preplay_esn, - xfrm_replay_state_esn_len(orig->preplay_esn), - GFP_KERNEL); - if (!x->preplay_esn) - return -ENOMEM; + if (!x->replay_esn) + return -ENOMEM; + x->preplay_esn = kmemdup(orig->preplay_esn, + xfrm_replay_state_esn_len(orig->preplay_esn), + GFP_KERNEL); + if (!x->preplay_esn) + return -ENOMEM; + } return 0; } +static inline void xfrm_replay_sync(struct xfrm_state *x, const struct xfrm_state *orig) +{ + x->replay = orig->replay; + x->preplay = orig->preplay; + + if (orig->replay_esn) { + memcpy(x->replay_esn, orig->replay_esn, + xfrm_replay_state_esn_len(orig->replay_esn)); + + memcpy(x->preplay_esn, orig->preplay_esn, + xfrm_replay_state_esn_len(orig->preplay_esn)); + } +} + +static inline void xfrm_migrate_sync(struct xfrm_state *x, + const struct xfrm_state *orig) +{ + /* called under lock so no race conditions or mallocs allowed */ + memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft)); + xfrm_replay_sync(x, orig); +} + static inline struct xfrm_algo_aead *xfrm_algo_aead_clone(struct xfrm_algo_aead *orig) { return kmemdup(orig, aead_len(orig), GFP_KERNEL); @@ -2069,7 +2113,7 @@ void xfrm_dev_resume(struct sk_buff *skb); void xfrm_dev_backlog(struct softnet_data *sd); struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again); int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, - struct xfrm_user_offload *xuo, + const struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack); int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp, struct xfrm_user_offload *xuo, u8 dir, @@ -2140,7 +2184,9 @@ static inline struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_fea return skb; } -static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack) +static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, + const struct xfrm_user_offload *xuo, + struct netlink_ext_ack *extack) { return 0; } diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 48d6deb3efd7..05a8191b148e 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -650,7 +650,7 @@ struct device_node; struct ocelot_ops { struct net_device *(*port_to_netdev)(struct ocelot *ocelot, int port); - int (*netdev_to_port)(struct net_device *dev); + int (*netdev_to_port)(struct ocelot *ocelot, struct net_device *dev); int (*reset)(struct ocelot *ocelot); u16 (*wm_enc)(u16 value); u16 (*wm_dec)(u16 value); diff --git a/include/trace/events/skb.h b/include/trace/events/skb.h index b877133cd93a..2945aa7fe9a7 100644 --- a/include/trace/events/skb.h +++ b/include/trace/events/skb.h @@ -24,14 +24,14 @@ DEFINE_DROP_REASON(FN, FN) TRACE_EVENT(kfree_skb, TP_PROTO(struct sk_buff *skb, void *location, - enum skb_drop_reason reason, struct sock *rx_sk), + enum skb_drop_reason reason, const struct sock *rx_sk), TP_ARGS(skb, location, reason, rx_sk), TP_STRUCT__entry( __field(void *, skbaddr) __field(void *, location) - __field(void *, rx_sk) + __field(const void *, rx_sk) __field(unsigned short, protocol) __field(enum skb_drop_reason, reason) ), diff --git a/include/uapi/linux/atm_eni.h b/include/uapi/linux/atm_eni.h deleted file mode 100644 index cf5bfd1a2691..000000000000 --- a/include/uapi/linux/atm_eni.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atm_eni.h - Driver-specific declarations of the ENI driver (for use by - driver-specific utilities) */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef LINUX_ATM_ENI_H -#define LINUX_ATM_ENI_H - -#include - - -struct eni_multipliers { - int tx,rx; /* values are in percent and must be > 100 */ -}; - - -#define ENI_MEMDUMP _IOW('a',ATMIOC_SARPRV,struct atmif_sioc) - /* printk memory map */ -#define ENI_SETMULT _IOW('a',ATMIOC_SARPRV+7,struct atmif_sioc) - /* set buffer multipliers */ - -#endif diff --git a/include/uapi/linux/atm_he.h b/include/uapi/linux/atm_he.h deleted file mode 100644 index 9f4b43293988..000000000000 --- a/include/uapi/linux/atm_he.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atm_he.h */ - -#ifndef LINUX_ATM_HE_H -#define LINUX_ATM_HE_H - -#include - -#define HE_GET_REG _IOW('a', ATMIOC_SARPRV, struct atmif_sioc) - -#define HE_REGTYPE_PCI 1 -#define HE_REGTYPE_RCM 2 -#define HE_REGTYPE_TCM 3 -#define HE_REGTYPE_MBOX 4 - -struct he_ioctl_reg { - unsigned addr, val; - char type; -}; - -#endif /* LINUX_ATM_HE_H */ diff --git a/include/uapi/linux/atm_idt77105.h b/include/uapi/linux/atm_idt77105.h deleted file mode 100644 index f0fd6912a14b..000000000000 --- a/include/uapi/linux/atm_idt77105.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atm_idt77105.h - Driver-specific declarations of the IDT77105 driver (for - * use by driver-specific utilities) */ - -/* Written 1999 by Greg Banks . Copied from atm_suni.h. */ - - -#ifndef LINUX_ATM_IDT77105_H -#define LINUX_ATM_IDT77105_H - -#include -#include -#include - -/* - * Structure for IDT77105_GETSTAT and IDT77105_GETSTATZ ioctls. - * Pointed to by `arg' in atmif_sioc. - */ -struct idt77105_stats { - __u32 symbol_errors; /* wire symbol errors */ - __u32 tx_cells; /* cells transmitted */ - __u32 rx_cells; /* cells received */ - __u32 rx_hec_errors; /* Header Error Check errors on receive */ -}; - -#define IDT77105_GETSTAT _IOW('a',ATMIOC_PHYPRV+2,struct atmif_sioc) /* get stats */ -#define IDT77105_GETSTATZ _IOW('a',ATMIOC_PHYPRV+3,struct atmif_sioc) /* get stats and zero */ - -#endif diff --git a/include/uapi/linux/atm_nicstar.h b/include/uapi/linux/atm_nicstar.h deleted file mode 100644 index 880d368b5b9a..000000000000 --- a/include/uapi/linux/atm_nicstar.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/****************************************************************************** - * - * atm_nicstar.h - * - * Driver-specific declarations for use by NICSTAR driver specific utils. - * - * Author: Rui Prior - * - * (C) INESC 1998 - * - ******************************************************************************/ - - -#ifndef LINUX_ATM_NICSTAR_H -#define LINUX_ATM_NICSTAR_H - -/* Note: non-kernel programs including this file must also include - * sys/types.h for struct timeval - */ - -#include -#include - -#define NS_GETPSTAT _IOWR('a',ATMIOC_SARPRV+1,struct atmif_sioc) - /* get pool statistics */ -#define NS_SETBUFLEV _IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc) - /* set buffer level markers */ -#define NS_ADJBUFLEV _IO('a',ATMIOC_SARPRV+3) - /* adjust buffer level */ - -typedef struct buf_nr -{ - unsigned min; - unsigned init; - unsigned max; -}buf_nr; - - -typedef struct pool_levels -{ - int buftype; - int count; /* (At least for now) only used in NS_GETPSTAT */ - buf_nr level; -} pool_levels; - -/* type must be one of the following: */ -#define NS_BUFTYPE_SMALL 1 -#define NS_BUFTYPE_LARGE 2 -#define NS_BUFTYPE_HUGE 3 -#define NS_BUFTYPE_IOVEC 4 - - -#endif /* LINUX_ATM_NICSTAR_H */ diff --git a/include/uapi/linux/atm_tcp.h b/include/uapi/linux/atm_tcp.h deleted file mode 100644 index 7309e1bc8867..000000000000 --- a/include/uapi/linux/atm_tcp.h +++ /dev/null @@ -1,62 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atm_tcp.h - Driver-specific declarations of the ATMTCP driver (for use by - driver-specific utilities) */ - -/* Written 1997-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef _UAPILINUX_ATM_TCP_H -#define _UAPILINUX_ATM_TCP_H - -#include -#include -#include -#include - - -/* - * All values in struct atmtcp_hdr are in network byte order - */ - -struct atmtcp_hdr { - __u16 vpi; - __u16 vci; - __u32 length; /* ... of data part */ -}; - -/* - * All values in struct atmtcp_command are in host byte order - */ - -#define ATMTCP_HDR_MAGIC (~0) /* this length indicates a command */ -#define ATMTCP_CTRL_OPEN 1 /* request/reply */ -#define ATMTCP_CTRL_CLOSE 2 /* request/reply */ - -struct atmtcp_control { - struct atmtcp_hdr hdr; /* must be first */ - int type; /* message type; both directions */ - atm_kptr_t vcc; /* both directions */ - struct sockaddr_atmpvc addr; /* suggested value from kernel */ - struct atm_qos qos; /* both directions */ - int result; /* to kernel only */ -} __ATM_API_ALIGN; - -/* - * Field usage: - * Messge type dir. hdr.v?i type addr qos vcc result - * ----------- ---- ------- ---- ---- --- --- ------ - * OPEN K->D Y Y Y Y Y 0 - * OPEN D->K - Y Y Y Y Y - * CLOSE K->D - - Y - Y 0 - * CLOSE D->K - - - - Y Y - */ - -#define SIOCSIFATMTCP _IO('a',ATMIOC_ITF) /* set ATMTCP mode */ -#define ATMTCP_CREATE _IO('a',ATMIOC_ITF+14) /* create persistent ATMTCP - interface */ -#define ATMTCP_REMOVE _IO('a',ATMIOC_ITF+15) /* destroy persistent ATMTCP - interface */ - - - -#endif /* _UAPILINUX_ATM_TCP_H */ diff --git a/include/uapi/linux/atm_zatm.h b/include/uapi/linux/atm_zatm.h deleted file mode 100644 index 5135027b93c1..000000000000 --- a/include/uapi/linux/atm_zatm.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atm_zatm.h - Driver-specific declarations of the ZATM driver (for use by - driver-specific utilities) */ - -/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef LINUX_ATM_ZATM_H -#define LINUX_ATM_ZATM_H - -/* - * Note: non-kernel programs including this file must also include - * sys/types.h for struct timeval - */ - -#include -#include - -#define ZATM_GETPOOL _IOW('a',ATMIOC_SARPRV+1,struct atmif_sioc) - /* get pool statistics */ -#define ZATM_GETPOOLZ _IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc) - /* get statistics and zero */ -#define ZATM_SETPOOL _IOW('a',ATMIOC_SARPRV+3,struct atmif_sioc) - /* set pool parameters */ - -struct zatm_pool_info { - int ref_count; /* free buffer pool usage counters */ - int low_water,high_water; /* refill parameters */ - int rqa_count,rqu_count; /* queue condition counters */ - int offset,next_off; /* alignment optimizations: offset */ - int next_cnt,next_thres; /* repetition counter and threshold */ -}; - -struct zatm_pool_req { - int pool_num; /* pool number */ - struct zatm_pool_info info; /* actual information */ -}; - -#define ZATM_OAM_POOL 0 /* free buffer pool for OAM cells */ -#define ZATM_AAL0_POOL 1 /* free buffer pool for AAL0 cells */ -#define ZATM_AAL5_POOL_BASE 2 /* first AAL5 free buffer pool */ -#define ZATM_LAST_POOL ZATM_AAL5_POOL_BASE+10 /* max. 64 kB */ - -#define ZATM_TIMER_HISTORY_SIZE 16 /* number of timer adjustments to - record; must be 2^n */ - -#endif diff --git a/include/uapi/linux/atmarp.h b/include/uapi/linux/atmarp.h deleted file mode 100644 index 8e44d121fde1..000000000000 --- a/include/uapi/linux/atmarp.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */ - -/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef _LINUX_ATMARP_H -#define _LINUX_ATMARP_H - -#include -#include -#include - - -#define ATMARP_RETRY_DELAY 30 /* request next resolution or forget - NAK after 30 sec - should go into - atmclip.h */ -#define ATMARP_MAX_UNRES_PACKETS 5 /* queue that many packets while - waiting for the resolver */ - - -#define ATMARPD_CTRL _IO('a',ATMIOC_CLIP+1) /* become atmarpd ctrl sock */ -#define ATMARP_MKIP _IO('a',ATMIOC_CLIP+2) /* attach socket to IP */ -#define ATMARP_SETENTRY _IO('a',ATMIOC_CLIP+3) /* fill or hide ARP entry */ -#define ATMARP_ENCAP _IO('a',ATMIOC_CLIP+5) /* change encapsulation */ - - -enum atmarp_ctrl_type { - act_invalid, /* catch uninitialized structures */ - act_need, /* need address resolution */ - act_up, /* interface is coming up */ - act_down, /* interface is going down */ - act_change /* interface configuration has changed */ -}; - -struct atmarp_ctrl { - enum atmarp_ctrl_type type; /* message type */ - int itf_num;/* interface number (if present) */ - __be32 ip; /* IP address (act_need only) */ -}; - -#endif diff --git a/include/uapi/linux/atmclip.h b/include/uapi/linux/atmclip.h deleted file mode 100644 index c818bb82b4e6..000000000000 --- a/include/uapi/linux/atmclip.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atmclip.h - Classical IP over ATM */ - -/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef LINUX_ATMCLIP_H -#define LINUX_ATMCLIP_H - -#include -#include - - -#define RFC1483LLC_LEN 8 /* LLC+OUI+PID = 8 */ -#define RFC1626_MTU 9180 /* RFC1626 default MTU */ - -#define CLIP_DEFAULT_IDLETIMER 1200 /* 20 minutes, see RFC1755 */ -#define CLIP_CHECK_INTERVAL 10 /* check every ten seconds */ - -#define SIOCMKCLIP _IO('a',ATMIOC_CLIP) /* create IP interface */ - -#endif diff --git a/include/uapi/linux/atmdev.h b/include/uapi/linux/atmdev.h index 20b0215084fc..a94e7b8360ee 100644 --- a/include/uapi/linux/atmdev.h +++ b/include/uapi/linux/atmdev.h @@ -60,14 +60,6 @@ struct atm_dev_stats { /* get interface type name */ #define ATM_GETESI _IOW('a',ATMIOC_ITF+5,struct atmif_sioc) /* get interface ESI */ -#define ATM_GETADDR _IOW('a',ATMIOC_ITF+6,struct atmif_sioc) - /* get itf's local ATM addr. list */ -#define ATM_RSTADDR _IOW('a',ATMIOC_ITF+7,struct atmif_sioc) - /* reset itf's ATM address list */ -#define ATM_ADDADDR _IOW('a',ATMIOC_ITF+8,struct atmif_sioc) - /* add a local ATM address */ -#define ATM_DELADDR _IOW('a',ATMIOC_ITF+9,struct atmif_sioc) - /* remove a local ATM address */ #define ATM_GETCIRANGE _IOW('a',ATMIOC_ITF+10,struct atmif_sioc) /* get connection identifier range */ #define ATM_SETCIRANGE _IOW('a',ATMIOC_ITF+11,struct atmif_sioc) @@ -76,12 +68,6 @@ struct atm_dev_stats { /* set interface ESI */ #define ATM_SETESIF _IOW('a',ATMIOC_ITF+13,struct atmif_sioc) /* force interface ESI */ -#define ATM_ADDLECSADDR _IOW('a', ATMIOC_ITF+14, struct atmif_sioc) - /* register a LECS address */ -#define ATM_DELLECSADDR _IOW('a', ATMIOC_ITF+15, struct atmif_sioc) - /* unregister a LECS address */ -#define ATM_GETLECSADDR _IOW('a', ATMIOC_ITF+16, struct atmif_sioc) - /* retrieve LECS address(es) */ #define ATM_GETSTAT _IOW('a',ATMIOC_SARCOM+0,struct atmif_sioc) /* get AAL layer statistics */ @@ -99,10 +85,6 @@ struct atm_dev_stats { /* set backend handler */ #define ATM_NEWBACKENDIF _IOW('a',ATMIOC_SPECIAL+3,atm_backend_t) /* use backend to make new if */ -#define ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf) - /* add party to p2mp call */ -#define ATM_DROPPARTY _IOW('a', ATMIOC_SPECIAL+5,int) - /* drop party from p2mp call */ /* * These are backend handkers that can be set via the ATM_SETBACKEND call diff --git a/include/uapi/linux/atmlec.h b/include/uapi/linux/atmlec.h deleted file mode 100644 index c68346bb40e6..000000000000 --- a/include/uapi/linux/atmlec.h +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * ATM Lan Emulation Daemon driver interface - * - * Marko Kiiskila - */ - -#ifndef _ATMLEC_H_ -#define _ATMLEC_H_ - -#include -#include -#include -#include -#include - -/* ATM lec daemon control socket */ -#define ATMLEC_CTRL _IO('a', ATMIOC_LANE) -#define ATMLEC_DATA _IO('a', ATMIOC_LANE+1) -#define ATMLEC_MCAST _IO('a', ATMIOC_LANE+2) - -/* Maximum number of LEC interfaces (tweakable) */ -#define MAX_LEC_ITF 48 - -typedef enum { - l_set_mac_addr, - l_del_mac_addr, - l_svc_setup, - l_addr_delete, - l_topology_change, - l_flush_complete, - l_arp_update, - l_narp_req, /* LANE2 mandates the use of this */ - l_config, - l_flush_tran_id, - l_set_lecid, - l_arp_xmt, - l_rdesc_arp_xmt, - l_associate_req, - l_should_bridge /* should we bridge this MAC? */ -} atmlec_msg_type; - -#define ATMLEC_MSG_TYPE_MAX l_should_bridge - -struct atmlec_config_msg { - unsigned int maximum_unknown_frame_count; - unsigned int max_unknown_frame_time; - unsigned short max_retry_count; - unsigned int aging_time; - unsigned int forward_delay_time; - unsigned int arp_response_time; - unsigned int flush_timeout; - unsigned int path_switching_delay; - unsigned int lane_version; /* LANE2: 1 for LANEv1, 2 for LANEv2 */ - int mtu; - int is_proxy; -}; - -struct atmlec_msg { - atmlec_msg_type type; - int sizeoftlvs; /* LANE2: if != 0, tlvs follow */ - union { - struct { - unsigned char mac_addr[ETH_ALEN]; - unsigned char atm_addr[ATM_ESA_LEN]; - unsigned int flag; /* - * Topology_change flag, - * remoteflag, permanent flag, - * lecid, transaction id - */ - unsigned int targetless_le_arp; /* LANE2 */ - unsigned int no_source_le_narp; /* LANE2 */ - } normal; - struct atmlec_config_msg config; - struct { - __u16 lec_id; /* requestor lec_id */ - __u32 tran_id; /* transaction id */ - unsigned char mac_addr[ETH_ALEN]; /* dst mac addr */ - unsigned char atm_addr[ATM_ESA_LEN]; /* reqestor ATM addr */ - } proxy; /* - * For mapping LE_ARP requests to responses. Filled by - * zeppelin, returned by kernel. Used only when proxying - */ - } content; -} __ATM_API_ALIGN; - -struct atmlec_ioc { - int dev_num; - unsigned char atm_addr[ATM_ESA_LEN]; - unsigned char receive; /* 1= receive vcc, 0 = send vcc */ -}; -#endif /* _ATMLEC_H_ */ diff --git a/include/uapi/linux/atmmpc.h b/include/uapi/linux/atmmpc.h deleted file mode 100644 index cc17f4304839..000000000000 --- a/include/uapi/linux/atmmpc.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef _ATMMPC_H_ -#define _ATMMPC_H_ - -#include -#include -#include -#include - -#define ATMMPC_CTRL _IO('a', ATMIOC_MPOA) -#define ATMMPC_DATA _IO('a', ATMIOC_MPOA+1) - -#define MPC_SOCKET_INGRESS 1 -#define MPC_SOCKET_EGRESS 2 - -struct atmmpc_ioc { - int dev_num; - __be32 ipaddr; /* the IP address of the shortcut */ - int type; /* ingress or egress */ -}; - -typedef struct in_ctrl_info { - __u8 Last_NHRP_CIE_code; - __u8 Last_Q2931_cause_value; - __u8 eg_MPC_ATM_addr[ATM_ESA_LEN]; - __be32 tag; - __be32 in_dst_ip; /* IP address this ingress MPC sends packets to */ - __u16 holding_time; - __u32 request_id; -} in_ctrl_info; - -typedef struct eg_ctrl_info { - __u8 DLL_header[256]; - __u8 DH_length; - __be32 cache_id; - __be32 tag; - __be32 mps_ip; - __be32 eg_dst_ip; /* IP address to which ingress MPC sends packets */ - __u8 in_MPC_data_ATM_addr[ATM_ESA_LEN]; - __u16 holding_time; -} eg_ctrl_info; - -struct mpc_parameters { - __u16 mpc_p1; /* Shortcut-Setup Frame Count */ - __u16 mpc_p2; /* Shortcut-Setup Frame Time */ - __u8 mpc_p3[8]; /* Flow-detection Protocols */ - __u16 mpc_p4; /* MPC Initial Retry Time */ - __u16 mpc_p5; /* MPC Retry Time Maximum */ - __u16 mpc_p6; /* Hold Down Time */ -} ; - -struct k_message { - __u16 type; - __be32 ip_mask; - __u8 MPS_ctrl[ATM_ESA_LEN]; - union { - in_ctrl_info in_info; - eg_ctrl_info eg_info; - struct mpc_parameters params; - } content; - struct atm_qos qos; -} __ATM_API_ALIGN; - -struct llc_snap_hdr { - /* RFC 1483 LLC/SNAP encapsulation for routed IP PDUs */ - __u8 dsap; /* Destination Service Access Point (0xAA) */ - __u8 ssap; /* Source Service Access Point (0xAA) */ - __u8 ui; /* Unnumbered Information (0x03) */ - __u8 org[3]; /* Organizational identification (0x000000) */ - __u8 type[2]; /* Ether type (for IP) (0x0800) */ -}; - -/* TLVs this MPC recognizes */ -#define TLV_MPOA_DEVICE_TYPE 0x00a03e2a - -/* MPOA device types in MPOA Device Type TLV */ -#define NON_MPOA 0 -#define MPS 1 -#define MPC 2 -#define MPS_AND_MPC 3 - - -/* MPC parameter defaults */ - -#define MPC_P1 10 /* Shortcut-Setup Frame Count */ -#define MPC_P2 1 /* Shortcut-Setup Frame Time */ -#define MPC_P3 0 /* Flow-detection Protocols */ -#define MPC_P4 5 /* MPC Initial Retry Time */ -#define MPC_P5 40 /* MPC Retry Time Maximum */ -#define MPC_P6 160 /* Hold Down Time */ -#define HOLDING_TIME_DEFAULT 1200 /* same as MPS-p7 */ - -/* MPC constants */ - -#define MPC_C1 2 /* Retry Time Multiplier */ -#define MPC_C2 60 /* Initial Keep-Alive Lifetime */ - -/* Message types - to MPOA daemon */ - -#define SND_MPOA_RES_RQST 201 -#define SET_MPS_CTRL_ADDR 202 -#define SND_MPOA_RES_RTRY 203 /* Different type in a retry due to req id */ -#define STOP_KEEP_ALIVE_SM 204 -#define EGRESS_ENTRY_REMOVED 205 -#define SND_EGRESS_PURGE 206 -#define DIE 207 /* tell the daemon to exit() */ -#define DATA_PLANE_PURGE 208 /* Data plane purge because of egress cache hit miss or dead MPS */ -#define OPEN_INGRESS_SVC 209 - -/* Message types - from MPOA daemon */ - -#define MPOA_TRIGGER_RCVD 101 -#define MPOA_RES_REPLY_RCVD 102 -#define INGRESS_PURGE_RCVD 103 -#define EGRESS_PURGE_RCVD 104 -#define MPS_DEATH 105 -#define CACHE_IMPOS_RCVD 106 -#define SET_MPC_CTRL_ADDR 107 /* Our MPC's control ATM address */ -#define SET_MPS_MAC_ADDR 108 -#define CLEAN_UP_AND_EXIT 109 -#define SET_MPC_PARAMS 110 /* MPC configuration parameters */ - -/* Message types - bidirectional */ - -#define RELOAD 301 /* kill -HUP the daemon for reload */ - -#endif /* _ATMMPC_H_ */ diff --git a/include/uapi/linux/atmsvc.h b/include/uapi/linux/atmsvc.h deleted file mode 100644 index 137b5f853449..000000000000 --- a/include/uapi/linux/atmsvc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* atmsvc.h - ATM signaling kernel-demon interface definitions */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef _LINUX_ATMSVC_H -#define _LINUX_ATMSVC_H - -#include -#include -#include - - -#define ATMSIGD_CTRL _IO('a',ATMIOC_SPECIAL) - /* become ATM signaling demon control socket */ - -enum atmsvc_msg_type { as_catch_null, as_bind, as_connect, as_accept, as_reject, - as_listen, as_okay, as_error, as_indicate, as_close, - as_itf_notify, as_modify, as_identify, as_terminate, - as_addparty, as_dropparty }; - -struct atmsvc_msg { - enum atmsvc_msg_type type; - atm_kptr_t vcc; - atm_kptr_t listen_vcc; /* indicate */ - int reply; /* for okay and close: */ - /* < 0: error before active */ - /* (sigd has discarded ctx) */ - /* ==0: success */ - /* > 0: error when active (still */ - /* need to close) */ - struct sockaddr_atmpvc pvc; /* indicate, okay (connect) */ - struct sockaddr_atmsvc local; /* local SVC address */ - struct atm_qos qos; /* QOS parameters */ - struct atm_sap sap; /* SAP */ - unsigned int session; /* for p2pm */ - struct sockaddr_atmsvc svc; /* SVC address */ -} __ATM_API_ALIGN; - -/* - * Message contents: see ftp://icaftp.epfl.ch/pub/linux/atm/docs/isp-*.tar.gz - */ - -/* - * Some policy stuff for atmsigd and for net/atm/svc.c. Both have to agree on - * what PCR is used to request bandwidth from the device driver. net/atm/svc.c - * tries to do better than that, but only if there's no routing decision (i.e. - * if signaling only uses one ATM interface). - */ - -#define SELECT_TOP_PCR(tp) ((tp).pcr ? (tp).pcr : \ - (tp).max_pcr && (tp).max_pcr != ATM_MAX_PCR ? (tp).max_pcr : \ - (tp).min_pcr ? (tp).min_pcr : ATM_MAX_PCR) - -#endif diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index 439132a819ea..1241285b866c 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -518,16 +518,16 @@ struct batadv_mcast_packet { * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the general header * @ttl: time to live for this packet, part of the general header + * @first_ttvn: tt-version number of first included packet * @first_source: original source of first included packet * @first_orig_dest: original destination of first included packet * @first_crc: checksum of first included packet - * @first_ttvn: tt-version number of first included packet * @second_ttl: ttl of second packet + * @second_ttvn: tt version number of second included packet * @second_dest: second receiver of this coded packet * @second_source: original source of second included packet * @second_orig_dest: original destination of second included packet * @second_crc: checksum of second included packet - * @second_ttvn: tt version number of second included packet * @coded_len: length of network coded part of the payload */ struct batadv_coded_packet { @@ -554,8 +554,8 @@ struct batadv_coded_packet { * @version: batman-adv protocol version, part of the general header * @ttl: time to live for this packet, part of the general header * @reserved: reserved field (for packet alignment) - * @src: address of the source * @dst: address of the destination + * @src: address of the source * @tvlv_len: length of tvlv data following the unicast tvlv header * @align: 2 bytes to align the header to a 4 byte boundary */ diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 0b165eac7619..ca713bcc47b9 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -406,6 +406,7 @@ enum devlink_var_attr_type { DEVLINK_VAR_ATTR_TYPE_BINARY, __DEVLINK_VAR_ATTR_TYPE_CUSTOM_BASE = 0x80, /* Any possible custom types, unrelated to NLA_* values go below */ + DEVLINK_VAR_ATTR_TYPE_U64_ARRAY, }; enum devlink_attr { diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index 871685f7c353..55eaa82f5f98 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -109,10 +109,12 @@ enum dpll_clock_quality_level { * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock + * @DPLL_TYPE_GENERIC: generic dpll type for devices outside PPS/EEC classes */ enum dpll_type { DPLL_TYPE_PPS = 1, DPLL_TYPE_EEC, + DPLL_TYPE_GENERIC, /* private: */ __DPLL_TYPE_MAX, @@ -178,6 +180,28 @@ enum dpll_pin_state { DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1) }; +/** + * enum dpll_pin_operstate - defines possible operational states of a pin with + * respect to its parent DPLL device, valid values for DPLL_A_PIN_OPERSTATE + * attribute + * @DPLL_PIN_OPERSTATE_ACTIVE: pin is qualified and actively used by the DPLL + * @DPLL_PIN_OPERSTATE_STANDBY: pin is qualified but not actively used by the + * DPLL + * @DPLL_PIN_OPERSTATE_NO_SIGNAL: pin does not have a valid signal + * @DPLL_PIN_OPERSTATE_QUAL_FAILED: pin signal failed qualification (e.g. + * frequency or phase monitor) + */ +enum dpll_pin_operstate { + DPLL_PIN_OPERSTATE_ACTIVE = 1, + DPLL_PIN_OPERSTATE_STANDBY, + DPLL_PIN_OPERSTATE_NO_SIGNAL, + DPLL_PIN_OPERSTATE_QUAL_FAILED, + + /* private: */ + __DPLL_PIN_OPERSTATE_MAX, + DPLL_PIN_OPERSTATE_MAX = (__DPLL_PIN_OPERSTATE_MAX - 1) +}; + /** * enum dpll_pin_capabilities - defines possible capabilities of a pin, valid * flags on DPLL_A_PIN_CAPABILITIES attribute @@ -257,6 +281,7 @@ enum dpll_a_pin { DPLL_A_PIN_PHASE_ADJUST_GRAN, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT, DPLL_A_PIN_MEASURED_FREQUENCY, + DPLL_A_PIN_OPERSTATE, __DPLL_A_PIN_MAX, DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1) diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 1cdfb8341df2..a2091d4e00f3 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -291,6 +291,9 @@ enum phy_tunable_id { ETHTOOL_PHY_DOWNSHIFT, ETHTOOL_PHY_FAST_LINK_DOWN, ETHTOOL_PHY_EDPD, + ETHTOOL_PHY_SHORT_CABLE_PRESET, + ETHTOOL_PHY_LPF_BW, + ETHTOOL_PHY_DSP_EQ_INIT_VALUE, /* * Add your fresh new phy tunable attribute above and remember to update * phy_tunable_strings[] in net/ethtool/common.c diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index e52f8207ab27..21a700c02ef7 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -526,6 +526,7 @@ enum { BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS, BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS, BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS, + BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT, __BRIDGE_VLANDB_ENTRY_MAX, }; #define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index df9d44a11540..1ffac52c39df 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -82,6 +82,7 @@ #define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ #define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ #define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ +#define ETH_P_8021AC 0x8870 /* 802.1AC LLC > 1500 bytes */ #define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport * over Ethernet */ @@ -123,6 +124,7 @@ #define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_NXP_NETC 0xFD3A /* NXP NETC DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value * then the frame is Ethernet II. Else it is 802.3 */ @@ -163,6 +165,10 @@ #define ETH_P_MCTP 0x00FA /* Management component transport * protocol packets */ +#define ETH_P_GRE_OSI 0x00FE /* GRE tunnels: LLC "fe fe 03" analog, + * used primarily for IS-IS over GRE + * WARNING: not internal, used on wire! + */ /* * This is an Ethernet frame header. diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 79ce4bc24cba..43cecca49f01 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -1085,6 +1085,22 @@ enum { * Note that this option only takes effect when *IFLA_BRPORT_NEIGH_SUPPRESS* * is enabled for a given port. * + * @IFLA_BRPORT_NEIGH_FORWARD_GRAT + * Controls whether gratuitous ARP packets and unsolicited Neighbor + * Advertisement packets are forwarded on a given port even when neighbor + * suppression is enabled. + * By default this flag is off, meaning gratuitous ARP and unsolicited NA + * packets will be suppressed when neighbor suppression is enabled. + * Setting this flag to on allows these packets to be forwarded even + * when *IFLA_BRPORT_NEIGH_SUPPRESS* or *IFLA_BRPORT_NEIGH_VLAN_SUPPRESS* + * is enabled. + * + * Note that this option only takes effect when *IFLA_BRPORT_NEIGH_SUPPRESS* + * or *IFLA_BRPORT_NEIGH_VLAN_SUPPRESS* is enabled for a given port. + * When *IFLA_BRPORT_NEIGH_VLAN_SUPPRESS* is set, this port-level flag is + * ignored and per-VLAN control is available via + * *BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT*. + * * @IFLA_BRPORT_BACKUP_NHID * The FDB nexthop object ID to attach to packets being redirected to a * backup port that has VLAN tunnel mapping enabled (via the @@ -1137,6 +1153,7 @@ enum { IFLA_BRPORT_MCAST_MAX_GROUPS, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, IFLA_BRPORT_BACKUP_NHID, + IFLA_BRPORT_NEIGH_FORWARD_GRAT, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) @@ -1489,6 +1506,8 @@ enum { IFLA_GENEVE_INNER_PROTO_INHERIT, IFLA_GENEVE_PORT_RANGE, IFLA_GENEVE_GRO_HINT, + IFLA_GENEVE_LOCAL, + IFLA_GENEVE_LOCAL6, __IFLA_GENEVE_MAX }; #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) @@ -1584,6 +1603,7 @@ enum { IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, IFLA_BOND_BROADCAST_NEIGH, + IFLA_BOND_LACP_STRICT, __IFLA_BOND_MAX, }; diff --git a/include/uapi/linux/llc.h b/include/uapi/linux/llc.h index cf8806b14d5f..2ffd81f9cc01 100644 --- a/include/uapi/linux/llc.h +++ b/include/uapi/linux/llc.h @@ -3,13 +3,6 @@ * IEEE 802.2 User Interface SAPs for Linux, data structures and indicators. * * Copyright (c) 2001 by Jay Schulist - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #ifndef _UAPI__LINUX_LLC_H #define _UAPI__LINUX_LLC_H diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h index 8d769f100de6..b2541c948fc1 100644 --- a/include/uapi/linux/mdio.h +++ b/include/uapi/linux/mdio.h @@ -23,6 +23,10 @@ #define MDIO_MMD_DTEXS 5 /* DTE Extender Sublayer */ #define MDIO_MMD_TC 6 /* Transmission Convergence */ #define MDIO_MMD_AN 7 /* Auto-Negotiation */ +#define MDIO_MMD_SEP_PMA1 8 /* Separated PMA (1) */ +#define MDIO_MMD_SEP_PMA2 9 /* Separated PMA (2) */ +#define MDIO_MMD_SEP_PMA3 10 /* Separated PMA (3) */ +#define MDIO_MMD_SEP_PMA4 11 /* Separated PMA (4) */ #define MDIO_MMD_POWER_UNIT 13 /* PHY Power Unit */ #define MDIO_MMD_C22EXT 29 /* Clause 22 extension */ #define MDIO_MMD_VEND1 30 /* Vendor specific 1 */ @@ -63,6 +67,8 @@ * Lanes B-D are numbered 134-136. */ #define MDIO_PMA_10GBR_FSRT_CSR 147 /* 10GBASE-R fast retrain status and control */ #define MDIO_PMA_10GBR_FECABLE 170 /* 10GBASE-R FEC ability */ +#define MDIO_PMA_RSFEC_CTRL 200 /* RSFEC control */ +#define MDIO_PMA_RSFEC_LANE_MAP 206 /* RSFEC lane mapping */ #define MDIO_PCS_10GBX_STAT1 24 /* 10GBASE-X PCS status 1 */ #define MDIO_PCS_10GBRT_STAT1 32 /* 10GBASE-R/-T PCS status 1 */ #define MDIO_PCS_10GBRT_STAT2 33 /* 10GBASE-R/-T PCS status 2 */ @@ -175,6 +181,10 @@ #define MDIO_DEVS_DTEXS MDIO_DEVS_PRESENT(MDIO_MMD_DTEXS) #define MDIO_DEVS_TC MDIO_DEVS_PRESENT(MDIO_MMD_TC) #define MDIO_DEVS_AN MDIO_DEVS_PRESENT(MDIO_MMD_AN) +#define MDIO_DEVS_SEP_PMA1 MDIO_DEVS_PRESENT(MDIO_MMD_SEP_PMA1) +#define MDIO_DEVS_SEP_PMA2 MDIO_DEVS_PRESENT(MDIO_MMD_SEP_PMA2) +#define MDIO_DEVS_SEP_PMA3 MDIO_DEVS_PRESENT(MDIO_MMD_SEP_PMA3) +#define MDIO_DEVS_SEP_PMA4 MDIO_DEVS_PRESENT(MDIO_MMD_SEP_PMA4) #define MDIO_DEVS_C22EXT MDIO_DEVS_PRESENT(MDIO_MMD_C22EXT) #define MDIO_DEVS_VEND1 MDIO_DEVS_PRESENT(MDIO_MMD_VEND1) #define MDIO_DEVS_VEND2 MDIO_DEVS_PRESENT(MDIO_MMD_VEND2) diff --git a/include/uapi/linux/netdev.h b/include/uapi/linux/netdev.h index 7df1056a35fd..2f3ab75e8cc0 100644 --- a/include/uapi/linux/netdev.h +++ b/include/uapi/linux/netdev.h @@ -97,6 +97,8 @@ enum { }; enum { + NETDEV_A_IO_URING_PROVIDER_INFO_RX_BUF_LEN = 1, + __NETDEV_A_IO_URING_PROVIDER_INFO_MAX, NETDEV_A_IO_URING_PROVIDER_INFO_MAX = (__NETDEV_A_IO_URING_PROVIDER_INFO_MAX - 1) }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3d55bf4be36f..9998f6c0a665 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -729,7 +729,9 @@ * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the * driver to schedule this time due to other concurrent needs for the - * radio). + * radio). An optional attribute %NL80211_ATTR_MAC can be used to filter + * incoming frames during remain-on-channel, such that frames + * addressed to the specified destination MAC are reported. * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) * that will be included with any events pertaining to this request; * the cookie is also used to cancel the request. @@ -1204,10 +1206,12 @@ * user space through the connect result as the user space would have * initiated the connection through the connect request. * - * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's - * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE, - * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its - * address(specified in %NL80211_ATTR_MAC). + * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notifies that a station's + * HT opmode or VHT opmode changed using any of %NL80211_ATTR_SMPS_MODE, + * %NL80211_ATTR_CHANNEL_WIDTH, %NL80211_ATTR_NSS attributes with its + * address (specified in %NL80211_ATTR_MAC). + * Note that 80+80 and 160 MHz might not be differentiated, i.e. may + * report %NL80211_CHAN_WIDTH_160 instead of %NL80211_CHAN_WIDTH_80P80. * * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute. @@ -1415,6 +1419,12 @@ * identifying the evacuated channel. * User space may reconfigure the local schedule in response to this * notification. + * @NL80211_CMD_START_PD: Start PD operation, identified by its + * %NL80211_ATTR_WDEV interface. This interface must have been previously + * created with %NL80211_CMD_NEW_INTERFACE. + * @NL80211_CMD_STOP_PD: Stop the PD operation, identified by + * its %NL80211_ATTR_WDEV interface. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1690,6 +1700,9 @@ enum nl80211_commands { NL80211_CMD_NAN_CHANNEL_EVAC, + NL80211_CMD_START_PD, + NL80211_CMD_STOP_PD, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2991,11 +3004,13 @@ enum nl80211_commands { * @NL80211_ATTR_EPCS: Flag attribute indicating that EPCS is enabled for a * station interface. * - * @NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS: Extended MLD capabilities and - * operations that userspace implements to use during association/ML - * link reconfig, currently only "BTM MLD Recommendation For Multiple - * APs Support". Drivers may set additional flags that they support - * in the kernel or device. + * @NL80211_ATTR_EXT_MLD_CAPA_AND_OPS: Extended MLD capabilities and operations. + * For association and link reconfiguration, indicates extra capabilities + * that userspace implements, currently only "BTM MLD Recommendation For + * Multiple APs Support". + * For wiphy information, additional flags that drivers will set, but + * this is informational only for userspace (it's not expected to set + * these.) * * @NL80211_ATTR_WIPHY_RADIO_INDEX: (int) Integer attribute denoting the index * of the radio in interest. Internally a value of -1 is used to @@ -3140,6 +3155,16 @@ enum nl80211_commands { * association response etc., since it's abridged in the beacon. Used * for START_AP etc. * + * @NL80211_ATTR_ASSOC_ENCRYPTED: Flag attribute, used only with the + * %NL80211_CMD_CONNECT event in SME-in-driver mode. The driver should + * set this flag to indicate that both the (Re)Association Request frame + * and the corresponding (Re)Association Response frame are transmitted + * encrypted over the air. Enhanced Privacy Protection (EPP), as defined + * in IEEE P802.11bi/D4.0, mandates this encryption. + * + * @NL80211_ATTR_NPCA_PRIMARY_FREQ: NPCA primary channel (u32) + * @NL80211_ATTR_NPCA_PUNCT_BITMAP: NPCA puncturing bitmap (u32) + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3695,7 +3720,7 @@ enum nl80211_attrs { NL80211_ATTR_MLO_RECONF_REM_LINKS, NL80211_ATTR_EPCS, - NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS, + NL80211_ATTR_EXT_MLD_CAPA_AND_OPS, NL80211_ATTR_WIPHY_RADIO_INDEX, @@ -3733,6 +3758,11 @@ enum nl80211_attrs { NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME, NL80211_ATTR_NAN_PEER_MAPS, + NL80211_ATTR_ASSOC_ENCRYPTED, + + NL80211_ATTR_NPCA_PRIMARY_FREQ, + NL80211_ATTR_NPCA_PUNCT_BITMAP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3747,6 +3777,7 @@ enum nl80211_attrs { #define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP +#define NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS NL80211_ATTR_EXT_MLD_CAPA_AND_OPS /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -3829,6 +3860,7 @@ enum nl80211_attrs { * @NL80211_IFTYPE_NAN_DATA: NAN data interface type (netdev); NAN data * interfaces can only be brought up (IFF_UP) when a NAN interface * already exists and NAN has been started (using %NL80211_CMD_START_NAN). + * @NL80211_IFTYPE_PD: PD device interface type (not a netdev) * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -3851,6 +3883,7 @@ enum nl80211_iftype { NL80211_IFTYPE_OCB, NL80211_IFTYPE_NAN, NL80211_IFTYPE_NAN_DATA, + NL80211_IFTYPE_PD, /* keep last */ NUM_NL80211_IFTYPES, @@ -5790,6 +5823,18 @@ enum nl80211_key_default_types { * @NL80211_KEY_MODE: the mode from enum nl80211_key_mode. * Defaults to @NL80211_KEY_RX_TX. * @NL80211_KEY_DEFAULT_BEACON: flag indicating default Beacon frame key + * @NL80211_KEY_LTF_SEED: LTF key seed is used by the driver to generate + * secure LTF keys used in case of peer measurement request with FTM + * request type as either %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED + * or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED. Secure LTF key seeds + * will help enable PHY security in peer measurement session. + * The LTF key seed is installed along with the TK (Temporal Key) using + * %NL80211_CMD_NEW_KEY. The TK is configured using the + * %NL80211_ATTR_KEY_DATA attribute, while the LTF key seed is configured + * using this attribute. Both keys must be configured before initiation + * of peer measurement to ensure peer measurement session is secure. + * Only valid if %NL80211_EXT_FEATURE_SET_KEY_LTF_SEED is set. This + * attribute is restricted to pairwise keys (%NL80211_KEYTYPE_PAIRWISE). * * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute @@ -5806,6 +5851,7 @@ enum nl80211_key_attributes { NL80211_KEY_DEFAULT_TYPES, NL80211_KEY_MODE, NL80211_KEY_DEFAULT_BEACON, + NL80211_KEY_LTF_SEED, /* keep last */ __NL80211_KEY_AFTER_LAST, @@ -7029,6 +7075,16 @@ enum nl80211_feature_flags { * (NL80211_CMD_AUTHENTICATE) in non-AP STA mode, as specified in * "IEEE P802.11bi/D4.0, 12.16.5". * + * @NL80211_EXT_FEATURE_ROC_ADDR_FILTER: Driver supports MAC address + * filtering during remain-on-channel. When %NL80211_ATTR_MAC is + * provided with %NL80211_CMD_REMAIN_ON_CHANNEL, the driver will + * forward frames with a matching MAC address to userspace during + * the off-channel period. + * + * @NL80211_EXT_FEATURE_SET_KEY_LTF_SEED: Driver supports installing the + * LTF key seed via %NL80211_KEY_LTF_SEED. The seed is used to generate + * secure LTF keys for secure LTF measurement sessions. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -7108,6 +7164,8 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_EPPKE, NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION, NL80211_EXT_FEATURE_IEEE8021X_AUTH, + NL80211_EXT_FEATURE_ROC_ADDR_FILTER, + NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -7960,6 +8018,26 @@ enum nl80211_peer_measurement_resp { NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1 }; +/** + * enum nl80211_peer_measurement_ftm_req_type - FTM ranging request type, + * used with %NL80211_PMSR_PEER_ATTR_REQ_TYPE + * + * @NL80211_PMSR_FTM_REQ_TYPE_INFRA: infrastructure ranging, i.e. STA-to-AP + * @NL80211_PMSR_FTM_REQ_TYPE_PD: peer-to-peer ranging as defined in the + * Wi-Fi Alliance specification "Proximity Ranging (PR) Implementation + * Consideration Draft 1.9 Rev 1" + * @NUM_NL80211_PMSR_FTM_REQ_TYPE: internal + * @NL80211_PMSR_FTM_REQ_TYPE_MAX: highest request type value + */ +enum nl80211_peer_measurement_ftm_req_type { + NL80211_PMSR_FTM_REQ_TYPE_INFRA, + NL80211_PMSR_FTM_REQ_TYPE_PD, + + /* keep last */ + NUM_NL80211_PMSR_FTM_REQ_TYPE, + NL80211_PMSR_FTM_REQ_TYPE_MAX = NUM_NL80211_PMSR_FTM_REQ_TYPE - 1 +}; + /** * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid @@ -7973,6 +8051,9 @@ enum nl80211_peer_measurement_resp { * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by * measurement type, with attributes from the * &enum nl80211_peer_measurement_resp inside. + * @NL80211_PMSR_PEER_ATTR_REQ_TYPE: u32 attribute specifying the ranging + * request type, using values from &enum nl80211_peer_measurement_ftm_req_type. + * If absent, defaults to %NL80211_PMSR_FTM_REQ_TYPE_INFRA. * * @NUM_NL80211_PMSR_PEER_ATTRS: internal * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number @@ -7984,6 +8065,7 @@ enum nl80211_peer_measurement_peer_attrs { NL80211_PMSR_PEER_ATTR_CHAN, NL80211_PMSR_PEER_ATTR_REQ, NL80211_PMSR_PEER_ATTR_RESP, + NL80211_PMSR_PEER_ATTR_REQ_TYPE, /* keep last */ NUM_NL80211_PMSR_PEER_ATTRS, @@ -8010,6 +8092,18 @@ enum nl80211_peer_measurement_peer_attrs { * meaningless, just a list of peers to measure with, with the * sub-attributes taken from * &enum nl80211_peer_measurement_peer_attrs. + * @NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE: u32 attribute indicating the + * maximum number of peers supported when the device operates in the + * ISTA (Initiator STA) role. If absent, no role-specific peer limit + * applies. The sum of %NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE and + * %NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE is enforced when the device + * supports concurrent ISTA/RSTA operation. + * @NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE: u32 attribute indicating the + * maximum number of peers supported when the device operates in the + * RSTA (Responder STA) role. If absent, no role-specific peer limit + * applies. The sum of %NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE and + * %NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE is enforced when the device + * supports concurrent ISTA/RSTA operation. * * @NUM_NL80211_PMSR_ATTR: internal * @NL80211_PMSR_ATTR_MAX: highest attribute number @@ -8022,6 +8116,8 @@ enum nl80211_peer_measurement_attrs { NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, NL80211_PMSR_ATTR_TYPE_CAPA, NL80211_PMSR_ATTR_PEERS, + NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE, + NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE, /* keep last */ NUM_NL80211_PMSR_ATTR, @@ -8080,6 +8176,54 @@ enum nl80211_peer_measurement_attrs { * This limits the allowed combinations of LTF repetitions and STS. * @NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT: flag attribute indicating the * device supports operating as the RSTA in PMSR FTM request + * @NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS: nested attribute containing ISTA + * (initiator) role capabilities. Uses the same sub-attributes as + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS. + * @NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS: nested attribute containing RSTA + * (responder) role capabilities. + * @NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB: flag attribute (used inside + * %NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS or + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS) indicating NTB ranging support. + * @NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB: flag attribute (used inside + * %NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS or + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS) indicating TB ranging support. + * @NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA: flag attribute (used inside + * %NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS or + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS) indicating EDCA based ranging + * support. + * @NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS: nested attribute containing ranging + * type capabilities. Uses sub-attributes from + * &enum nl80211_peer_measurement_ftm_type_capa. + * @NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT: flag attribute + * indicating that the device can simultaneously act as initiator and + * responder in a multi-peer measurement request. Only valid if + * @NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT is set. + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS: u32 attribute indicating + * the maximum number of transmit antennas supported for EDCA based ranging + * (0 means unknown) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS: u32 attribute indicating + * the maximum number of receive antennas supported for EDCA based ranging + * (0 means unknown) + * @NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA: u32 attribute indicating + * the minimum EDCA ranging interval supported by the device + * in milli seconds. (0 means unknown). Applications can use this value + * to estimate the burst period to be given in the FTM request for the + * EDCA based ranging case. If non-zero, this value will be used to + * validate the burst period in the FTM request. + * @NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB: u32 attribute indicating + * the minimum NTB ranging interval supported by the device + * in milli seconds. (0 means unknown). Applications can use this value + * to estimate the burst period to be given in the FTM request for the + * NTB ranging case. If non-zero, this value will be used to validate + * the nominal time in the FTM request. + * @NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES: u32 bitmap of values from + * &enum nl80211_preamble indicating the supported preambles for PD + * ranging requests. Only valid if %NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT + * is set. + * @NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS: u32 bitmap of values from + * &enum nl80211_chan_width indicating the supported channel bandwidths + * for PD ranging requests. Only valid if + * %NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT is set. * * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number @@ -8105,12 +8249,52 @@ enum nl80211_peer_measurement_ftm_capa { NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX, NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX, NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT, + NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS, + NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS, + NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB, + NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB, + NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA, + NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS, + NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS, + NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA, + NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB, + NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES, + NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS, /* keep last */ NUM_NL80211_PMSR_FTM_CAPA_ATTR, NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1 }; +/** + * enum nl80211_peer_measurement_ftm_type_capa - FTM ranging type capability + * sub-attributes, used inside %NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS + * @__NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT: flag attribute indicating + * that the device supports infrastructure ranging (STA-to-AP or + * AP-to-STA) as part of Proximity Detection + * @NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT: flag attribute indicating that + * the device supports peer-to-peer ranging as mentioned in the + * specification "PR Implementation Consideration Draft 1.9 rev 1" where + * PD stands for proximity detection + * + * @NUM_NL80211_PMSR_FTM_TYPE_CAPA_ATTR: internal + * @NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_type_capa { + __NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INVALID, + + NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT, + NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT, + + /* keep last */ + NUM_NL80211_PMSR_FTM_TYPE_CAPA_ATTR, + NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_TYPE_CAPA_ATTR - 1 +}; + /** * enum nl80211_peer_measurement_ftm_req - FTM request attributes * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid @@ -8129,9 +8313,11 @@ enum nl80211_peer_measurement_ftm_capa { * default 15 i.e. "no preference"). For non-EDCA ranging, this is the * burst duration in milliseconds (optional with default 0, i.e. let the * device decide). - * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames - * requested per burst + * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: (Optional) number of successful + * FTM frames requested per burst * (u8, 0-31, optional with default 0 i.e. "no preference") + * If the attribute is absent ("no preference"), the driver or firmware can + * choose a suitable value. * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries * (u8, default 3) * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag) @@ -8165,6 +8351,50 @@ enum nl80211_peer_measurement_ftm_capa { * Only valid if %NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK is set (so the * RSTA will have the measurement results to report back in the FTM * response). + * @NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS: minimum time + * between two consecutive range measurements in units of 100 microseconds, + * for non-trigger based ranging (u32). Should be set as short as possible + * to minimize turnaround time, since two-way ranging with delayed LMR + * requires two measurements. Only valid if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS: maximum time + * between two consecutive range measurements in units of 10 milliseconds, + * for non-trigger based ranging (u32). Acts as a session timeout; if + * exceeded, the ranging session should be terminated. Only valid if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME: The nominal time field shall be + * set to the nominal duration between adjacent Availability Windows in + * units of milli seconds (u32). Mandatory if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION: (Optional) The AW duration field + * shall be set to the duration of the AW in units of 1ms (0-255 ms) (u32). + * Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * If the attribute is absent ("no preference"), the driver or firmware + * can choose a suitable value. + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS: (Optional) number of + * Availability Windows (AWs) to schedule for non-trigger-based ranging. + * Each AW may contain multiple FTM exchanges as configured by + * %NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST. Only valid if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * If the attribute is absent ("no preference"), the driver or firmware + * can choose a suitable value. + * @NL80211_PMSR_FTM_REQ_ATTR_PAD: ignore, for u64/s64 padding only. + * @NL80211_PMSR_FTM_REQ_ATTR_INGRESS: optional u64 attribute in units of mm. + * When specified, the measurement result of the peer needs to be + * indicated if the device moves into this range. + * @NL80211_PMSR_FTM_REQ_ATTR_EGRESS: optional u64 attribute in units of mm. + * When specified, the measurement result of the peer needs to be + * indicated if the device moves out of this range. + * If neither or only one of @NL80211_PMSR_FTM_REQ_ATTR_INGRESS and + * @NL80211_PMSR_FTM_REQ_ATTR_EGRESS is specified, only the specified + * threshold is used. If both are specified, both thresholds are applied. + * If neither is specified, results are reported without threshold + * filtering. + * @NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS: Flag to suppress ranging + * results for PD requests. When set, ranging measurements are performed + * but results are not reported to userspace, regardless of ranging role + * or type. Only valid when %NL80211_PMSR_PEER_ATTR_REQ_TYPE is set to + * %NL80211_PMSR_FTM_REQ_TYPE_PD. * * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number @@ -8186,6 +8416,15 @@ enum nl80211_peer_measurement_ftm_req { NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK, NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR, NL80211_PMSR_FTM_REQ_ATTR_RSTA, + NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME, + NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION, + NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS, + NL80211_PMSR_FTM_REQ_ATTR_PAD, + NL80211_PMSR_FTM_REQ_ATTR_INGRESS, + NL80211_PMSR_FTM_REQ_ATTR_EGRESS, + NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS, /* keep last */ NUM_NL80211_PMSR_FTM_REQ_ATTR, @@ -8272,6 +8511,33 @@ enum nl80211_peer_measurement_ftm_failure_reasons { * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only * @NL80211_PMSR_FTM_RESP_ATTR_BURST_PERIOD: actual burst period used by * the responder (similar to request, u16) + * @NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT: negotiated value of + * number of tx ltf repetitions in NDP frames (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT: negotiated value of + * number of rx ltf repetitions in NDP frames (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS: negotiated value + * where latest time by which the ISTA needs to complete the next round of + * measurements, in units of 10 ms (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS: negotiated + * minimum time between two consecutive range measurements initiated by an + * ISTA, in units of 100 us (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS: number of Tx space-time + * streams used in NDP frames during the measurement sounding phase + * (u32, optional). + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS: number of Rx space-time + * streams used in the NDP frames during the measurement sounding phase + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME: negotiated nominal time used in + * this session in milliseconds. (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW: negotiated availability + * window time used in this session, in units of milli seconds. + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH: u32 attribute indicating channel + * width used for measurement, see &enum nl80211_chan_width (optional). + * @NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE: u32 attribute indicating the preamble + * type used for the measurement, see &enum nl80211_preamble (optional). + * @NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR: flag, indicates if the + * current result is delayed LMR data. * * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number @@ -8301,6 +8567,17 @@ enum nl80211_peer_measurement_ftm_resp { NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, NL80211_PMSR_FTM_RESP_ATTR_PAD, NL80211_PMSR_FTM_RESP_ATTR_BURST_PERIOD, + NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT, + NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT, + NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS, + NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME, + NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW, + NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH, + NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE, + NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR, /* keep last */ NUM_NL80211_PMSR_FTM_RESP_ATTR, diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 66e8072f44df..490efd288526 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -569,6 +569,16 @@ struct tc_netem_gemodel { #define NETEM_DIST_SCALE 8192 #define NETEM_DIST_MAX 16384 +struct tc_netem_xstats { + __u64 delayed; /* packets delayed */ + __u64 dropped; /* packets dropped by loss model */ + __u64 corrupted; /* packets with bit errors injected */ + __u64 duplicated; /* duplicate packets generated */ + __u64 reordered; /* packets sent out of order */ + __u64 ecn_marked; /* packets ECN CE-marked (not dropped)*/ + __u64 allocation_errors; +}; + /* DRR */ enum { diff --git a/include/uapi/linux/pps.h b/include/uapi/linux/pps.h index 009ebcd8ced5..1088dea65e12 100644 --- a/include/uapi/linux/pps.h +++ b/include/uapi/linux/pps.h @@ -26,7 +26,7 @@ #include #define PPS_VERSION "5.3.6" -#define PPS_MAX_SOURCES 16 /* should be enough... */ +#define PPS_MAX_SOURCES 256 /* should be enough... */ /* Implementation note: the logical states ``assert'' and ``clear'' * are implemented in terms of the chip register, i.e. ``assert'' diff --git a/include/uapi/linux/psp.h b/include/uapi/linux/psp.h index a3a336488dc3..1c8899cd4da5 100644 --- a/include/uapi/linux/psp.h +++ b/include/uapi/linux/psp.h @@ -17,11 +17,22 @@ enum psp_version { PSP_VERSION_HDR0_AES_GMAC_256, }; +enum { + PSP_A_ASSOC_DEV_INFO_IFINDEX = 1, + PSP_A_ASSOC_DEV_INFO_NSID, + + __PSP_A_ASSOC_DEV_INFO_MAX, + PSP_A_ASSOC_DEV_INFO_MAX = (__PSP_A_ASSOC_DEV_INFO_MAX - 1) +}; + enum { PSP_A_DEV_ID = 1, PSP_A_DEV_IFINDEX, PSP_A_DEV_PSP_VERSIONS_CAP, PSP_A_DEV_PSP_VERSIONS_ENA, + PSP_A_DEV_ASSOC_LIST, + PSP_A_DEV_NSID, + PSP_A_DEV_BY_ASSOCIATION, __PSP_A_DEV_MAX, PSP_A_DEV_MAX = (__PSP_A_DEV_MAX - 1) @@ -74,6 +85,8 @@ enum { PSP_CMD_RX_ASSOC, PSP_CMD_TX_ASSOC, PSP_CMD_GET_STATS, + PSP_CMD_DEV_ASSOC, + PSP_CMD_DEV_DISASSOC, __PSP_CMD_MAX, PSP_CMD_MAX = (__PSP_CMD_MAX - 1) diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index dab9493c791b..27265fd31e5f 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -840,6 +840,7 @@ enum { #define RTEXT_FILTER_CFM_CONFIG (1 << 5) #define RTEXT_FILTER_CFM_STATUS (1 << 6) #define RTEXT_FILTER_MST (1 << 7) +#define RTEXT_FILTER_NAME_ONLY (1 << 8) /* End of information exported to user level */ diff --git a/include/uapi/linux/tls.h b/include/uapi/linux/tls.h index b8b9c42f848c..1245ab38afc1 100644 --- a/include/uapi/linux/tls.h +++ b/include/uapi/linux/tls.h @@ -203,6 +203,6 @@ enum { #define TLS_CONF_BASE 1 #define TLS_CONF_SW 2 #define TLS_CONF_HW 3 -#define TLS_CONF_HW_RECORD 4 +#define TLS_CONF_HW_RECORD 4 /* unused */ #endif /* _UAPI_LINUX_TLS_H */ diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index a23495c0e0a1..051f8066efd1 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -227,6 +227,9 @@ enum { #define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT XFRM_MSG_GETDEFAULT, #define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT + + XFRM_MSG_MIGRATE_STATE, +#define XFRM_MSG_MIGRATE_STATE XFRM_MSG_MIGRATE_STATE __XFRM_MSG_MAX }; #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1) @@ -507,6 +510,28 @@ struct xfrm_user_migrate { __u16 new_family; }; +struct xfrm_user_migrate_state { + struct xfrm_usersa_id id; + xfrm_address_t new_daddr; + xfrm_address_t new_saddr; + struct xfrm_mark old_mark; + struct xfrm_selector new_sel; + __u32 new_reqid; + __u32 flags; + __u16 new_family; + __u16 reserved; +}; + +/* Flags for xfrm_user_migrate_state.flags */ +enum xfrm_migrate_state_flags { + XFRM_MIGRATE_STATE_CLEAR_OFFLOAD = 1, /* do not inherit offload from existing SA */ + XFRM_MIGRATE_STATE_UPDATE_H2H_SEL = 2, /* update H2H selector from new daddr/saddr */ +}; + +/* All flags defined as of this header version; unknown bits are rejected. */ +#define XFRM_MIGRATE_STATE_KNOWN_FLAGS \ + (XFRM_MIGRATE_STATE_CLEAR_OFFLOAD | XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) + struct xfrm_user_mapping { struct xfrm_usersa_id id; __u32 reqid; diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 49163f9c39df..7ad52f499f87 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -1294,6 +1294,7 @@ static void io_pp_zc_destroy(struct page_pool *pp) static int io_pp_nl_fill(void *mp_priv, struct sk_buff *rsp, struct netdev_rx_queue *rxq) { + struct io_zcrx_ifq *ifq = mp_priv; struct nlattr *nest; int type; @@ -1301,6 +1302,13 @@ static int io_pp_nl_fill(void *mp_priv, struct sk_buff *rsp, nest = nla_nest_start(rsp, type); if (!nest) return -EMSGSIZE; + + if (nla_put_uint(rsp, NETDEV_A_IO_URING_PROVIDER_INFO_RX_BUF_LEN, + 1ULL << ifq->niov_shift)) { + nla_nest_cancel(rsp, nest); + return -EMSGSIZE; + } + nla_nest_end(rsp, nest); return 0; diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c index 7b374595328d..a4dde85664f2 100644 --- a/net/6lowpan/nhc.c +++ b/net/6lowpan/nhc.c @@ -117,9 +117,9 @@ int lowpan_nhc_do_uncompression(struct sk_buff *skb, return ret; } } else { - spin_unlock_bh(&lowpan_nhc_lock); netdev_warn(dev, "received nhc id for %s which is not implemented.\n", nhc->name); + spin_unlock_bh(&lowpan_nhc_lock); return -ENOTSUPP; } } else { diff --git a/net/802/Makefile b/net/802/Makefile index 9503ef6b2e06..54784f3cae5a 100644 --- a/net/802/Makefile +++ b/net/802/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_LLC) += psnap.o obj-$(CONFIG_NET_FC) += fc.o obj-$(CONFIG_FDDI) += fddi.o -obj-$(CONFIG_ATALK) += psnap.o obj-$(CONFIG_STP) += stp.o obj-$(CONFIG_GARP) += garp.o obj-$(CONFIG_MRP) += mrp.o diff --git a/net/802/fddi.c b/net/802/fddi.c index 888379ae35ec..e26f4549e904 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -103,6 +103,9 @@ __be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev) skb->dev = dev; skb_reset_mac_header(skb); /* point to frame control (FC) */ + if (skb->len < FDDI_K_8022_HLEN) + return htons(0); + if(fddi->hdr.llc_8022_1.dsap==0xe0) { skb_pull(skb, FDDI_K_8022_HLEN-3); @@ -110,6 +113,8 @@ __be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev) } else { + if (skb->len < FDDI_K_SNAP_HLEN) + return htons(0); skb_pull(skb, FDDI_K_SNAP_HLEN); /* adjust for 21 byte header */ type=fddi->hdr.llc_snap.ethertype; } diff --git a/net/802/garp.c b/net/802/garp.c index c7a39f298ad6..56b934ec1aae 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -452,6 +452,7 @@ static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, if (!pskb_may_pull(skb, ga->len)) return -1; + ga = (struct garp_attr_hdr *)skb->data; skb_pull(skb, ga->len); dlen = ga->len - sizeof(*ga); @@ -492,6 +493,7 @@ static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) { const struct garp_msg_hdr *gm; + u8 attrtype; if (!pskb_may_pull(skb, sizeof(*gm))) return -1; @@ -499,9 +501,10 @@ static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) if (gm->attrtype == 0) return -1; skb_pull(skb, sizeof(*gm)); + attrtype = gm->attrtype; while (skb->len > 0) { - if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0) + if (garp_pdu_parse_attr(app, skb, attrtype) < 0) return -1; if (garp_pdu_parse_end_mark(skb) < 0) break; diff --git a/net/Kconfig b/net/Kconfig index bdea8aef7983..e38477393551 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -261,7 +261,6 @@ source "net/bridge/Kconfig" source "net/dsa/Kconfig" source "net/8021q/Kconfig" source "net/llc/Kconfig" -source "net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" source "net/phonet/Kconfig" diff --git a/net/Makefile b/net/Makefile index d2175fce0406..5b2dd7f07a85 100644 --- a/net/Makefile +++ b/net/Makefile @@ -25,7 +25,6 @@ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DEVLINK) += devlink/ obj-y += dsa/ -obj-$(CONFIG_ATALK) += appletalk/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ obj-$(CONFIG_CAN) += can/ diff --git a/net/appletalk/Kconfig b/net/appletalk/Kconfig deleted file mode 100644 index 041141abf925..000000000000 --- a/net/appletalk/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Appletalk configuration -# -config ATALK - tristate "Appletalk protocol support" - select LLC - help - AppleTalk is the protocol that Apple computers can use to communicate - on a network. If your Linux box is connected to such a network and you - wish to connect to it, say Y. You will need to use the netatalk package - so that your Linux box can act as a print and file server for Macs as - well as access AppleTalk printers. Check out - on the WWW for details. - EtherTalk is the name used for AppleTalk over Ethernet and the - cheaper and slower LocalTalk is AppleTalk over a proprietary Apple - network using serial links. EtherTalk and LocalTalk are fully - supported by Linux. - - General information about how to connect Linux, Windows machines and - Macs is on the WWW at . The - NET3-4-HOWTO, available from - , contains valuable - information as well. - - To compile this driver as a module, choose M here: the module will be - called appletalk. You almost certainly want to compile it as a - module so you can restart your AppleTalk stack without rebooting - your machine. I hear that the GNU boycott of Apple is over, so - even politically correct people are allowed to say Y here. diff --git a/net/appletalk/Makefile b/net/appletalk/Makefile deleted file mode 100644 index 152312a15180..000000000000 --- a/net/appletalk/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Linux AppleTalk layer. -# - -obj-$(CONFIG_ATALK) += appletalk.o - -appletalk-y := aarp.o ddp.o -appletalk-$(CONFIG_PROC_FS) += atalk_proc.o -appletalk-$(CONFIG_SYSCTL) += sysctl_net_atalk.o diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c deleted file mode 100644 index 078fb7a6efa5..000000000000 --- a/net/appletalk/aarp.c +++ /dev/null @@ -1,1041 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * AARP: An implementation of the AppleTalk AARP protocol for - * Ethernet 'ELAP'. - * - * Alan Cox - * - * This doesn't fit cleanly with the IP arp. Potentially we can use - * the generic neighbour discovery code to clean this up. - * - * FIXME: - * We ought to handle the retransmits with a single list and a - * separate fast timer for when it is needed. - * Use neighbour discovery code. - * Token Ring Support. - * - * References: - * Inside AppleTalk (2nd Ed). - * Fixes: - * Jaume Grau - flush caches on AARP_PROBE - * Rob Newberry - Added proxy AARP and AARP proc fs, - * moved probing from DDP module. - * Arnaldo C. Melo - don't mangle rx packets - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME; -int sysctl_aarp_tick_time = AARP_TICK_TIME; -int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT; -int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME; - -/* Lists of aarp entries */ -/** - * struct aarp_entry - AARP entry - * @refcnt: Reference count - * @last_sent: Last time we xmitted the aarp request - * @packet_queue: Queue of frames wait for resolution - * @status: Used for proxy AARP - * @expires_at: Entry expiry time - * @target_addr: DDP Address - * @dev: Device to use - * @hwaddr: Physical i/f address of target/router - * @xmit_count: When this hits 10 we give up - * @next: Next entry in chain - */ -struct aarp_entry { - refcount_t refcnt; - /* These first two are only used for unresolved entries */ - unsigned long last_sent; - struct sk_buff_head packet_queue; - int status; - unsigned long expires_at; - struct atalk_addr target_addr; - struct net_device *dev; - char hwaddr[ETH_ALEN]; - unsigned short xmit_count; - struct aarp_entry *next; -}; - -/* Hashed list of resolved, unresolved and proxy entries */ -static struct aarp_entry *resolved[AARP_HASH_SIZE]; -static struct aarp_entry *unresolved[AARP_HASH_SIZE]; -static struct aarp_entry *proxies[AARP_HASH_SIZE]; -static int unresolved_count; - -/* One lock protects it all. */ -static DEFINE_RWLOCK(aarp_lock); - -/* Used to walk the list and purge/kick entries. */ -static struct timer_list aarp_timer; - -static inline void aarp_entry_get(struct aarp_entry *a) -{ - refcount_inc(&a->refcnt); -} - -static inline void aarp_entry_put(struct aarp_entry *a) -{ - if (refcount_dec_and_test(&a->refcnt)) - kfree(a); -} - -/* - * Delete an aarp queue - * - * Must run under aarp_lock. - */ -static void __aarp_expire(struct aarp_entry *a) -{ - skb_queue_purge(&a->packet_queue); - aarp_entry_put(a); -} - -/* - * Send an aarp queue entry request - * - * Must run under aarp_lock. - */ -static void __aarp_send_query(struct aarp_entry *a) -{ - static unsigned char aarp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - struct net_device *dev = a->dev; - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - struct atalk_addr *sat = atalk_find_dev_addr(dev); - - if (!skb) - return; - - if (!sat) { - kfree_skb(skb); - return; - } - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_REQUEST); - - ether_addr_copy(eah->hw_src, dev->dev_addr); - - eah->pa_src_zero = 0; - eah->pa_src_net = sat->s_net; - eah->pa_src_node = sat->s_node; - - eth_zero_addr(eah->hw_dst); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = a->target_addr.s_net; - eah->pa_dst_node = a->target_addr.s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); - /* Update the sending count */ - a->xmit_count++; - a->last_sent = jiffies; -} - -/* This runs under aarp_lock and in softint context, so only atomic memory - * allocations can be used. */ -static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us, - struct atalk_addr *them, unsigned char *sha) -{ - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - - if (!skb) - return; - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_REPLY); - - ether_addr_copy(eah->hw_src, dev->dev_addr); - - eah->pa_src_zero = 0; - eah->pa_src_net = us->s_net; - eah->pa_src_node = us->s_node; - - if (!sha) - eth_zero_addr(eah->hw_dst); - else - ether_addr_copy(eah->hw_dst, sha); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = them->s_net; - eah->pa_dst_node = them->s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, sha); -} - -/* - * Send probe frames. Called from aarp_probe_network and - * aarp_proxy_probe_network. - */ - -static void aarp_send_probe(struct net_device *dev, struct atalk_addr *us) -{ - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - static unsigned char aarp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - - if (!skb) - return; - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_PROBE); - - ether_addr_copy(eah->hw_src, dev->dev_addr); - - eah->pa_src_zero = 0; - eah->pa_src_net = us->s_net; - eah->pa_src_node = us->s_node; - - eth_zero_addr(eah->hw_dst); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = us->s_net; - eah->pa_dst_node = us->s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); -} - -/* - * Handle an aarp timer expire - * - * Must run under the aarp_lock. - */ - -static void __aarp_expire_timer(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) - /* Expired ? */ - if (time_after(jiffies, (*n)->expires_at)) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else - n = &((*n)->next); -} - -/* - * Kick all pending requests 5 times a second. - * - * Must run under the aarp_lock. - */ -static void __aarp_kick(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) - /* Expired: if this will be the 11th tx, we delete instead. */ - if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else { - __aarp_send_query(*n); - n = &((*n)->next); - } -} - -/* - * A device has gone down. Take all entries referring to the device - * and remove them. - * - * Must run under the aarp_lock. - */ -static void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev) -{ - struct aarp_entry *t; - - while (*n) - if ((*n)->dev == dev) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else - n = &((*n)->next); -} - -/* Handle the timer event */ -static void aarp_expire_timeout(struct timer_list *unused) -{ - int ct; - - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_timer(&resolved[ct]); - __aarp_kick(&unresolved[ct]); - __aarp_expire_timer(&unresolved[ct]); - __aarp_expire_timer(&proxies[ct]); - } - - write_unlock_bh(&aarp_lock); - mod_timer(&aarp_timer, jiffies + - (unresolved_count ? sysctl_aarp_tick_time : - sysctl_aarp_expiry_time)); -} - -/* Network device notifier chain handler. */ -static int aarp_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - int ct; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_DOWN) { - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_device(&resolved[ct], dev); - __aarp_expire_device(&unresolved[ct], dev); - __aarp_expire_device(&proxies[ct], dev); - } - - write_unlock_bh(&aarp_lock); - } - return NOTIFY_DONE; -} - -/* Expire all entries in a hash chain */ -static void __aarp_expire_all(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } -} - -/* Cleanup all hash chains -- module unloading */ -static void aarp_purge(void) -{ - int ct; - - write_lock_bh(&aarp_lock); - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_all(&resolved[ct]); - __aarp_expire_all(&unresolved[ct]); - __aarp_expire_all(&proxies[ct]); - } - write_unlock_bh(&aarp_lock); -} - -/* - * Create a new aarp entry. This must use GFP_ATOMIC because it - * runs while holding spinlocks. - */ -static struct aarp_entry *aarp_alloc(void) -{ - struct aarp_entry *a = kzalloc_obj(*a, GFP_ATOMIC); - if (!a) - return NULL; - - refcount_set(&a->refcnt, 1); - skb_queue_head_init(&a->packet_queue); - return a; -} - -/* - * Find an entry. We might return an expired but not yet purged entry. We - * don't care as it will do no harm. - * - * This must run under the aarp_lock. - */ -static struct aarp_entry *__aarp_find_entry(struct aarp_entry *list, - struct net_device *dev, - struct atalk_addr *sat) -{ - while (list) { - if (list->target_addr.s_net == sat->s_net && - list->target_addr.s_node == sat->s_node && - list->dev == dev) - break; - list = list->next; - } - - return list; -} - -/* Called from the DDP code, and thus must be exported. */ -void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa) -{ - int hash = sa->s_node % (AARP_HASH_SIZE - 1); - struct aarp_entry *a; - - write_lock_bh(&aarp_lock); - - a = __aarp_find_entry(proxies[hash], dev, sa); - if (a) - a->expires_at = jiffies - 1; - - write_unlock_bh(&aarp_lock); -} - -/* This must run under aarp_lock. */ -static struct atalk_addr *__aarp_proxy_find(struct net_device *dev, - struct atalk_addr *sa) -{ - int hash = sa->s_node % (AARP_HASH_SIZE - 1); - struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa); - - return a ? sa : NULL; -} - -void aarp_probe_network(struct atalk_iface *atif) -{ - unsigned int count; - - for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { - aarp_send_probe(atif->dev, &atif->address); - - /* Defer 1/10th */ - msleep(100); - - if (atif->status & ATIF_PROBE_FAIL) - break; - } -} - -int aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa) -{ - int hash, retval = -EPROTONOSUPPORT; - struct aarp_entry *entry; - unsigned int count; - - /* - * we don't currently support LocalTalk or PPP for proxy AARP; - * if someone wants to try and add it, have fun - */ - if (atif->dev->type == ARPHRD_LOCALTLK || - atif->dev->type == ARPHRD_PPP) - goto out; - - /* - * create a new AARP entry with the flags set to be published -- - * we need this one to hang around even if it's in use - */ - entry = aarp_alloc(); - retval = -ENOMEM; - if (!entry) - goto out; - - entry->expires_at = -1; - entry->status = ATIF_PROBE; - entry->target_addr.s_node = sa->s_node; - entry->target_addr.s_net = sa->s_net; - entry->dev = atif->dev; - - write_lock_bh(&aarp_lock); - aarp_entry_get(entry); - - hash = sa->s_node % (AARP_HASH_SIZE - 1); - entry->next = proxies[hash]; - proxies[hash] = entry; - - for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { - aarp_send_probe(atif->dev, sa); - - /* Defer 1/10th */ - write_unlock_bh(&aarp_lock); - msleep(100); - write_lock_bh(&aarp_lock); - - if (entry->status & ATIF_PROBE_FAIL) - break; - } - - if (entry->status & ATIF_PROBE_FAIL) { - entry->expires_at = jiffies - 1; /* free the entry */ - retval = -EADDRINUSE; /* return network full */ - } else { /* clear the probing flag */ - entry->status &= ~ATIF_PROBE; - retval = 1; - } - - aarp_entry_put(entry); - write_unlock_bh(&aarp_lock); -out: - return retval; -} - -/* Send a DDP frame */ -int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, - struct atalk_addr *sa, void *hwaddr) -{ - static char ddp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - int hash; - struct aarp_entry *a; - - skb_reset_network_header(skb); - - /* Check for LocalTalk first */ - if (dev->type == ARPHRD_LOCALTLK) { - struct atalk_addr *at = atalk_find_dev_addr(dev); - struct ddpehdr *ddp = (struct ddpehdr *)skb->data; - int ft = 2; - - if (!at) { - kfree_skb(skb); - return NET_XMIT_DROP; - } - - /* - * Compressible ? - * - * IFF: src_net == dest_net == device_net - * (zero matches anything) - */ - - if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) && - (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) { - skb_pull(skb, sizeof(*ddp) - 4); - - /* - * The upper two remaining bytes are the port - * numbers we just happen to need. Now put the - * length in the lower two. - */ - *((__be16 *)skb->data) = htons(skb->len); - ft = 1; - } - /* - * Nice and easy. No AARP type protocols occur here so we can - * just shovel it out with a 3 byte LLAP header - */ - - skb_push(skb, 3); - skb->data[0] = sa->s_node; - skb->data[1] = at->s_node; - skb->data[2] = ft; - skb->dev = dev; - goto sendit; - } - - /* On a PPP link we neither compress nor aarp. */ - if (dev->type == ARPHRD_PPP) { - skb->protocol = htons(ETH_P_PPPTALK); - skb->dev = dev; - goto sendit; - } - - /* Non ELAP we cannot do. */ - if (dev->type != ARPHRD_ETHER) - goto free_it; - - skb->dev = dev; - skb->protocol = htons(ETH_P_ATALK); - hash = sa->s_node % (AARP_HASH_SIZE - 1); - - /* Do we have a resolved entry? */ - if (sa->s_node == ATADDR_BCAST) { - /* Send it */ - ddp_dl->request(ddp_dl, skb, ddp_eth_multicast); - goto sent; - } - - write_lock_bh(&aarp_lock); - a = __aarp_find_entry(resolved[hash], dev, sa); - - if (a) { /* Return 1 and fill in the address */ - a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10); - ddp_dl->request(ddp_dl, skb, a->hwaddr); - write_unlock_bh(&aarp_lock); - goto sent; - } - - /* Do we have an unresolved entry: This is the less common path */ - a = __aarp_find_entry(unresolved[hash], dev, sa); - if (a) { /* Queue onto the unresolved queue */ - skb_queue_tail(&a->packet_queue, skb); - goto out_unlock; - } - - /* Allocate a new entry */ - a = aarp_alloc(); - if (!a) { - /* Whoops slipped... good job it's an unreliable protocol 8) */ - write_unlock_bh(&aarp_lock); - goto free_it; - } - - /* Set up the queue */ - skb_queue_tail(&a->packet_queue, skb); - a->expires_at = jiffies + sysctl_aarp_resolve_time; - a->dev = dev; - a->next = unresolved[hash]; - a->target_addr = *sa; - a->xmit_count = 0; - unresolved[hash] = a; - unresolved_count++; - - /* Send an initial request for the address */ - __aarp_send_query(a); - - /* - * Switch to fast timer if needed (That is if this is the first - * unresolved entry to get added) - */ - - if (unresolved_count == 1) - mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time); - - /* Now finally, it is safe to drop the lock. */ -out_unlock: - write_unlock_bh(&aarp_lock); - - /* Tell the ddp layer we have taken over for this frame. */ - goto sent; - -sendit: - if (skb->sk) - skb->priority = READ_ONCE(skb->sk->sk_priority); - if (dev_queue_xmit(skb)) - goto drop; -sent: - return NET_XMIT_SUCCESS; -free_it: - kfree_skb(skb); -drop: - return NET_XMIT_DROP; -} -EXPORT_SYMBOL(aarp_send_ddp); - -/* - * An entry in the aarp unresolved queue has become resolved. Send - * all the frames queued under it. - * - * Must run under aarp_lock. - */ -static void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, - int hash) -{ - struct sk_buff *skb; - - while (*list) - if (*list == a) { - unresolved_count--; - *list = a->next; - - /* Move into the resolved list */ - a->next = resolved[hash]; - resolved[hash] = a; - - /* Kick frames off */ - while ((skb = skb_dequeue(&a->packet_queue)) != NULL) { - a->expires_at = jiffies + - sysctl_aarp_expiry_time * 10; - ddp_dl->request(ddp_dl, skb, a->hwaddr); - } - } else - list = &((*list)->next); -} - -/* - * This is called by the SNAP driver whenever we see an AARP SNAP - * frame. We currently only support Ethernet. - */ -static int aarp_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct elapaarp *ea = aarp_hdr(skb); - int hash, ret = 0; - __u16 function; - struct aarp_entry *a; - struct atalk_addr sa, *ma, da; - struct atalk_iface *ifa; - - if (!net_eq(dev_net(dev), &init_net)) - goto out0; - - /* We only do Ethernet SNAP AARP. */ - if (dev->type != ARPHRD_ETHER) - goto out0; - - /* Frame size ok? */ - if (!skb_pull(skb, sizeof(*ea))) - goto out0; - - function = ntohs(ea->function); - - /* Sanity check fields. */ - if (function < AARP_REQUEST || function > AARP_PROBE || - ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN || - ea->pa_src_zero || ea->pa_dst_zero) - goto out0; - - /* Looks good. */ - hash = ea->pa_src_node % (AARP_HASH_SIZE - 1); - - /* Build an address. */ - sa.s_node = ea->pa_src_node; - sa.s_net = ea->pa_src_net; - - /* Process the packet. Check for replies of me. */ - ifa = atalk_find_dev(dev); - if (!ifa) - goto out1; - - if (ifa->status & ATIF_PROBE && - ifa->address.s_node == ea->pa_dst_node && - ifa->address.s_net == ea->pa_dst_net) { - ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */ - goto out1; - } - - /* Check for replies of proxy AARP entries */ - da.s_node = ea->pa_dst_node; - da.s_net = ea->pa_dst_net; - - write_lock_bh(&aarp_lock); - a = __aarp_find_entry(proxies[hash], dev, &da); - - if (a && a->status & ATIF_PROBE) { - a->status |= ATIF_PROBE_FAIL; - /* - * we do not respond to probe or request packets of - * this address while we are probing this address - */ - goto unlock; - } - - switch (function) { - case AARP_REPLY: - if (!unresolved_count) /* Speed up */ - break; - - /* Find the entry. */ - a = __aarp_find_entry(unresolved[hash], dev, &sa); - if (!a || dev != a->dev) - break; - - /* We can fill one in - this is good. */ - ether_addr_copy(a->hwaddr, ea->hw_src); - __aarp_resolved(&unresolved[hash], a, hash); - if (!unresolved_count) - mod_timer(&aarp_timer, - jiffies + sysctl_aarp_expiry_time); - break; - - case AARP_REQUEST: - case AARP_PROBE: - - /* - * If it is my address set ma to my address and reply. - * We can treat probe and request the same. Probe - * simply means we shouldn't cache the querying host, - * as in a probe they are proposing an address not - * using one. - * - * Support for proxy-AARP added. We check if the - * address is one of our proxies before we toss the - * packet out. - */ - - sa.s_node = ea->pa_dst_node; - sa.s_net = ea->pa_dst_net; - - /* See if we have a matching proxy. */ - ma = __aarp_proxy_find(dev, &sa); - if (!ma) - ma = &ifa->address; - else { /* We need to make a copy of the entry. */ - da.s_node = sa.s_node; - da.s_net = sa.s_net; - ma = &da; - } - - if (function == AARP_PROBE) { - /* - * A probe implies someone trying to get an - * address. So as a precaution flush any - * entries we have for this address. - */ - a = __aarp_find_entry(resolved[sa.s_node % - (AARP_HASH_SIZE - 1)], - skb->dev, &sa); - - /* - * Make it expire next tick - that avoids us - * getting into a probe/flush/learn/probe/ - * flush/learn cycle during probing of a slow - * to respond host addr. - */ - if (a) { - a->expires_at = jiffies - 1; - mod_timer(&aarp_timer, jiffies + - sysctl_aarp_tick_time); - } - } - - if (sa.s_node != ma->s_node) - break; - - if (sa.s_net && ma->s_net && sa.s_net != ma->s_net) - break; - - sa.s_node = ea->pa_src_node; - sa.s_net = ea->pa_src_net; - - /* aarp_my_address has found the address to use for us. - */ - aarp_send_reply(dev, ma, &sa, ea->hw_src); - break; - } - -unlock: - write_unlock_bh(&aarp_lock); -out1: - ret = 1; -out0: - kfree_skb(skb); - return ret; -} - -static struct notifier_block aarp_notifier = { - .notifier_call = aarp_device_event, -}; - -static unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 }; - -int __init aarp_proto_init(void) -{ - int rc; - - aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv); - if (!aarp_dl) { - printk(KERN_CRIT "Unable to register AARP with SNAP.\n"); - return -ENOMEM; - } - timer_setup(&aarp_timer, aarp_expire_timeout, 0); - aarp_timer.expires = jiffies + sysctl_aarp_expiry_time; - add_timer(&aarp_timer); - rc = register_netdevice_notifier(&aarp_notifier); - if (rc) { - timer_delete_sync(&aarp_timer); - unregister_snap_client(aarp_dl); - } - return rc; -} - -/* Remove the AARP entries associated with a device. */ -void aarp_device_down(struct net_device *dev) -{ - int ct; - - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_device(&resolved[ct], dev); - __aarp_expire_device(&unresolved[ct], dev); - __aarp_expire_device(&proxies[ct], dev); - } - - write_unlock_bh(&aarp_lock); -} - -#ifdef CONFIG_PROC_FS -/* - * Get the aarp entry that is in the chain described - * by the iterator. - * If pos is set then skip till that index. - * pos = 1 is the first entry - */ -static struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos) -{ - int ct = iter->bucket; - struct aarp_entry **table = iter->table; - loff_t off = 0; - struct aarp_entry *entry; - - rescan: - while (ct < AARP_HASH_SIZE) { - for (entry = table[ct]; entry; entry = entry->next) { - if (!pos || ++off == *pos) { - iter->table = table; - iter->bucket = ct; - return entry; - } - } - ++ct; - } - - if (table == resolved) { - ct = 0; - table = unresolved; - goto rescan; - } - if (table == unresolved) { - ct = 0; - table = proxies; - goto rescan; - } - return NULL; -} - -static void *aarp_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(aarp_lock) -{ - struct aarp_iter_state *iter = seq->private; - - read_lock_bh(&aarp_lock); - iter->table = resolved; - iter->bucket = 0; - - return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN; -} - -static void *aarp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct aarp_entry *entry = v; - struct aarp_iter_state *iter = seq->private; - - ++*pos; - - /* first line after header */ - if (v == SEQ_START_TOKEN) - entry = iter_next(iter, NULL); - - /* next entry in current bucket */ - else if (entry->next) - entry = entry->next; - - /* next bucket or table */ - else { - ++iter->bucket; - entry = iter_next(iter, NULL); - } - return entry; -} - -static void aarp_seq_stop(struct seq_file *seq, void *v) - __releases(aarp_lock) -{ - read_unlock_bh(&aarp_lock); -} - -static const char *dt2str(unsigned long ticks) -{ - static char buf[32]; - - sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100) / HZ); - - return buf; -} - -static int aarp_seq_show(struct seq_file *seq, void *v) -{ - struct aarp_iter_state *iter = seq->private; - struct aarp_entry *entry = v; - unsigned long now = jiffies; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "Address Interface Hardware Address" - " Expires LastSend Retry Status\n"); - else { - seq_printf(seq, "%04X:%02X %-12s", - ntohs(entry->target_addr.s_net), - (unsigned int) entry->target_addr.s_node, - entry->dev ? entry->dev->name : "????"); - seq_printf(seq, "%pM", entry->hwaddr); - seq_printf(seq, " %8s", - dt2str((long)entry->expires_at - (long)now)); - if (iter->table == unresolved) - seq_printf(seq, " %8s %6hu", - dt2str(now - entry->last_sent), - entry->xmit_count); - else - seq_puts(seq, " "); - seq_printf(seq, " %s\n", - (iter->table == resolved) ? "resolved" - : (iter->table == unresolved) ? "unresolved" - : (iter->table == proxies) ? "proxies" - : "unknown"); - } - return 0; -} - -const struct seq_operations aarp_seq_ops = { - .start = aarp_seq_start, - .next = aarp_seq_next, - .stop = aarp_seq_stop, - .show = aarp_seq_show, -}; -#endif - -/* General module cleanup. Called from cleanup_module() in ddp.c. */ -void aarp_cleanup_module(void) -{ - timer_delete_sync(&aarp_timer); - unregister_netdevice_notifier(&aarp_notifier); - unregister_snap_client(aarp_dl); - aarp_purge(); -} diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c deleted file mode 100644 index 01787fb6a7bc..000000000000 --- a/net/appletalk/atalk_proc.c +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * atalk_proc.c - proc support for Appletalk - * - * Copyright(c) Arnaldo Carvalho de Melo - */ - -#include -#include -#include -#include -#include -#include -#include - - -static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) -{ - struct atalk_iface *i; - - for (i = atalk_interfaces; pos && i; i = i->next) - --pos; - - return i; -} - -static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_interfaces_lock) -{ - loff_t l = *pos; - - read_lock_bh(&atalk_interfaces_lock); - return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN; -} - -static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct atalk_iface *i; - - ++*pos; - if (v == SEQ_START_TOKEN) { - i = NULL; - if (atalk_interfaces) - i = atalk_interfaces; - goto out; - } - i = v; - i = i->next; -out: - return i; -} - -static void atalk_seq_interface_stop(struct seq_file *seq, void *v) - __releases(atalk_interfaces_lock) -{ - read_unlock_bh(&atalk_interfaces_lock); -} - -static int atalk_seq_interface_show(struct seq_file *seq, void *v) -{ - struct atalk_iface *iface; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Interface Address Networks " - "Status\n"); - goto out; - } - - iface = v; - seq_printf(seq, "%-16s %04X:%02X %04X-%04X %d\n", - iface->dev->name, ntohs(iface->address.s_net), - iface->address.s_node, ntohs(iface->nets.nr_firstnet), - ntohs(iface->nets.nr_lastnet), iface->status); -out: - return 0; -} - -static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) -{ - struct atalk_route *r; - - for (r = atalk_routes; pos && r; r = r->next) - --pos; - - return r; -} - -static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_routes_lock) -{ - loff_t l = *pos; - - read_lock_bh(&atalk_routes_lock); - return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN; -} - -static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct atalk_route *r; - - ++*pos; - if (v == SEQ_START_TOKEN) { - r = NULL; - if (atalk_routes) - r = atalk_routes; - goto out; - } - r = v; - r = r->next; -out: - return r; -} - -static void atalk_seq_route_stop(struct seq_file *seq, void *v) - __releases(atalk_routes_lock) -{ - read_unlock_bh(&atalk_routes_lock); -} - -static int atalk_seq_route_show(struct seq_file *seq, void *v) -{ - struct atalk_route *rt; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Target Router Flags Dev\n"); - goto out; - } - - if (atrtr_default.dev) { - rt = &atrtr_default; - seq_printf(seq, "Default %04X:%02X %-4d %s\n", - ntohs(rt->gateway.s_net), rt->gateway.s_node, - rt->flags, rt->dev->name); - } - - rt = v; - seq_printf(seq, "%04X:%02X %04X:%02X %-4d %s\n", - ntohs(rt->target.s_net), rt->target.s_node, - ntohs(rt->gateway.s_net), rt->gateway.s_node, - rt->flags, rt->dev->name); -out: - return 0; -} - -static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_sockets_lock) -{ - read_lock_bh(&atalk_sockets_lock); - return seq_hlist_start_head(&atalk_sockets, *pos); -} - -static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &atalk_sockets, pos); -} - -static void atalk_seq_socket_stop(struct seq_file *seq, void *v) - __releases(atalk_sockets_lock) -{ - read_unlock_bh(&atalk_sockets_lock); -} - -static int atalk_seq_socket_show(struct seq_file *seq, void *v) -{ - struct sock *s; - struct atalk_sock *at; - - if (v == SEQ_START_TOKEN) { - seq_printf(seq, "Type Local_addr Remote_addr Tx_queue " - "Rx_queue St UID\n"); - goto out; - } - - s = sk_entry(v); - at = at_sk(s); - - seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " - "%02X %u\n", - s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, - ntohs(at->dest_net), at->dest_node, at->dest_port, - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_state, - from_kuid_munged(seq_user_ns(seq), sk_uid(s))); -out: - return 0; -} - -static const struct seq_operations atalk_seq_interface_ops = { - .start = atalk_seq_interface_start, - .next = atalk_seq_interface_next, - .stop = atalk_seq_interface_stop, - .show = atalk_seq_interface_show, -}; - -static const struct seq_operations atalk_seq_route_ops = { - .start = atalk_seq_route_start, - .next = atalk_seq_route_next, - .stop = atalk_seq_route_stop, - .show = atalk_seq_route_show, -}; - -static const struct seq_operations atalk_seq_socket_ops = { - .start = atalk_seq_socket_start, - .next = atalk_seq_socket_next, - .stop = atalk_seq_socket_stop, - .show = atalk_seq_socket_show, -}; - -int __init atalk_proc_init(void) -{ - if (!proc_mkdir("atalk", init_net.proc_net)) - return -ENOMEM; - - if (!proc_create_seq("atalk/interface", 0444, init_net.proc_net, - &atalk_seq_interface_ops)) - goto out; - - if (!proc_create_seq("atalk/route", 0444, init_net.proc_net, - &atalk_seq_route_ops)) - goto out; - - if (!proc_create_seq("atalk/socket", 0444, init_net.proc_net, - &atalk_seq_socket_ops)) - goto out; - - if (!proc_create_seq_private("atalk/arp", 0444, init_net.proc_net, - &aarp_seq_ops, - sizeof(struct aarp_iter_state), NULL)) - goto out; - - return 0; - -out: - remove_proc_subtree("atalk", init_net.proc_net); - return -ENOMEM; -} - -void atalk_proc_exit(void) -{ - remove_proc_subtree("atalk", init_net.proc_net); -} diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c deleted file mode 100644 index 30a6dc06291c..000000000000 --- a/net/appletalk/ddp.c +++ /dev/null @@ -1,2017 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * DDP: An implementation of the AppleTalk DDP protocol for - * Ethernet 'ELAP'. - * - * Alan Cox - * - * With more than a little assistance from - * - * Wesley Craig - * - * Fixes: - * Neil Horman : Added missing device ioctls - * Michael Callahan : Made routing work - * Wesley Craig : Fix probing to listen to a - * passed node id. - * Alan Cox : Added send/recvmsg support - * Alan Cox : Moved at. to protinfo in - * socket. - * Alan Cox : Added firewall hooks. - * Alan Cox : Supports new ARPHRD_LOOPBACK - * Christer Weinigel : Routing and /proc fixes. - * Bradford Johnson : LocalTalk. - * Tom Dyas : Module support. - * Alan Cox : Hooks for PPP (based on the - * LocalTalk hook). - * Alan Cox : Posix bits - * Alan Cox/Mike Freeman : Possible fix to NBP problems - * Bradford Johnson : IP-over-DDP (experimental) - * Jay Schulist : Moved IP-over-DDP to its own - * driver file. (ipddp.c & ipddp.h) - * Jay Schulist : Made work as module with - * AppleTalk drivers, cleaned it. - * Rob Newberry : Added proxy AARP and AARP - * procfs, moved probing to AARP - * module. - * Adrian Sun/ - * Michael Zuelsdorff : fix for net.0 packets. don't - * allow illegal ether/tokentalk - * port assignment. we lose a - * valid localtalk port as a - * result. - * Arnaldo C. de Melo : Cleanup, in preparation for - * shared skb support 8) - * Arnaldo C. de Melo : Move proc stuff to atalk_proc.c, - * use seq_file - */ - -#include -#include -#include -#include /* For TIOCOUTQ/INQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct datalink_proto *ddp_dl, *aarp_dl; -static const struct proto_ops atalk_dgram_ops; - -/**************************************************************************\ -* * -* Handlers for the socket list. * -* * -\**************************************************************************/ - -HLIST_HEAD(atalk_sockets); -DEFINE_RWLOCK(atalk_sockets_lock); - -static inline void __atalk_insert_socket(struct sock *sk) -{ - sk_add_node(sk, &atalk_sockets); -} - -static inline void atalk_remove_socket(struct sock *sk) -{ - write_lock_bh(&atalk_sockets_lock); - sk_del_node_init(sk); - write_unlock_bh(&atalk_sockets_lock); -} - -static struct sock *atalk_search_socket(struct sockaddr_at *to, - struct atalk_iface *atif) -{ - struct sock *def_socket = NULL; - struct sock *s; - - read_lock_bh(&atalk_sockets_lock); - sk_for_each(s, &atalk_sockets) { - struct atalk_sock *at = at_sk(s); - - if (to->sat_port != at->src_port) - continue; - - if (to->sat_addr.s_net == ATADDR_ANYNET && - to->sat_addr.s_node == ATADDR_BCAST) { - if (atif->address.s_node == at->src_node && - atif->address.s_net == at->src_net) { - /* This socket's address matches the address of the interface - * that received the packet -- use it - */ - goto found; - } - - /* Continue searching for a socket matching the interface address, - * but use this socket by default if no other one is found - */ - def_socket = s; - } - - if (to->sat_addr.s_net == at->src_net && - (to->sat_addr.s_node == at->src_node || - to->sat_addr.s_node == ATADDR_BCAST || - to->sat_addr.s_node == ATADDR_ANYNODE)) - goto found; - - /* XXXX.0 -- we got a request for this router. make sure - * that the node is appropriately set. */ - if (to->sat_addr.s_node == ATADDR_ANYNODE && - to->sat_addr.s_net != ATADDR_ANYNET && - atif->address.s_node == at->src_node) { - to->sat_addr.s_node = atif->address.s_node; - goto found; - } - } - s = def_socket; -found: - read_unlock_bh(&atalk_sockets_lock); - return s; -} - -/** - * atalk_find_or_insert_socket - Try to find a socket matching ADDR - * @sk: socket to insert in the list if it is not there already - * @sat: address to search for - * - * Try to find a socket matching ADDR in the socket list, if found then return - * it. If not, insert SK into the socket list. - * - * This entire operation must execute atomically. - */ -static struct sock *atalk_find_or_insert_socket(struct sock *sk, - struct sockaddr_at *sat) -{ - struct sock *s; - struct atalk_sock *at; - - write_lock_bh(&atalk_sockets_lock); - sk_for_each(s, &atalk_sockets) { - at = at_sk(s); - - if (at->src_net == sat->sat_addr.s_net && - at->src_node == sat->sat_addr.s_node && - at->src_port == sat->sat_port) - goto found; - } - s = NULL; - __atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */ -found: - write_unlock_bh(&atalk_sockets_lock); - return s; -} - -static void atalk_destroy_timer(struct timer_list *t) -{ - struct sock *sk = timer_container_of(sk, t, sk_timer); - - if (sk_has_allocations(sk)) { - sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -static inline void atalk_destroy_socket(struct sock *sk) -{ - atalk_remove_socket(sk); - skb_queue_purge(&sk->sk_receive_queue); - - if (sk_has_allocations(sk)) { - timer_setup(&sk->sk_timer, atalk_destroy_timer, 0); - sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/**************************************************************************\ -* * -* Routing tables for the AppleTalk socket layer. * -* * -\**************************************************************************/ - -/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */ -struct atalk_route *atalk_routes; -DEFINE_RWLOCK(atalk_routes_lock); - -struct atalk_iface *atalk_interfaces; -DEFINE_RWLOCK(atalk_interfaces_lock); - -/* For probing devices or in a routerless network */ -struct atalk_route atrtr_default; - -/* AppleTalk interface control */ -/* - * Drop a device. Doesn't drop any of its routes - that is the caller's - * problem. Called when we down the interface or delete the address. - */ -static void atif_drop_device(struct net_device *dev) -{ - struct atalk_iface **iface = &atalk_interfaces; - struct atalk_iface *tmp; - - write_lock_bh(&atalk_interfaces_lock); - while ((tmp = *iface) != NULL) { - if (tmp->dev == dev) { - *iface = tmp->next; - dev_put(dev); - kfree(tmp); - dev->atalk_ptr = NULL; - } else - iface = &tmp->next; - } - write_unlock_bh(&atalk_interfaces_lock); -} - -static struct atalk_iface *atif_add_device(struct net_device *dev, - struct atalk_addr *sa) -{ - struct atalk_iface *iface = kzalloc_obj(*iface); - - if (!iface) - goto out; - - dev_hold(dev); - iface->dev = dev; - dev->atalk_ptr = iface; - iface->address = *sa; - iface->status = 0; - - write_lock_bh(&atalk_interfaces_lock); - iface->next = atalk_interfaces; - atalk_interfaces = iface; - write_unlock_bh(&atalk_interfaces_lock); -out: - return iface; -} - -/* Perform phase 2 AARP probing on our tentative address */ -static int atif_probe_device(struct atalk_iface *atif) -{ - int netrange = ntohs(atif->nets.nr_lastnet) - - ntohs(atif->nets.nr_firstnet) + 1; - int probe_net = ntohs(atif->address.s_net); - int probe_node = atif->address.s_node; - int netct, nodect; - - /* Offset the network we start probing with */ - if (probe_net == ATADDR_ANYNET) { - probe_net = ntohs(atif->nets.nr_firstnet); - if (netrange) - probe_net += jiffies % netrange; - } - if (probe_node == ATADDR_ANYNODE) - probe_node = jiffies & 0xFF; - - /* Scan the networks */ - atif->status |= ATIF_PROBE; - for (netct = 0; netct <= netrange; netct++) { - /* Sweep the available nodes from a given start */ - atif->address.s_net = htons(probe_net); - for (nodect = 0; nodect < 256; nodect++) { - atif->address.s_node = (nodect + probe_node) & 0xFF; - if (atif->address.s_node > 0 && - atif->address.s_node < 254) { - /* Probe a proposed address */ - aarp_probe_network(atif); - - if (!(atif->status & ATIF_PROBE_FAIL)) { - atif->status &= ~ATIF_PROBE; - return 0; - } - } - atif->status &= ~ATIF_PROBE_FAIL; - } - probe_net++; - if (probe_net > ntohs(atif->nets.nr_lastnet)) - probe_net = ntohs(atif->nets.nr_firstnet); - } - atif->status &= ~ATIF_PROBE; - - return -EADDRINUSE; /* Network is full... */ -} - - -/* Perform AARP probing for a proxy address */ -static int atif_proxy_probe_device(struct atalk_iface *atif, - struct atalk_addr *proxy_addr) -{ - int netrange = ntohs(atif->nets.nr_lastnet) - - ntohs(atif->nets.nr_firstnet) + 1; - /* we probe the interface's network */ - int probe_net = ntohs(atif->address.s_net); - int probe_node = ATADDR_ANYNODE; /* we'll take anything */ - int netct, nodect; - - /* Offset the network we start probing with */ - if (probe_net == ATADDR_ANYNET) { - probe_net = ntohs(atif->nets.nr_firstnet); - if (netrange) - probe_net += jiffies % netrange; - } - - if (probe_node == ATADDR_ANYNODE) - probe_node = jiffies & 0xFF; - - /* Scan the networks */ - for (netct = 0; netct <= netrange; netct++) { - /* Sweep the available nodes from a given start */ - proxy_addr->s_net = htons(probe_net); - for (nodect = 0; nodect < 256; nodect++) { - proxy_addr->s_node = (nodect + probe_node) & 0xFF; - if (proxy_addr->s_node > 0 && - proxy_addr->s_node < 254) { - /* Tell AARP to probe a proposed address */ - int ret = aarp_proxy_probe_network(atif, - proxy_addr); - - if (ret != -EADDRINUSE) - return ret; - } - } - probe_net++; - if (probe_net > ntohs(atif->nets.nr_lastnet)) - probe_net = ntohs(atif->nets.nr_firstnet); - } - - return -EADDRINUSE; /* Network is full... */ -} - - -struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) -{ - struct atalk_iface *iface = dev->atalk_ptr; - return iface ? &iface->address : NULL; -} - -static struct atalk_addr *atalk_find_primary(void) -{ - struct atalk_iface *fiface = NULL; - struct atalk_addr *retval; - struct atalk_iface *iface; - - /* - * Return a point-to-point interface only if - * there is no non-ptp interface available. - */ - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if (!fiface && !(iface->dev->flags & IFF_LOOPBACK)) - fiface = iface; - if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { - retval = &iface->address; - goto out; - } - } - - if (fiface) - retval = &fiface->address; - else if (atalk_interfaces) - retval = &atalk_interfaces->address; - else - retval = NULL; -out: - read_unlock_bh(&atalk_interfaces_lock); - return retval; -} - -/* - * Find a match for 'any network' - ie any of our interfaces with that - * node number will do just nicely. - */ -static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) -{ - struct atalk_iface *iface = dev->atalk_ptr; - - if (!iface || iface->status & ATIF_PROBE) - goto out_err; - - if (node != ATADDR_BCAST && - iface->address.s_node != node && - node != ATADDR_ANYNODE) - goto out_err; -out: - return iface; -out_err: - iface = NULL; - goto out; -} - -/* Find a match for a specific network:node pair */ -static struct atalk_iface *atalk_find_interface(__be16 net, int node) -{ - struct atalk_iface *iface; - - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if ((node == ATADDR_BCAST || - node == ATADDR_ANYNODE || - iface->address.s_node == node) && - iface->address.s_net == net && - !(iface->status & ATIF_PROBE)) - break; - - /* XXXX.0 -- net.0 returns the iface associated with net */ - if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET && - ntohs(iface->nets.nr_firstnet) <= ntohs(net) && - ntohs(net) <= ntohs(iface->nets.nr_lastnet)) - break; - } - read_unlock_bh(&atalk_interfaces_lock); - return iface; -} - - -/* - * Find a route for an AppleTalk packet. This ought to get cached in - * the socket (later on...). We know about host routes and the fact - * that a route must be direct to broadcast. - */ -static struct atalk_route *atrtr_find(struct atalk_addr *target) -{ - /* - * we must search through all routes unless we find a - * host route, because some host routes might overlap - * network routes - */ - struct atalk_route *net_route = NULL; - struct atalk_route *r; - - read_lock_bh(&atalk_routes_lock); - for (r = atalk_routes; r; r = r->next) { - if (!(r->flags & RTF_UP)) - continue; - - if (r->target.s_net == target->s_net) { - if (r->flags & RTF_HOST) { - /* - * if this host route is for the target, - * the we're done - */ - if (r->target.s_node == target->s_node) - goto out; - } else - /* - * this route will work if there isn't a - * direct host route, so cache it - */ - net_route = r; - } - } - - /* - * if we found a network route but not a direct host - * route, then return it - */ - if (net_route) - r = net_route; - else if (atrtr_default.dev) - r = &atrtr_default; - else /* No route can be found */ - r = NULL; -out: - read_unlock_bh(&atalk_routes_lock); - return r; -} - - -/* - * Given an AppleTalk network, find the device to use. This can be - * a simple lookup. - */ -struct net_device *atrtr_get_dev(struct atalk_addr *sa) -{ - struct atalk_route *atr = atrtr_find(sa); - return atr ? atr->dev : NULL; -} - -/* Set up a default router */ -static void atrtr_set_default(struct net_device *dev) -{ - atrtr_default.dev = dev; - atrtr_default.flags = RTF_UP; - atrtr_default.gateway.s_net = htons(0); - atrtr_default.gateway.s_node = 0; -} - -/* - * Add a router. Basically make sure it looks valid and stuff the - * entry in the list. While it uses netranges we always set them to one - * entry to work like netatalk. - */ -static int atrtr_create(struct rtentry *r, struct net_device *devhint) -{ - struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst; - struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway; - struct atalk_route *rt; - struct atalk_iface *iface, *riface; - int retval = -EINVAL; - - /* - * Fixme: Raise/Lower a routing change semaphore for these - * operations. - */ - - /* Validate the request */ - if (ta->sat_family != AF_APPLETALK || - (!devhint && ga->sat_family != AF_APPLETALK)) - goto out; - - /* Now walk the routing table and make our decisions */ - write_lock_bh(&atalk_routes_lock); - for (rt = atalk_routes; rt; rt = rt->next) { - if (r->rt_flags != rt->flags) - continue; - - if (ta->sat_addr.s_net == rt->target.s_net) { - if (!(rt->flags & RTF_HOST)) - break; - if (ta->sat_addr.s_node == rt->target.s_node) - break; - } - } - - if (!devhint) { - riface = NULL; - - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if (!riface && - ntohs(ga->sat_addr.s_net) >= - ntohs(iface->nets.nr_firstnet) && - ntohs(ga->sat_addr.s_net) <= - ntohs(iface->nets.nr_lastnet)) - riface = iface; - - if (ga->sat_addr.s_net == iface->address.s_net && - ga->sat_addr.s_node == iface->address.s_node) - riface = iface; - } - read_unlock_bh(&atalk_interfaces_lock); - - retval = -ENETUNREACH; - if (!riface) - goto out_unlock; - - devhint = riface->dev; - } - - if (!rt) { - rt = kzalloc_obj(*rt, GFP_ATOMIC); - - retval = -ENOBUFS; - if (!rt) - goto out_unlock; - - rt->next = atalk_routes; - atalk_routes = rt; - } - - /* Fill in the routing entry */ - rt->target = ta->sat_addr; - dev_put(rt->dev); /* Release old device */ - dev_hold(devhint); - rt->dev = devhint; - rt->flags = r->rt_flags; - rt->gateway = ga->sat_addr; - - retval = 0; -out_unlock: - write_unlock_bh(&atalk_routes_lock); -out: - return retval; -} - -/* Delete a route. Find it and discard it */ -static int atrtr_delete(struct atalk_addr *addr) -{ - struct atalk_route **r = &atalk_routes; - int retval = 0; - struct atalk_route *tmp; - - write_lock_bh(&atalk_routes_lock); - while ((tmp = *r) != NULL) { - if (tmp->target.s_net == addr->s_net && - (!(tmp->flags&RTF_GATEWAY) || - tmp->target.s_node == addr->s_node)) { - *r = tmp->next; - dev_put(tmp->dev); - kfree(tmp); - goto out; - } - r = &tmp->next; - } - retval = -ENOENT; -out: - write_unlock_bh(&atalk_routes_lock); - return retval; -} - -/* - * Called when a device is downed. Just throw away any routes - * via it. - */ -static void atrtr_device_down(struct net_device *dev) -{ - struct atalk_route **r = &atalk_routes; - struct atalk_route *tmp; - - write_lock_bh(&atalk_routes_lock); - while ((tmp = *r) != NULL) { - if (tmp->dev == dev) { - *r = tmp->next; - dev_put(dev); - kfree(tmp); - } else - r = &tmp->next; - } - write_unlock_bh(&atalk_routes_lock); - - if (atrtr_default.dev == dev) - atrtr_set_default(NULL); -} - -/* Actually down the interface */ -static inline void atalk_dev_down(struct net_device *dev) -{ - atrtr_device_down(dev); /* Remove all routes for the device */ - aarp_device_down(dev); /* Remove AARP entries for the device */ - atif_drop_device(dev); /* Remove the device */ -} - -/* - * A device event has occurred. Watch for devices going down and - * delete our use of them (iface and route). - */ -static int ddp_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_DOWN) - /* Discard any use of this */ - atalk_dev_down(dev); - - return NOTIFY_DONE; -} - -/* ioctl calls. Shouldn't even need touching */ -/* Device configuration ioctl calls */ -static int atif_ioctl(int cmd, void __user *arg) -{ - static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF }; - struct ifreq atreq; - struct atalk_netrange *nr; - struct sockaddr_at *sa; - struct net_device *dev; - struct atalk_iface *atif; - int ct; - int limit; - struct rtentry rtdef; - int add_route; - - if (get_user_ifreq(&atreq, NULL, arg)) - return -EFAULT; - - dev = __dev_get_by_name(&init_net, atreq.ifr_name); - if (!dev) - return -ENODEV; - - sa = (struct sockaddr_at *)&atreq.ifr_addr; - atif = atalk_find_dev(dev); - - switch (cmd) { - case SIOCSIFADDR: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - if (dev->type != ARPHRD_ETHER && - dev->type != ARPHRD_LOOPBACK && - dev->type != ARPHRD_LOCALTLK && - dev->type != ARPHRD_PPP) - return -EPROTONOSUPPORT; - - nr = (struct atalk_netrange *)&sa->sat_zero[0]; - add_route = 1; - - /* - * if this is a point-to-point iface, and we already - * have an iface for this AppleTalk address, then we - * should not add a route - */ - if ((dev->flags & IFF_POINTOPOINT) && - atalk_find_interface(sa->sat_addr.s_net, - sa->sat_addr.s_node)) { - printk(KERN_DEBUG "AppleTalk: point-to-point " - "interface added with " - "existing address\n"); - add_route = 0; - } - - /* - * Phase 1 is fine on LocalTalk but we don't do - * EtherTalk phase 1. Anyone wanting to add it, go ahead. - */ - if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) - return -EPROTONOSUPPORT; - if (sa->sat_addr.s_node == ATADDR_BCAST || - sa->sat_addr.s_node == 254) - return -EINVAL; - if (atif) { - /* Already setting address */ - if (atif->status & ATIF_PROBE) - return -EBUSY; - - atif->address.s_net = sa->sat_addr.s_net; - atif->address.s_node = sa->sat_addr.s_node; - atrtr_device_down(dev); /* Flush old routes */ - } else { - atif = atif_add_device(dev, &sa->sat_addr); - if (!atif) - return -ENOMEM; - } - atif->nets = *nr; - - /* - * Check if the chosen address is used. If so we - * error and atalkd will try another. - */ - - if (!(dev->flags & IFF_LOOPBACK) && - !(dev->flags & IFF_POINTOPOINT) && - atif_probe_device(atif) < 0) { - atif_drop_device(dev); - return -EADDRINUSE; - } - - /* Hey it worked - add the direct routes */ - sa = (struct sockaddr_at *)&rtdef.rt_gateway; - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_net = atif->address.s_net; - sa->sat_addr.s_node = atif->address.s_node; - sa = (struct sockaddr_at *)&rtdef.rt_dst; - rtdef.rt_flags = RTF_UP; - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_node = ATADDR_ANYNODE; - if (dev->flags & IFF_LOOPBACK || - dev->flags & IFF_POINTOPOINT) - rtdef.rt_flags |= RTF_HOST; - - /* Routerless initial state */ - if (nr->nr_firstnet == htons(0) && - nr->nr_lastnet == htons(0xFFFE)) { - sa->sat_addr.s_net = atif->address.s_net; - atrtr_create(&rtdef, dev); - atrtr_set_default(dev); - } else { - limit = ntohs(nr->nr_lastnet); - if (limit - ntohs(nr->nr_firstnet) > 4096) { - printk(KERN_WARNING "Too many routes/" - "iface.\n"); - return -EINVAL; - } - if (add_route) - for (ct = ntohs(nr->nr_firstnet); - ct <= limit; ct++) { - sa->sat_addr.s_net = htons(ct); - atrtr_create(&rtdef, dev); - } - } - dev_mc_add_global(dev, aarp_mcast); - return 0; - - case SIOCGIFADDR: - if (!atif) - return -EADDRNOTAVAIL; - - sa->sat_family = AF_APPLETALK; - sa->sat_addr = atif->address; - break; - - case SIOCGIFBRDADDR: - if (!atif) - return -EADDRNOTAVAIL; - - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_net = atif->address.s_net; - sa->sat_addr.s_node = ATADDR_BCAST; - break; - - case SIOCATALKDIFADDR: - case SIOCDIFADDR: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - atalk_dev_down(dev); - break; - - case SIOCSARP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - /* - * for now, we only support proxy AARP on ELAP; - * we should be able to do it for LocalTalk, too. - */ - if (dev->type != ARPHRD_ETHER) - return -EPROTONOSUPPORT; - - /* - * atif points to the current interface on this network; - * we aren't concerned about its current status (at - * least for now), but it has all the settings about - * the network we're going to probe. Consequently, it - * must exist. - */ - if (!atif) - return -EADDRNOTAVAIL; - - nr = (struct atalk_netrange *)&(atif->nets); - /* - * Phase 1 is fine on Localtalk but we don't do - * Ethertalk phase 1. Anyone wanting to add it, go ahead. - */ - if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) - return -EPROTONOSUPPORT; - - if (sa->sat_addr.s_node == ATADDR_BCAST || - sa->sat_addr.s_node == 254) - return -EINVAL; - - /* - * Check if the chosen address is used. If so we - * error and ATCP will try another. - */ - if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0) - return -EADDRINUSE; - - /* - * We now have an address on the local network, and - * the AARP code will defend it for us until we take it - * down. We don't set up any routes right now, because - * ATCP will install them manually via SIOCADDRT. - */ - break; - - case SIOCDARP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - if (!atif) - return -EADDRNOTAVAIL; - - /* give to aarp module to remove proxy entry */ - aarp_proxy_remove(atif->dev, &(sa->sat_addr)); - return 0; - } - - return put_user_ifreq(&atreq, arg); -} - -static int atrtr_ioctl_addrt(struct rtentry *rt) -{ - struct net_device *dev = NULL; - - if (rt->rt_dev) { - char name[IFNAMSIZ]; - - if (copy_from_user(name, rt->rt_dev, IFNAMSIZ-1)) - return -EFAULT; - name[IFNAMSIZ-1] = '\0'; - - dev = __dev_get_by_name(&init_net, name); - if (!dev) - return -ENODEV; - } - return atrtr_create(rt, dev); -} - -/* Routing ioctl() calls */ -static int atrtr_ioctl(unsigned int cmd, void __user *arg) -{ - struct rtentry rt; - - if (copy_from_user(&rt, arg, sizeof(rt))) - return -EFAULT; - - switch (cmd) { - case SIOCDELRT: - if (rt.rt_dst.sa_family != AF_APPLETALK) - return -EINVAL; - return atrtr_delete(&((struct sockaddr_at *) - &rt.rt_dst)->sat_addr); - - case SIOCADDRT: - return atrtr_ioctl_addrt(&rt); - } - return -EINVAL; -} - -/**************************************************************************\ -* * -* Handling for system calls applied via the various interfaces to an * -* AppleTalk socket object. * -* * -\**************************************************************************/ - -/* - * Checksum: This is 'optional'. It's quite likely also a good - * candidate for assembler hackery 8) - */ -static unsigned long atalk_sum_partial(const unsigned char *data, - int len, unsigned long sum) -{ - /* This ought to be unwrapped neatly. I'll trust gcc for now */ - while (len--) { - sum += *data++; - sum = rol16(sum, 1); - } - return sum; -} - -/* Checksum skb data -- similar to skb_checksum */ -static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset, - int len, unsigned long sum) -{ - int start = skb_headlen(skb); - struct sk_buff *frag_iter; - int i, copy; - - /* checksum stuff in header space */ - if ((copy = start - offset) > 0) { - if (copy > len) - copy = len; - sum = atalk_sum_partial(skb->data + offset, copy, sum); - if ((len -= copy) == 0) - return sum; - - offset += copy; - } - - /* checksum stuff in frags */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - int end; - const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - WARN_ON(start > offset + len); - - end = start + skb_frag_size(frag); - if ((copy = end - offset) > 0) { - u8 *vaddr; - - if (copy > len) - copy = len; - vaddr = kmap_atomic(skb_frag_page(frag)); - sum = atalk_sum_partial(vaddr + skb_frag_off(frag) + - offset - start, copy, sum); - kunmap_atomic(vaddr); - - if (!(len -= copy)) - return sum; - offset += copy; - } - start = end; - } - - skb_walk_frags(skb, frag_iter) { - int end; - - WARN_ON(start > offset + len); - - end = start + frag_iter->len; - if ((copy = end - offset) > 0) { - if (copy > len) - copy = len; - sum = atalk_sum_skb(frag_iter, offset - start, - copy, sum); - if ((len -= copy) == 0) - return sum; - offset += copy; - } - start = end; - } - - BUG_ON(len > 0); - - return sum; -} - -static __be16 atalk_checksum(const struct sk_buff *skb, int len) -{ - unsigned long sum; - - /* skip header 4 bytes */ - sum = atalk_sum_skb(skb, 4, len-4, 0); - - /* Use 0xFFFF for 0. 0 itself means none */ - return sum ? htons((unsigned short)sum) : htons(0xFFFF); -} - -static struct proto ddp_proto = { - .name = "DDP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct atalk_sock), -}; - -/* - * Create a socket. Initialise the socket, blank the addresses - * set the state. - */ -static int atalk_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - int rc = -ESOCKTNOSUPPORT; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - /* - * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do - * and gives you the full ELAP frame. Should be handy for CAP 8) - */ - if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) - goto out; - - rc = -EPERM; - if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) - goto out; - - rc = -ENOMEM; - sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto, kern); - if (!sk) - goto out; - rc = 0; - sock->ops = &atalk_dgram_ops; - sock_init_data(sock, sk); - - /* Checksums on by default */ - sock_set_flag(sk, SOCK_ZAPPED); -out: - return rc; -} - -/* Free a socket. No work needed */ -static int atalk_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (sk) { - sock_hold(sk); - lock_sock(sk); - - sock_orphan(sk); - sock->sk = NULL; - atalk_destroy_socket(sk); - - release_sock(sk); - sock_put(sk); - } - return 0; -} - -/** - * atalk_pick_and_bind_port - Pick a source port when one is not given - * @sk: socket to insert into the tables - * @sat: address to search for - * - * Pick a source port when one is not given. If we can find a suitable free - * one, we insert the socket into the tables using it. - * - * This whole operation must be atomic. - */ -static int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat) -{ - int retval; - - write_lock_bh(&atalk_sockets_lock); - - for (sat->sat_port = ATPORT_RESERVED; - sat->sat_port < ATPORT_LAST; - sat->sat_port++) { - struct sock *s; - - sk_for_each(s, &atalk_sockets) { - struct atalk_sock *at = at_sk(s); - - if (at->src_net == sat->sat_addr.s_net && - at->src_node == sat->sat_addr.s_node && - at->src_port == sat->sat_port) - goto try_next_port; - } - - /* Wheee, it's free, assign and insert. */ - __atalk_insert_socket(sk); - at_sk(sk)->src_port = sat->sat_port; - retval = 0; - goto out; - -try_next_port:; - } - - retval = -EBUSY; -out: - write_unlock_bh(&atalk_sockets_lock); - return retval; -} - -static int atalk_autobind(struct sock *sk) -{ - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at sat; - struct atalk_addr *ap = atalk_find_primary(); - int n = -EADDRNOTAVAIL; - - if (!ap || ap->s_net == htons(ATADDR_ANYNET)) - goto out; - - at->src_net = sat.sat_addr.s_net = ap->s_net; - at->src_node = sat.sat_addr.s_node = ap->s_node; - - n = atalk_pick_and_bind_port(sk, &sat); - if (!n) - sock_reset_flag(sk, SOCK_ZAPPED); -out: - return n; -} - -/* Set the address 'our end' of the connection */ -static int atalk_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) -{ - struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - int err; - - if (!sock_flag(sk, SOCK_ZAPPED) || - addr_len != sizeof(struct sockaddr_at)) - return -EINVAL; - - if (addr->sat_family != AF_APPLETALK) - return -EAFNOSUPPORT; - - lock_sock(sk); - if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) { - struct atalk_addr *ap = atalk_find_primary(); - - err = -EADDRNOTAVAIL; - if (!ap) - goto out; - - at->src_net = addr->sat_addr.s_net = ap->s_net; - at->src_node = addr->sat_addr.s_node = ap->s_node; - } else { - err = -EADDRNOTAVAIL; - if (!atalk_find_interface(addr->sat_addr.s_net, - addr->sat_addr.s_node)) - goto out; - - at->src_net = addr->sat_addr.s_net; - at->src_node = addr->sat_addr.s_node; - } - - if (addr->sat_port == ATADDR_ANYPORT) { - err = atalk_pick_and_bind_port(sk, addr); - - if (err < 0) - goto out; - } else { - at->src_port = addr->sat_port; - - err = -EADDRINUSE; - if (atalk_find_or_insert_socket(sk, addr)) - goto out; - } - - sock_reset_flag(sk, SOCK_ZAPPED); - err = 0; -out: - release_sock(sk); - return err; -} - -/* Set the address we talk to */ -static int atalk_connect(struct socket *sock, struct sockaddr_unsized *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at *addr; - int err; - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if (addr_len != sizeof(*addr)) - return -EINVAL; - - addr = (struct sockaddr_at *)uaddr; - - if (addr->sat_family != AF_APPLETALK) - return -EAFNOSUPPORT; - - if (addr->sat_addr.s_node == ATADDR_BCAST && - !sock_flag(sk, SOCK_BROADCAST)) { -#if 1 - pr_warn("atalk_connect: %s is broken and did not set SO_BROADCAST.\n", - current->comm); -#else - return -EACCES; -#endif - } - - lock_sock(sk); - err = -EBUSY; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - err = -ENETUNREACH; - if (!atrtr_get_dev(&addr->sat_addr)) - goto out; - - at->dest_port = addr->sat_port; - at->dest_net = addr->sat_addr.s_net; - at->dest_node = addr->sat_addr.s_node; - - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - err = 0; -out: - release_sock(sk); - return err; -} - -/* - * Find the name of an AppleTalk socket. Just copy the right - * fields into the sockaddr. - */ -static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, - int peer) -{ - struct sockaddr_at sat; - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - int err; - - lock_sock(sk); - err = -ENOBUFS; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - memset(&sat, 0, sizeof(sat)); - - if (peer) { - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - sat.sat_addr.s_net = at->dest_net; - sat.sat_addr.s_node = at->dest_node; - sat.sat_port = at->dest_port; - } else { - sat.sat_addr.s_net = at->src_net; - sat.sat_addr.s_node = at->src_node; - sat.sat_port = at->src_port; - } - - sat.sat_family = AF_APPLETALK; - memcpy(uaddr, &sat, sizeof(sat)); - err = sizeof(struct sockaddr_at); - -out: - release_sock(sk); - return err; -} - -static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, - struct ddpehdr *ddp, __u16 len_hops, int origlen) -{ - struct atalk_route *rt; - struct atalk_addr ta; - - /* - * Don't route multicast, etc., packets, or packets sent to "this - * network" - */ - if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) { - /* - * FIXME: - * - * Can it ever happen that a packet is from a PPP iface and - * needs to be broadcast onto the default network? - */ - if (dev->type == ARPHRD_PPP) - printk(KERN_DEBUG "AppleTalk: didn't forward broadcast " - "packet received from PPP iface\n"); - goto free_it; - } - - ta.s_net = ddp->deh_dnet; - ta.s_node = ddp->deh_dnode; - - /* Route the packet */ - rt = atrtr_find(&ta); - /* increment hops count */ - len_hops += 1 << 10; - if (!rt || !(len_hops & (15 << 10))) - goto free_it; - - /* FIXME: use skb->cb to be able to use shared skbs */ - - /* - * Route goes through another gateway, so set the target to the - * gateway instead. - */ - - if (rt->flags & RTF_GATEWAY) { - ta.s_net = rt->gateway.s_net; - ta.s_node = rt->gateway.s_node; - } - - /* Fix up skb->len field */ - skb_trim(skb, min_t(unsigned int, origlen, - (rt->dev->hard_header_len + - ddp_dl->header_length + (len_hops & 1023)))); - - /* FIXME: use skb->cb to be able to use shared skbs */ - ddp->deh_len_hops = htons(len_hops); - - /* - * Send the buffer onwards - * - * Now we must always be careful. If it's come from LocalTalk to - * EtherTalk it might not fit - * - * Order matters here: If a packet has to be copied to make a new - * headroom (rare hopefully) then it won't need unsharing. - * - * Note. ddp-> becomes invalid at the realloc. - */ - if (skb_headroom(skb) < 22) { - /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ - struct sk_buff *nskb = skb_realloc_headroom(skb, 32); - kfree_skb(skb); - skb = nskb; - } else - skb = skb_unshare(skb, GFP_ATOMIC); - - /* - * If the buffer didn't vanish into the lack of space bitbucket we can - * send it. - */ - if (skb == NULL) - goto drop; - - if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP) - return NET_RX_DROP; - return NET_RX_SUCCESS; -free_it: - kfree_skb(skb); -drop: - return NET_RX_DROP; -} - -/** - * atalk_rcv - Receive a packet (in skb) from device dev - * @skb: packet received - * @dev: network device where the packet comes from - * @pt: packet type - * @orig_dev: the original receive net device - * - * Receive a packet (in skb) from device dev. This has come from the SNAP - * decoder, and on entry skb->transport_header is the DDP header, skb->len - * is the DDP header, skb->len is the DDP length. The physical headers - * have been extracted. PPP should probably pass frames marked as for this - * layer. [ie ARPHRD_ETHERTALK] - */ -static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct ddpehdr *ddp; - struct sock *sock; - struct atalk_iface *atif; - struct sockaddr_at tosat; - int origlen; - __u16 len_hops; - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - /* Don't mangle buffer if shared */ - if (!(skb = skb_share_check(skb, GFP_ATOMIC))) - goto out; - - /* Size check and make sure header is contiguous */ - if (!pskb_may_pull(skb, sizeof(*ddp))) - goto drop; - - ddp = ddp_hdr(skb); - - len_hops = ntohs(ddp->deh_len_hops); - - /* Trim buffer in case of stray trailing data */ - origlen = skb->len; - skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023)); - - /* - * Size check to see if ddp->deh_len was crap - * (Otherwise we'll detonate most spectacularly - * in the middle of atalk_checksum() or recvmsg()). - */ - if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { - pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " - "skb->len=%u)\n", len_hops & 1023, skb->len); - goto drop; - } - - /* - * Any checksums. Note we don't do htons() on this == is assumed to be - * valid for net byte orders all over the networking code... - */ - if (ddp->deh_sum && - atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) - /* Not a valid AppleTalk frame - dustbin time */ - goto drop; - - /* Check the packet is aimed at us */ - if (!ddp->deh_dnet) /* Net 0 is 'this network' */ - atif = atalk_find_anynet(ddp->deh_dnode, dev); - else - atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode); - - if (!atif) { - /* Not ours, so we route the packet via the correct - * AppleTalk iface - */ - return atalk_route_packet(skb, dev, ddp, len_hops, origlen); - } - - /* - * Which socket - atalk_search_socket() looks for a *full match* - * of the tuple. - */ - tosat.sat_addr.s_net = ddp->deh_dnet; - tosat.sat_addr.s_node = ddp->deh_dnode; - tosat.sat_port = ddp->deh_dport; - - sock = atalk_search_socket(&tosat, atif); - if (!sock) /* But not one of our sockets */ - goto drop; - - /* Queue packet (standard) */ - if (sock_queue_rcv_skb(sock, skb) < 0) - goto drop; - - return NET_RX_SUCCESS; - -drop: - kfree_skb(skb); -out: - return NET_RX_DROP; - -} - -/* - * Receive a LocalTalk frame. We make some demands on the caller here. - * Caller must provide enough headroom on the packet to pull the short - * header and append a long one. - */ -static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - if (!net_eq(dev_net(dev), &init_net)) - goto freeit; - - /* Expand any short form frames */ - if (skb_mac_header(skb)[2] == 1) { - struct ddpehdr *ddp; - /* Find our address */ - struct atalk_addr *ap = atalk_find_dev_addr(dev); - - if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) - goto freeit; - - /* Don't mangle buffer if shared */ - if (!(skb = skb_share_check(skb, GFP_ATOMIC))) - return 0; - - /* - * The push leaves us with a ddephdr not an shdr, and - * handily the port bytes in the right place preset. - */ - ddp = skb_push(skb, sizeof(*ddp) - 4); - - /* Now fill in the long header */ - - /* - * These two first. The mac overlays the new source/dest - * network information so we MUST copy these before - * we write the network numbers ! - */ - - ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ - ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ - - ddp->deh_dnet = ap->s_net; /* Network number */ - ddp->deh_snet = ap->s_net; - ddp->deh_sum = 0; /* No checksum */ - /* - * Not sure about this bit... - */ - /* Non routable, so force a drop if we slip up later */ - ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); - } - skb_reset_transport_header(skb); - - return atalk_rcv(skb, dev, pt, orig_dev); -freeit: - kfree_skb(skb); - return 0; -} - -static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - DECLARE_SOCKADDR(struct sockaddr_at *, usat, msg->msg_name); - int flags = msg->msg_flags; - int loopback = 0; - struct sockaddr_at local_satalk, gsat; - struct sk_buff *skb; - struct net_device *dev; - struct ddpehdr *ddp; - int size, hard_header_len; - struct atalk_route *rt, *rt_lo = NULL; - int err; - - if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - if (len > DDP_MAXSZ) - return -EMSGSIZE; - - lock_sock(sk); - if (usat) { - err = -EBUSY; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - err = -EINVAL; - if (msg->msg_namelen < sizeof(*usat) || - usat->sat_family != AF_APPLETALK) - goto out; - - err = -EPERM; - /* netatalk didn't implement this check */ - if (usat->sat_addr.s_node == ATADDR_BCAST && - !sock_flag(sk, SOCK_BROADCAST)) { - goto out; - } - } else { - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - usat = &local_satalk; - usat->sat_family = AF_APPLETALK; - usat->sat_port = at->dest_port; - usat->sat_addr.s_node = at->dest_node; - usat->sat_addr.s_net = at->dest_net; - } - - /* Build a packet */ - net_dbg_ratelimited("SK %p: Got address.\n", sk); - - /* For headers */ - size = sizeof(struct ddpehdr) + len + ddp_dl->header_length; - - if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) { - rt = atrtr_find(&usat->sat_addr); - } else { - struct atalk_addr at_hint; - - at_hint.s_node = 0; - at_hint.s_net = at->src_net; - - rt = atrtr_find(&at_hint); - } - err = -ENETUNREACH; - if (!rt) - goto out; - - dev = rt->dev; - - net_dbg_ratelimited("SK %p: Size needed %d, device %s\n", - sk, size, dev->name); - - hard_header_len = dev->hard_header_len; - /* Leave room for loopback hardware header if necessary */ - if (usat->sat_addr.s_node == ATADDR_BCAST && - (dev->flags & IFF_LOOPBACK || !(rt->flags & RTF_GATEWAY))) { - struct atalk_addr at_lo; - - at_lo.s_node = 0; - at_lo.s_net = 0; - - rt_lo = atrtr_find(&at_lo); - - if (rt_lo && rt_lo->dev->hard_header_len > hard_header_len) - hard_header_len = rt_lo->dev->hard_header_len; - } - - size += hard_header_len; - release_sock(sk); - skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err); - lock_sock(sk); - if (!skb) - goto out; - - skb_reserve(skb, ddp_dl->header_length); - skb_reserve(skb, hard_header_len); - skb->dev = dev; - - net_dbg_ratelimited("SK %p: Begin build.\n", sk); - - ddp = skb_put(skb, sizeof(struct ddpehdr)); - ddp->deh_len_hops = htons(len + sizeof(*ddp)); - ddp->deh_dnet = usat->sat_addr.s_net; - ddp->deh_snet = at->src_net; - ddp->deh_dnode = usat->sat_addr.s_node; - ddp->deh_snode = at->src_node; - ddp->deh_dport = usat->sat_port; - ddp->deh_sport = at->src_port; - - net_dbg_ratelimited("SK %p: Copy user data (%zd bytes).\n", sk, len); - - err = memcpy_from_msg(skb_put(skb, len), msg, len); - if (err) { - kfree_skb(skb); - err = -EFAULT; - goto out; - } - - if (sk->sk_no_check_tx) - ddp->deh_sum = 0; - else - ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); - - /* - * Loopback broadcast packets to non gateway targets (ie routes - * to group we are in) - */ - if (ddp->deh_dnode == ATADDR_BCAST && - !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) { - struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL); - - if (skb2) { - loopback = 1; - net_dbg_ratelimited("SK %p: send out(copy).\n", sk); - /* - * If it fails it is queued/sent above in the aarp queue - */ - aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL); - } - } - - if (dev->flags & IFF_LOOPBACK || loopback) { - net_dbg_ratelimited("SK %p: Loop back.\n", sk); - /* loop back */ - skb_orphan(skb); - if (ddp->deh_dnode == ATADDR_BCAST) { - if (!rt_lo) { - kfree_skb(skb); - err = -ENETUNREACH; - goto out; - } - dev = rt_lo->dev; - skb->dev = dev; - } - ddp_dl->request(ddp_dl, skb, dev->dev_addr); - } else { - net_dbg_ratelimited("SK %p: send out.\n", sk); - if (rt->flags & RTF_GATEWAY) { - gsat.sat_addr = rt->gateway; - usat = &gsat; - } - - /* - * If it fails it is queued/sent above in the aarp queue - */ - aarp_send_ddp(dev, skb, &usat->sat_addr, NULL); - } - net_dbg_ratelimited("SK %p: Done write (%zd).\n", sk, len); - -out: - release_sock(sk); - return err ? : len; -} - -static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, - int flags) -{ - struct sock *sk = sock->sk; - struct ddpehdr *ddp; - int copied = 0; - int offset = 0; - int err = 0; - struct sk_buff *skb; - - skb = skb_recv_datagram(sk, flags, &err); - lock_sock(sk); - - if (!skb) - goto out; - - /* FIXME: use skb->cb to be able to use shared skbs */ - ddp = ddp_hdr(skb); - copied = ntohs(ddp->deh_len_hops) & 1023; - - if (sk->sk_type != SOCK_RAW) { - offset = sizeof(*ddp); - copied -= offset; - } - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - err = skb_copy_datagram_msg(skb, offset, msg, copied); - - if (!err && msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_at *, sat, msg->msg_name); - sat->sat_family = AF_APPLETALK; - sat->sat_port = ddp->deh_sport; - sat->sat_addr.s_node = ddp->deh_snode; - sat->sat_addr.s_net = ddp->deh_snet; - msg->msg_namelen = sizeof(*sat); - } - - skb_free_datagram(sk, skb); /* Free the datagram. */ - -out: - release_sock(sk); - return err ? : copied; -} - - -/* - * AppleTalk ioctl calls. - */ -static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int rc = -ENOIOCTLCMD; - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - - switch (cmd) { - /* Protocol layer */ - case TIOCOUTQ: { - long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - - if (amount < 0) - amount = 0; - rc = put_user(amount, (int __user *)argp); - break; - } - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0; - - spin_lock_irq(&sk->sk_receive_queue.lock); - skb = skb_peek(&sk->sk_receive_queue); - if (skb) - amount = skb->len - sizeof(struct ddpehdr); - spin_unlock_irq(&sk->sk_receive_queue.lock); - rc = put_user(amount, (int __user *)argp); - break; - } - /* Routing */ - case SIOCADDRT: - case SIOCDELRT: - rc = -EPERM; - if (capable(CAP_NET_ADMIN)) - rc = atrtr_ioctl(cmd, argp); - break; - /* Interface */ - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFBRDADDR: - case SIOCATALKDIFADDR: - case SIOCDIFADDR: - case SIOCSARP: /* proxy AARP */ - case SIOCDARP: /* proxy AARP */ - rtnl_lock(); - rc = atif_ioctl(cmd, argp); - rtnl_unlock(); - break; - } - - return rc; -} - - -#ifdef CONFIG_COMPAT -static int atalk_compat_routing_ioctl(struct sock *sk, unsigned int cmd, - struct compat_rtentry __user *ur) -{ - compat_uptr_t rtdev; - struct rtentry rt; - - if (copy_from_user(&rt.rt_dst, &ur->rt_dst, - 3 * sizeof(struct sockaddr)) || - get_user(rt.rt_flags, &ur->rt_flags) || - get_user(rt.rt_metric, &ur->rt_metric) || - get_user(rt.rt_mtu, &ur->rt_mtu) || - get_user(rt.rt_window, &ur->rt_window) || - get_user(rt.rt_irtt, &ur->rt_irtt) || - get_user(rtdev, &ur->rt_dev)) - return -EFAULT; - - switch (cmd) { - case SIOCDELRT: - if (rt.rt_dst.sa_family != AF_APPLETALK) - return -EINVAL; - return atrtr_delete(&((struct sockaddr_at *) - &rt.rt_dst)->sat_addr); - - case SIOCADDRT: - rt.rt_dev = compat_ptr(rtdev); - return atrtr_ioctl_addrt(&rt); - default: - return -EINVAL; - } -} -static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - void __user *argp = compat_ptr(arg); - struct sock *sk = sock->sk; - - switch (cmd) { - case SIOCADDRT: - case SIOCDELRT: - return atalk_compat_routing_ioctl(sk, cmd, argp); - /* - * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we - * cannot handle it in common code. The data we access if ifreq - * here is compatible, so we can simply call the native - * handler. - */ - case SIOCATALKDIFADDR: - return atalk_ioctl(sock, cmd, (unsigned long)argp); - default: - return -ENOIOCTLCMD; - } -} -#endif /* CONFIG_COMPAT */ - - -static const struct net_proto_family atalk_family_ops = { - .family = PF_APPLETALK, - .create = atalk_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops atalk_dgram_ops = { - .family = PF_APPLETALK, - .owner = THIS_MODULE, - .release = atalk_release, - .bind = atalk_bind, - .connect = atalk_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = atalk_getname, - .poll = datagram_poll, - .ioctl = atalk_ioctl, - .gettstamp = sock_gettstamp, -#ifdef CONFIG_COMPAT - .compat_ioctl = atalk_compat_ioctl, -#endif - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .sendmsg = atalk_sendmsg, - .recvmsg = atalk_recvmsg, - .mmap = sock_no_mmap, -}; - -static struct notifier_block ddp_notifier = { - .notifier_call = ddp_device_event, -}; - -static struct packet_type ltalk_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_LOCALTALK), - .func = ltalk_rcv, -}; - -static struct packet_type ppptalk_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_PPPTALK), - .func = atalk_rcv, -}; - -static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B }; - -/* Export symbols for use by drivers when AppleTalk is a module */ -EXPORT_SYMBOL(atrtr_get_dev); -EXPORT_SYMBOL(atalk_find_dev_addr); - -/* Called by proto.c on kernel start up */ -static int __init atalk_init(void) -{ - int rc; - - rc = proto_register(&ddp_proto, 0); - if (rc) - goto out; - - rc = sock_register(&atalk_family_ops); - if (rc) - goto out_proto; - - ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv); - if (!ddp_dl) { - pr_crit("Unable to register DDP with SNAP.\n"); - rc = -ENOMEM; - goto out_sock; - } - - dev_add_pack(<alk_packet_type); - dev_add_pack(&ppptalk_packet_type); - - rc = register_netdevice_notifier(&ddp_notifier); - if (rc) - goto out_snap; - - rc = aarp_proto_init(); - if (rc) - goto out_dev; - - rc = atalk_proc_init(); - if (rc) - goto out_aarp; - - rc = atalk_register_sysctl(); - if (rc) - goto out_proc; -out: - return rc; -out_proc: - atalk_proc_exit(); -out_aarp: - aarp_cleanup_module(); -out_dev: - unregister_netdevice_notifier(&ddp_notifier); -out_snap: - dev_remove_pack(&ppptalk_packet_type); - dev_remove_pack(<alk_packet_type); - unregister_snap_client(ddp_dl); -out_sock: - sock_unregister(PF_APPLETALK); -out_proto: - proto_unregister(&ddp_proto); - goto out; -} -module_init(atalk_init); - -/* - * No explicit module reference count manipulation is needed in the - * protocol. Socket layer sets module reference count for us - * and interfaces reference counting is done - * by the network device layer. - * - * Ergo, before the AppleTalk module can be removed, all AppleTalk - * sockets should be closed from user space. - */ -static void __exit atalk_exit(void) -{ -#ifdef CONFIG_SYSCTL - atalk_unregister_sysctl(); -#endif /* CONFIG_SYSCTL */ - atalk_proc_exit(); - aarp_cleanup_module(); /* General aarp clean-up. */ - unregister_netdevice_notifier(&ddp_notifier); - dev_remove_pack(<alk_packet_type); - dev_remove_pack(&ppptalk_packet_type); - unregister_snap_client(ddp_dl); - sock_unregister(PF_APPLETALK); - proto_unregister(&ddp_proto); -} -module_exit(atalk_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alan Cox "); -MODULE_DESCRIPTION("AppleTalk 0.20\n"); -MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c deleted file mode 100644 index 7aebfe903242..000000000000 --- a/net/appletalk/sysctl_net_atalk.c +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * sysctl_net_atalk.c: sysctl interface to net AppleTalk subsystem. - * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/atalk directory entry (empty =) ). [MS] - * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) - */ - -#include -#include -#include - -static struct ctl_table atalk_table[] = { - { - .procname = "aarp-expiry-time", - .data = &sysctl_aarp_expiry_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "aarp-tick-time", - .data = &sysctl_aarp_tick_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "aarp-retransmit-limit", - .data = &sysctl_aarp_retransmit_limit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "aarp-resolve-time", - .data = &sysctl_aarp_resolve_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, -}; - -static struct ctl_table_header *atalk_table_header; - -int __init atalk_register_sysctl(void) -{ - atalk_table_header = register_net_sysctl(&init_net, "net/appletalk", atalk_table); - if (!atalk_table_header) - return -ENOMEM; - return 0; -} - -void atalk_unregister_sysctl(void) -{ - unregister_net_sysctl_table(atalk_table_header); -} diff --git a/net/atm/Makefile b/net/atm/Makefile index 484a1b1552cc..0a14ea7d4683 100644 --- a/net/atm/Makefile +++ b/net/atm/Makefile @@ -3,7 +3,7 @@ # Makefile for the ATM Protocol Families. # -atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o +atm-y := pvc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o obj-$(CONFIG_ATM) += atm.o obj-$(CONFIG_ATM_BR2684) += br2684.o diff --git a/net/atm/addr.c b/net/atm/addr.c deleted file mode 100644 index 938f360ae230..000000000000 --- a/net/atm/addr.c +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* net/atm/addr.c - Local ATM address registry */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#include -#include -#include -#include - -#include "signaling.h" -#include "addr.h" - -static int check_addr(const struct sockaddr_atmsvc *addr) -{ - int i; - - if (addr->sas_family != AF_ATMSVC) - return -EAFNOSUPPORT; - if (!*addr->sas_addr.pub) - return *addr->sas_addr.prv ? 0 : -EINVAL; - for (i = 1; i < ATM_E164_LEN + 1; i++) /* make sure it's \0-terminated */ - if (!addr->sas_addr.pub[i]) - return 0; - return -EINVAL; -} - -static int identical(const struct sockaddr_atmsvc *a, const struct sockaddr_atmsvc *b) -{ - if (*a->sas_addr.prv) - if (memcmp(a->sas_addr.prv, b->sas_addr.prv, ATM_ESA_LEN)) - return 0; - if (!*a->sas_addr.pub) - return !*b->sas_addr.pub; - if (!*b->sas_addr.pub) - return 0; - return !strcmp(a->sas_addr.pub, b->sas_addr.pub); -} - -static void notify_sigd(const struct atm_dev *dev) -{ - struct sockaddr_atmpvc pvc; - - pvc.sap_addr.itf = dev->number; - sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL); -} - -void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t atype) -{ - unsigned long flags; - struct atm_dev_addr *this, *p; - struct list_head *head; - - spin_lock_irqsave(&dev->lock, flags); - if (atype == ATM_ADDR_LECS) - head = &dev->lecs; - else - head = &dev->local; - list_for_each_entry_safe(this, p, head, entry) { - list_del(&this->entry); - kfree(this); - } - spin_unlock_irqrestore(&dev->lock, flags); - if (head == &dev->local) - notify_sigd(dev); -} - -int atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr, - enum atm_addr_type_t atype) -{ - unsigned long flags; - struct atm_dev_addr *this; - struct list_head *head; - int error; - - error = check_addr(addr); - if (error) - return error; - spin_lock_irqsave(&dev->lock, flags); - if (atype == ATM_ADDR_LECS) - head = &dev->lecs; - else - head = &dev->local; - list_for_each_entry(this, head, entry) { - if (identical(&this->addr, addr)) { - spin_unlock_irqrestore(&dev->lock, flags); - return -EEXIST; - } - } - this = kmalloc_obj(struct atm_dev_addr, GFP_ATOMIC); - if (!this) { - spin_unlock_irqrestore(&dev->lock, flags); - return -ENOMEM; - } - this->addr = *addr; - list_add(&this->entry, head); - spin_unlock_irqrestore(&dev->lock, flags); - if (head == &dev->local) - notify_sigd(dev); - return 0; -} - -int atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr, - enum atm_addr_type_t atype) -{ - unsigned long flags; - struct atm_dev_addr *this; - struct list_head *head; - int error; - - error = check_addr(addr); - if (error) - return error; - spin_lock_irqsave(&dev->lock, flags); - if (atype == ATM_ADDR_LECS) - head = &dev->lecs; - else - head = &dev->local; - list_for_each_entry(this, head, entry) { - if (identical(&this->addr, addr)) { - list_del(&this->entry); - spin_unlock_irqrestore(&dev->lock, flags); - kfree(this); - if (head == &dev->local) - notify_sigd(dev); - return 0; - } - } - spin_unlock_irqrestore(&dev->lock, flags); - return -ENOENT; -} - -int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf, - size_t size, enum atm_addr_type_t atype) -{ - unsigned long flags; - struct atm_dev_addr *this; - struct list_head *head; - int total = 0, error; - struct sockaddr_atmsvc *tmp_buf, *tmp_bufp; - - spin_lock_irqsave(&dev->lock, flags); - if (atype == ATM_ADDR_LECS) - head = &dev->lecs; - else - head = &dev->local; - list_for_each_entry(this, head, entry) - total += sizeof(struct sockaddr_atmsvc); - tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC); - if (!tmp_buf) { - spin_unlock_irqrestore(&dev->lock, flags); - return -ENOMEM; - } - list_for_each_entry(this, head, entry) - memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc)); - spin_unlock_irqrestore(&dev->lock, flags); - error = total > size ? -E2BIG : total; - if (copy_to_user(buf, tmp_buf, total < size ? total : size)) - error = -EFAULT; - kfree(tmp_buf); - return error; -} diff --git a/net/atm/addr.h b/net/atm/addr.h deleted file mode 100644 index da3f848411a0..000000000000 --- a/net/atm/addr.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* net/atm/addr.h - Local ATM address registry */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef NET_ATM_ADDR_H -#define NET_ATM_ADDR_H - -#include -#include - -void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t type); -int atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr, - enum atm_addr_type_t type); -int atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr, - enum atm_addr_type_t type); -int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user *buf, - size_t size, enum atm_addr_type_t type); - -#endif diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c index 54e7fb1a4ee5..0676a9c333ff 100644 --- a/net/atm/atm_sysfs.c +++ b/net/atm/atm_sysfs.c @@ -27,29 +27,6 @@ static ssize_t address_show(struct device *cdev, return scnprintf(buf, PAGE_SIZE, "%pM\n", adev->esi); } -static ssize_t atmaddress_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags; - struct atm_dev *adev = to_atm_dev(cdev); - struct atm_dev_addr *aaddr; - int count = 0; - - spin_lock_irqsave(&adev->lock, flags); - list_for_each_entry(aaddr, &adev->local, entry) { - count += scnprintf(buf + count, PAGE_SIZE - count, - "%1phN.%2phN.%10phN.%6phN.%1phN\n", - &aaddr->addr.sas_addr.prv[0], - &aaddr->addr.sas_addr.prv[1], - &aaddr->addr.sas_addr.prv[3], - &aaddr->addr.sas_addr.prv[13], - &aaddr->addr.sas_addr.prv[19]); - } - spin_unlock_irqrestore(&adev->lock, flags); - - return count; -} - static ssize_t atmindex_show(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -91,14 +68,12 @@ static ssize_t link_rate_show(struct device *cdev, } static DEVICE_ATTR_RO(address); -static DEVICE_ATTR_RO(atmaddress); static DEVICE_ATTR_RO(atmindex); static DEVICE_ATTR_RO(carrier); static DEVICE_ATTR_RO(type); static DEVICE_ATTR_RO(link_rate); static struct device_attribute *atm_attrs[] = { - &dev_attr_atmaddress, &dev_attr_address, &dev_attr_atmindex, &dev_attr_carrier, diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 6580d67c3456..05712c28386a 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -311,8 +311,7 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, bh_lock_sock(sk_atm(atmvcc)); - if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) || - test_bit(ATM_VF_CLOSE, &atmvcc->flags) || + if (test_bit(ATM_VF_CLOSE, &atmvcc->flags) || !test_bit(ATM_VF_READY, &atmvcc->flags)) { dev->stats.tx_dropped++; dev_kfree_skb(skb); diff --git a/net/atm/common.c b/net/atm/common.c index fe77f51f6ce1..c7f92405daf0 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -23,14 +23,13 @@ #include /* struct sock */ #include #include +#include #include #include "resources.h" /* atm_find_dev */ #include "common.h" /* prototypes */ #include "protocols.h" /* atm_init_ */ -#include "addr.h" /* address registry */ -#include "signaling.h" /* for WAITING and sigd_attach */ struct hlist_head vcc_hash[VCC_HTABLE_SIZE]; EXPORT_SYMBOL(vcc_hash); @@ -154,15 +153,12 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family, i vcc = atm_sk(sk); vcc->dev = NULL; - memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc)); - memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc)); vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */ refcount_set(&sk->sk_wmem_alloc, SK_WMEM_ALLOC_BIAS); atomic_set(&sk->sk_rmem_alloc, 0); vcc->push = NULL; vcc->pop = NULL; vcc->owner = NULL; - vcc->push_oam = NULL; vcc->release_cb = NULL; vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */ vcc->atm_options = vcc->aal_options = 0; @@ -217,7 +213,6 @@ void vcc_release_async(struct atm_vcc *vcc, int reply) set_bit(ATM_VF_CLOSE, &vcc->flags); sk->sk_shutdown |= RCV_SHUTDOWN; sk->sk_err = -reply; - clear_bit(ATM_VF_WAITING, &vcc->flags); sk->sk_state_change(sk); } EXPORT_SYMBOL(vcc_release_async); @@ -292,9 +287,6 @@ static int adjust_tp(struct atm_trafprm *tp, unsigned char aal) case ATM_AAL0: max_sdu = ATM_CELL_SIZE-1; break; - case ATM_AAL34: - max_sdu = ATM_MAX_AAL34_PDU; - break; default: pr_warn("AAL problems ... (%d)\n", aal); fallthrough; @@ -410,10 +402,6 @@ static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, error = atm_init_aal0(vcc); vcc->stats = &dev->stats.aal0; break; - case ATM_AAL34: - error = atm_init_aal34(vcc); - vcc->stats = &dev->stats.aal34; - break; case ATM_NO_AAL: /* ATM_AAL5 is also used in the "0 for default" case */ vcc->qos.aal = ATM_AAL5; @@ -535,8 +523,7 @@ int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, return -EOPNOTSUPP; vcc = ATM_SD(sock); - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || - test_bit(ATM_VF_CLOSE, &vcc->flags) || + if (test_bit(ATM_VF_CLOSE, &vcc->flags) || !test_bit(ATM_VF_READY, &vcc->flags)) return 0; @@ -583,8 +570,7 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) goto out; } vcc = ATM_SD(sock); - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || - test_bit(ATM_VF_CLOSE, &vcc->flags) || + if (test_bit(ATM_VF_CLOSE, &vcc->flags) || !test_bit(ATM_VF_READY, &vcc->flags)) { error = -EPIPE; send_sig(SIGPIPE, current, 0); @@ -612,8 +598,7 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) error = -ERESTARTSYS; break; } - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || - test_bit(ATM_VF_CLOSE, &vcc->flags) || + if (test_bit(ATM_VF_CLOSE, &vcc->flags) || !test_bit(ATM_VF_READY, &vcc->flags)) { error = -EPIPE; send_sig(SIGPIPE, current, 0); @@ -641,12 +626,6 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) if (eff != size) memset(skb->data + size, 0, eff-size); - if (vcc->dev->ops->pre_send) { - error = vcc->dev->ops->pre_send(vcc, skb); - if (error) - goto free_skb; - } - error = vcc->dev->ops->send(vcc, skb); error = error ? error : size; out: @@ -673,8 +652,7 @@ __poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait) if (sk->sk_err) mask = EPOLLERR; - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || - test_bit(ATM_VF_CLOSE, &vcc->flags)) + if (test_bit(ATM_VF_CLOSE, &vcc->flags)) mask |= EPOLLHUP; /* readable? */ @@ -682,10 +660,6 @@ __poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait) mask |= EPOLLIN | EPOLLRDNORM; /* writable? */ - if (sock->state == SS_CONNECTING && - test_bit(ATM_VF_WAITING, &vcc->flags)) - return mask; - if (vcc->qos.txtp.traffic_class != ATM_NONE && vcc_writable(sk)) mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; @@ -693,33 +667,11 @@ __poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait) return mask; } -static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) -{ - int error; - - /* - * Don't let the QoS change the already connected AAL type nor the - * traffic class. - */ - if (qos->aal != vcc->qos.aal || - qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class || - qos->txtp.traffic_class != vcc->qos.txtp.traffic_class) - return -EINVAL; - error = adjust_tp(&qos->txtp, qos->aal); - if (!error) - error = adjust_tp(&qos->rxtp, qos->aal); - if (error) - return error; - if (!vcc->dev->ops->change_qos) - return -EOPNOTSUPP; - if (sk_atm(vcc)->sk_family == AF_ATMPVC) - return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET); - return svc_change_qos(vcc, qos); -} - static int check_tp(const struct atm_trafprm *tp) { /* @@@ Should be merged with adjust_tp */ + if (tp->traffic_class > ATM_ANYCLASS) + return -EINVAL; if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) return 0; if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr && @@ -775,8 +727,9 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, error = check_qos(&qos); if (error) return error; + /* QoS cannot be renegotiated on an already connected VCC. */ if (sock->state == SS_CONNECTED) - return atm_change_qos(vcc, &qos); + return -EOPNOTSUPP; if (sock->state != SS_UNCONNECTED) return -EBADFD; vcc->qos = qos; @@ -797,13 +750,13 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, } int vcc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct atm_vcc *vcc; + int val; int len; - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; if (__SO_LEVEL_MATCH(optname, level) && len != __SO_SIZE(optname)) return -EINVAL; @@ -812,11 +765,13 @@ int vcc_getsockopt(struct socket *sock, int level, int optname, case SO_ATMQOS: if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) return -EINVAL; - return copy_to_user(optval, &vcc->qos, sizeof(vcc->qos)) + return copy_to_iter(&vcc->qos, sizeof(vcc->qos), + &opt->iter_out) != sizeof(vcc->qos) ? -EFAULT : 0; case SO_SETCLP: - return put_user(vcc->atm_options & ATM_ATMOPT_CLP ? 1 : 0, - (unsigned long __user *)optval) ? -EFAULT : 0; + val = vcc->atm_options & ATM_ATMOPT_CLP ? 1 : 0; + return copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val) ? -EFAULT : 0; case SO_ATMPVC: { struct sockaddr_atmpvc pvc; @@ -828,7 +783,8 @@ int vcc_getsockopt(struct socket *sock, int level, int optname, pvc.sap_addr.itf = vcc->dev->number; pvc.sap_addr.vpi = vcc->vpi; pvc.sap_addr.vci = vcc->vci; - return copy_to_user(optval, &pvc, sizeof(pvc)) ? -EFAULT : 0; + return copy_to_iter(&pvc, sizeof(pvc), &opt->iter_out) != + sizeof(pvc) ? -EFAULT : 0; } default: return -EINVAL; @@ -859,15 +815,10 @@ static int __init atm_init(void) pr_err("atmpvc_init() failed with %d\n", error); goto out_unregister_vcc_proto; } - error = atmsvc_init(); - if (error < 0) { - pr_err("atmsvc_init() failed with %d\n", error); - goto out_atmpvc_exit; - } error = atm_proc_init(); if (error < 0) { pr_err("atm_proc_init() failed with %d\n", error); - goto out_atmsvc_exit; + goto out_atmpvc_exit; } error = atm_sysfs_init(); if (error < 0) { @@ -878,8 +829,6 @@ static int __init atm_init(void) return error; out_atmproc_exit: atm_proc_exit(); -out_atmsvc_exit: - atmsvc_exit(); out_atmpvc_exit: atmpvc_exit(); out_unregister_vcc_proto: @@ -891,7 +840,6 @@ static void __exit atm_exit(void) { atm_proc_exit(); atm_sysfs_exit(); - atmsvc_exit(); atmpvc_exit(); proto_unregister(&vcc_proto); } @@ -903,4 +851,3 @@ module_exit(atm_exit); MODULE_DESCRIPTION("Asynchronous Transfer Mode (ATM) networking core"); MODULE_LICENSE("GPL"); MODULE_ALIAS_NETPROTO(PF_ATMPVC); -MODULE_ALIAS_NETPROTO(PF_ATMSVC); diff --git a/net/atm/common.h b/net/atm/common.h index a1e56e8de698..11cb51dd7dbb 100644 --- a/net/atm/common.h +++ b/net/atm/common.h @@ -23,13 +23,11 @@ int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int vcc_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, unsigned int optlen); int vcc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen); + sockopt_t *opt); void vcc_process_recv_queue(struct atm_vcc *vcc); int atmpvc_init(void); void atmpvc_exit(void); -int atmsvc_init(void); -void atmsvc_exit(void); int atm_sysfs_init(void); void atm_sysfs_exit(void); @@ -48,9 +46,6 @@ static inline void atm_proc_exit(void) } #endif /* CONFIG_PROC_FS */ -/* SVC */ -int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos); - void atm_dev_release_vccs(struct atm_dev *dev); #endif diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 0f3f9ad8301f..194587fc15e8 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c @@ -11,16 +11,12 @@ #include /* struct socket, struct proto_ops */ #include /* ATM stuff */ #include -#include /* manifest constants */ #include -#include /* for ioctls */ -#include #include #include #include #include "resources.h" -#include "signaling.h" /* for WAITING and sigd_attach */ #include "common.h" @@ -87,36 +83,6 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, current->comm, task_pid_nr(current)); error = 0; goto done; - case ATMSIGD_CTRL: - if (!capable(CAP_NET_ADMIN)) { - error = -EPERM; - goto done; - } - /* - * The user/kernel protocol for exchanging signalling - * info uses kernel pointers as opaque references, - * so the holder of the file descriptor can scribble - * on the kernel... so we should make sure that we - * have the same privileges that /proc/kcore needs - */ - if (!capable(CAP_SYS_RAWIO)) { - error = -EPERM; - goto done; - } -#ifdef CONFIG_COMPAT - /* WTF? I don't even want to _think_ about making this - work for 32-bit userspace. TBH I don't really want - to think about it at all. dwmw2. */ - if (compat) { - net_warn_ratelimited("32-bit task cannot be atmsigd\n"); - error = -EINVAL; - goto done; - } -#endif - error = sigd_attach(vcc); - if (!error) - sock->state = SS_CONNECTED; - goto done; case ATM_SETBACKEND: case ATM_NEWBACKENDIF: { @@ -221,10 +187,6 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) #define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct compat_atm_iobuf) #define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct compat_atmif_sioc) #define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct compat_atmif_sioc) -#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct compat_atmif_sioc) -#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct compat_atmif_sioc) -#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct compat_atmif_sioc) -#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct compat_atmif_sioc) #define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct compat_atmif_sioc) #define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct compat_atmif_sioc) #define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct compat_atmif_sioc) @@ -243,10 +205,6 @@ static struct { { ATM_GETNAMES32, ATM_GETNAMES }, { ATM_GETTYPE32, ATM_GETTYPE }, { ATM_GETESI32, ATM_GETESI }, - { ATM_GETADDR32, ATM_GETADDR }, - { ATM_RSTADDR32, ATM_RSTADDR }, - { ATM_ADDADDR32, ATM_ADDADDR }, - { ATM_DELADDR32, ATM_DELADDR }, { ATM_GETCIRANGE32, ATM_GETCIRANGE }, { ATM_SETCIRANGE32, ATM_SETCIRANGE }, { ATM_SETESI32, ATM_SETESI }, @@ -290,18 +248,6 @@ static int do_atm_ioctl(struct socket *sock, unsigned int cmd32, int i; unsigned int cmd = 0; - switch (cmd32) { - case SONET_GETSTAT: - case SONET_GETSTATZ: - case SONET_GETDIAG: - case SONET_SETDIAG: - case SONET_CLRDIAG: - case SONET_SETFRAMING: - case SONET_GETFRAMING: - case SONET_GETFRSENSE: - return do_atmif_sioc(sock, cmd32, arg); - } - for (i = 0; i < NR_ATM_IOCTL; i++) { if (cmd32 == atm_ioctl_map[i].cmd32) { cmd = atm_ioctl_map[i].cmd; @@ -318,10 +264,6 @@ static int do_atm_ioctl(struct socket *sock, unsigned int cmd32, case ATM_GETLINKRATE: case ATM_GETTYPE: case ATM_GETESI: - case ATM_GETADDR: - case ATM_RSTADDR: - case ATM_ADDADDR: - case ATM_DELADDR: case ATM_GETCIRANGE: case ATM_SETCIRANGE: case ATM_SETESI: diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c index e3c422dc533a..6da52d12df68 100644 --- a/net/atm/pppoatm.c +++ b/net/atm/pppoatm.c @@ -308,8 +308,7 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb) test_and_set_bit(BLOCKED, &pvcc->blocked); goto nospace; } - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || - test_bit(ATM_VF_CLOSE, &vcc->flags) || + if (test_bit(ATM_VF_CLOSE, &vcc->flags) || !test_bit(ATM_VF_READY, &vcc->flags)) { bh_unlock_sock(sk_atm(vcc)); kfree_skb(skb); diff --git a/net/atm/proc.c b/net/atm/proc.c index b650da764a23..8f20b49b9c02 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -29,7 +29,6 @@ #include #include "resources.h" #include "common.h" /* atm_proc_init prototype */ -#include "signaling.h" /* to get sigd - ugly too */ static ssize_t proc_dev_atm_read(struct file *file, char __user *buf, size_t count, loff_t *pos); @@ -156,13 +155,6 @@ static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) seq_putc(seq, '\n'); } -static const char *vcc_state(struct atm_vcc *vcc) -{ - static const char *const map[] = { ATM_VS2TXT_MAP }; - - return map[ATM_VF2VS(vcc->flags)]; -} - static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) { struct sock *sk = sk_atm(vcc); @@ -177,9 +169,6 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) case AF_ATMPVC: seq_printf(seq, "PVC"); break; - case AF_ATMSVC: - seq_printf(seq, "SVC"); - break; default: seq_printf(seq, "%3d", sk->sk_family); } @@ -190,26 +179,6 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) refcount_read(&sk->sk_refcnt)); } -static void svc_info(struct seq_file *seq, struct atm_vcc *vcc) -{ - if (!vcc->dev) - seq_printf(seq, sizeof(void *) == 4 ? - "N/A@%pK%10s" : "N/A@%pK%2s", vcc, ""); - else - seq_printf(seq, "%3d %3d %5d ", - vcc->dev->number, vcc->vpi, vcc->vci); - seq_printf(seq, "%-10s ", vcc_state(vcc)); - seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub, - *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : ""); - if (*vcc->remote.sas_addr.prv) { - int i; - - for (i = 0; i < ATM_ESA_LEN; i++) - seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]); - } - seq_putc(seq, '\n'); -} - static int atm_dev_seq_show(struct seq_file *seq, void *v) { static char atm_dev_banner[] = @@ -278,29 +247,6 @@ static const struct seq_operations vcc_seq_ops = { .show = vcc_seq_show, }; -static int svc_seq_show(struct seq_file *seq, void *v) -{ - static const char atm_svc_banner[] = - "Itf VPI VCI State Remote\n"; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, atm_svc_banner); - else { - struct vcc_state *state = seq->private; - struct atm_vcc *vcc = atm_sk(state->sk); - - svc_info(seq, vcc); - } - return 0; -} - -static const struct seq_operations svc_seq_ops = { - .start = vcc_seq_start, - .next = vcc_seq_next, - .stop = vcc_seq_stop, - .show = svc_seq_show, -}; - static ssize_t proc_dev_atm_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { @@ -376,8 +322,6 @@ int __init atm_proc_init(void) proc_create_seq("devices", 0444, atm_proc_root, &atm_dev_seq_ops); proc_create_seq_private("pvc", 0444, atm_proc_root, &pvc_seq_ops, sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMPVC); - proc_create_seq_private("svc", 0444, atm_proc_root, &svc_seq_ops, - sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMSVC); proc_create_seq_private("vc", 0444, atm_proc_root, &vcc_seq_ops, sizeof(struct vcc_state), NULL); return 0; diff --git a/net/atm/protocols.h b/net/atm/protocols.h index 18d4d008bac3..30158efb5e1a 100644 --- a/net/atm/protocols.h +++ b/net/atm/protocols.h @@ -8,7 +8,6 @@ #define NET_ATM_PROTOCOLS_H int atm_init_aal0(struct atm_vcc *vcc); /* "raw" AAL0 */ -int atm_init_aal34(struct atm_vcc *vcc);/* "raw" AAL3/4 transport */ int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */ #endif diff --git a/net/atm/pvc.c b/net/atm/pvc.c index 8f5e76f5dd9e..8b2c3e515601 100644 --- a/net/atm/pvc.c +++ b/net/atm/pvc.c @@ -75,13 +75,13 @@ static int pvc_setsockopt(struct socket *sock, int level, int optname, } static int pvc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; int error; lock_sock(sk); - error = vcc_getsockopt(sock, level, optname, optval, optlen); + error = vcc_getsockopt(sock, level, optname, opt); release_sock(sk); return error; } @@ -122,7 +122,7 @@ static const struct proto_ops pvc_proto_ops = { .listen = sock_no_listen, .shutdown = pvc_shutdown, .setsockopt = pvc_setsockopt, - .getsockopt = pvc_getsockopt, + .getsockopt_iter = pvc_getsockopt, .sendmsg = vcc_sendmsg, .recvmsg = vcc_recvmsg, .mmap = sock_no_mmap, diff --git a/net/atm/raw.c b/net/atm/raw.c index 1e6511ec842c..87d136c7554b 100644 --- a/net/atm/raw.c +++ b/net/atm/raw.c @@ -54,8 +54,6 @@ static int atm_send_aal0(struct atm_vcc *vcc, struct sk_buff *skb) kfree_skb(skb); return -EADDRNOTAVAIL; } - if (vcc->dev->ops->send_bh) - return vcc->dev->ops->send_bh(vcc, skb); return vcc->dev->ops->send(vcc, skb); } @@ -63,32 +61,15 @@ int atm_init_aal0(struct atm_vcc *vcc) { vcc->push = atm_push_raw; vcc->pop = atm_pop_raw; - vcc->push_oam = NULL; vcc->send = atm_send_aal0; return 0; } -int atm_init_aal34(struct atm_vcc *vcc) -{ - vcc->push = atm_push_raw; - vcc->pop = atm_pop_raw; - vcc->push_oam = NULL; - if (vcc->dev->ops->send_bh) - vcc->send = vcc->dev->ops->send_bh; - else - vcc->send = vcc->dev->ops->send; - return 0; -} - int atm_init_aal5(struct atm_vcc *vcc) { vcc->push = atm_push_raw; vcc->pop = atm_pop_raw; - vcc->push_oam = NULL; - if (vcc->dev->ops->send_bh) - vcc->send = vcc->dev->ops->send_bh; - else - vcc->send = vcc->dev->ops->send; + vcc->send = vcc->dev->ops->send; return 0; } EXPORT_SYMBOL(atm_init_aal5); diff --git a/net/atm/resources.c b/net/atm/resources.c index 939452a610c0..d94dc8221ea1 100644 --- a/net/atm/resources.c +++ b/net/atm/resources.c @@ -13,7 +13,6 @@ #include #include #include -#include #include /* for barrier */ #include #include @@ -26,7 +25,6 @@ #include "common.h" #include "resources.h" -#include "addr.h" LIST_HEAD(atm_devs); @@ -42,9 +40,6 @@ static struct atm_dev *__alloc_atm_dev(const char *type) dev->type = type; dev->signal = ATM_PHY_SIG_UNKNOWN; dev->link_rate = ATM_OC3_PCR; - spin_lock_init(&dev->lock); - INIT_LIST_HEAD(&dev->local); - INIT_LIST_HEAD(&dev->lecs); return dev; } @@ -228,11 +223,8 @@ int atm_getnames(void __user *buf, int __user *iobuf_len) int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len, int number, int compat) { - int error, len, size = 0; struct atm_dev *dev; - - if (get_user(len, sioc_len)) - return -EFAULT; + int error, size = 0; dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d", number); @@ -307,51 +299,6 @@ int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len, goto done; } break; - case ATM_RSTADDR: - if (!capable(CAP_NET_ADMIN)) { - error = -EPERM; - goto done; - } - atm_reset_addr(dev, ATM_ADDR_LOCAL); - break; - case ATM_ADDADDR: - case ATM_DELADDR: - case ATM_ADDLECSADDR: - case ATM_DELLECSADDR: - { - struct sockaddr_atmsvc addr; - - if (!capable(CAP_NET_ADMIN)) { - error = -EPERM; - goto done; - } - - if (copy_from_user(&addr, buf, sizeof(addr))) { - error = -EFAULT; - goto done; - } - if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR) - error = atm_add_addr(dev, &addr, - (cmd == ATM_ADDADDR ? - ATM_ADDR_LOCAL : ATM_ADDR_LECS)); - else - error = atm_del_addr(dev, &addr, - (cmd == ATM_DELADDR ? - ATM_ADDR_LOCAL : ATM_ADDR_LECS)); - goto done; - } - case ATM_GETADDR: - case ATM_GETLECSADDR: - error = atm_get_addr(dev, buf, len, - (cmd == ATM_GETADDR ? - ATM_ADDR_LOCAL : ATM_ADDR_LECS)); - if (error < 0) - goto done; - size = error; - /* may return 0, but later on size == 0 means "don't - write the length" */ - error = put_user(size, sioc_len) ? -EFAULT : 0; - goto done; case ATM_SETLOOP: if (__ATM_LM_XTRMT((int) (unsigned long) buf) && __ATM_LM_XTLOC((int) (unsigned long) buf) > @@ -361,10 +308,6 @@ int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len, } fallthrough; case ATM_SETCIRANGE: - case SONET_GETSTATZ: - case SONET_SETDIAG: - case SONET_CLRDIAG: - case SONET_SETFRAMING: if (!capable(CAP_NET_ADMIN)) { error = -EPERM; goto done; diff --git a/net/atm/signaling.c b/net/atm/signaling.c deleted file mode 100644 index b991d937205a..000000000000 --- a/net/atm/signaling.c +++ /dev/null @@ -1,297 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* net/atm/signaling.c - ATM signaling */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include /* error codes */ -#include /* printk */ -#include -#include -#include /* jiffies and HZ */ -#include /* ATM stuff */ -#include -#include -#include -#include -#include - -#include "resources.h" -#include "signaling.h" - -struct atm_vcc *sigd = NULL; - -/* - * find_get_vcc - validate and get a reference to a vcc pointer - * @vcc: the vcc pointer to validate - * - * This function validates that @vcc points to a registered VCC in vcc_hash. - * If found, it increments the socket reference count and returns the vcc. - * The caller must call sock_put(sk_atm(vcc)) when done. - * - * Returns the vcc pointer if valid, NULL otherwise. - */ -static struct atm_vcc *find_get_vcc(struct atm_vcc *vcc) -{ - int i; - - read_lock(&vcc_sklist_lock); - for (i = 0; i < VCC_HTABLE_SIZE; i++) { - struct sock *s; - - sk_for_each(s, &vcc_hash[i]) { - if (atm_sk(s) == vcc) { - sock_hold(s); - read_unlock(&vcc_sklist_lock); - return vcc; - } - } - } - read_unlock(&vcc_sklist_lock); - return NULL; -} - -static void sigd_put_skb(struct sk_buff *skb) -{ - if (!sigd) { - pr_debug("atmsvc: no signaling daemon\n"); - kfree_skb(skb); - return; - } - atm_force_charge(sigd, skb->truesize); - skb_queue_tail(&sk_atm(sigd)->sk_receive_queue, skb); - sk_atm(sigd)->sk_data_ready(sk_atm(sigd)); -} - -static void modify_qos(struct atm_vcc *vcc, struct atmsvc_msg *msg) -{ - struct sk_buff *skb; - - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || - !test_bit(ATM_VF_READY, &vcc->flags)) - return; - msg->type = as_error; - if (!vcc->dev->ops->change_qos) - msg->reply = -EOPNOTSUPP; - else { - /* should lock VCC */ - msg->reply = vcc->dev->ops->change_qos(vcc, &msg->qos, - msg->reply); - if (!msg->reply) - msg->type = as_okay; - } - /* - * Should probably just turn around the old skb. But then, the buffer - * space accounting needs to follow the change too. Maybe later. - */ - while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL))) - schedule(); - *(struct atmsvc_msg *)skb_put(skb, sizeof(struct atmsvc_msg)) = *msg; - sigd_put_skb(skb); -} - -static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct atmsvc_msg *msg; - struct atm_vcc *session_vcc; - struct sock *sk; - - msg = (struct atmsvc_msg *) skb->data; - WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc)); - - vcc = find_get_vcc(*(struct atm_vcc **)&msg->vcc); - if (!vcc) { - pr_debug("invalid vcc pointer in msg\n"); - dev_kfree_skb(skb); - return -EINVAL; - } - - pr_debug("%d (0x%lx)\n", (int)msg->type, (unsigned long)vcc); - sk = sk_atm(vcc); - - switch (msg->type) { - case as_okay: - sk->sk_err = -msg->reply; - clear_bit(ATM_VF_WAITING, &vcc->flags); - if (!*vcc->local.sas_addr.prv && !*vcc->local.sas_addr.pub) { - vcc->local.sas_family = AF_ATMSVC; - memcpy(vcc->local.sas_addr.prv, - msg->local.sas_addr.prv, ATM_ESA_LEN); - memcpy(vcc->local.sas_addr.pub, - msg->local.sas_addr.pub, ATM_E164_LEN + 1); - } - session_vcc = vcc->session ? vcc->session : vcc; - if (session_vcc->vpi || session_vcc->vci) - break; - session_vcc->itf = msg->pvc.sap_addr.itf; - session_vcc->vpi = msg->pvc.sap_addr.vpi; - session_vcc->vci = msg->pvc.sap_addr.vci; - if (session_vcc->vpi || session_vcc->vci) - session_vcc->qos = msg->qos; - break; - case as_error: - clear_bit(ATM_VF_REGIS, &vcc->flags); - clear_bit(ATM_VF_READY, &vcc->flags); - sk->sk_err = -msg->reply; - clear_bit(ATM_VF_WAITING, &vcc->flags); - break; - case as_indicate: - /* Release the reference from msg->vcc, we'll use msg->listen_vcc instead */ - sock_put(sk); - - vcc = find_get_vcc(*(struct atm_vcc **)&msg->listen_vcc); - if (!vcc) { - pr_debug("invalid listen_vcc pointer in msg\n"); - dev_kfree_skb(skb); - return -EINVAL; - } - - sk = sk_atm(vcc); - pr_debug("as_indicate!!!\n"); - lock_sock(sk); - if (sk_acceptq_is_full(sk)) { - sigd_enq(NULL, as_reject, vcc, NULL, NULL); - dev_kfree_skb(skb); - goto as_indicate_complete; - } - sk_acceptq_added(sk); - skb_queue_tail(&sk->sk_receive_queue, skb); - pr_debug("waking sk_sleep(sk) 0x%p\n", sk_sleep(sk)); - sk->sk_state_change(sk); -as_indicate_complete: - release_sock(sk); - /* Paired with find_get_vcc(msg->listen_vcc) above */ - sock_put(sk); - return 0; - case as_close: - set_bit(ATM_VF_RELEASED, &vcc->flags); - vcc_release_async(vcc, msg->reply); - goto out; - case as_modify: - modify_qos(vcc, msg); - break; - case as_addparty: - case as_dropparty: - WRITE_ONCE(sk->sk_err_soft, -msg->reply); - /* < 0 failure, otherwise ep_ref */ - clear_bit(ATM_VF_WAITING, &vcc->flags); - break; - default: - pr_alert("bad message type %d\n", (int)msg->type); - dev_kfree_skb(skb); - /* Paired with find_get_vcc(msg->vcc) above */ - sock_put(sk); - return -EINVAL; - } - sk->sk_state_change(sk); -out: - dev_kfree_skb(skb); - /* Paired with find_get_vcc(msg->vcc) above */ - sock_put(sk); - return 0; -} - -void sigd_enq2(struct atm_vcc *vcc, enum atmsvc_msg_type type, - struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc, - const struct sockaddr_atmsvc *svc, const struct atm_qos *qos, - int reply) -{ - struct sk_buff *skb; - struct atmsvc_msg *msg; - static unsigned int session = 0; - - pr_debug("%d (0x%p)\n", (int)type, vcc); - while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL))) - schedule(); - msg = skb_put_zero(skb, sizeof(struct atmsvc_msg)); - msg->type = type; - *(struct atm_vcc **) &msg->vcc = vcc; - *(struct atm_vcc **) &msg->listen_vcc = listen_vcc; - msg->reply = reply; - if (qos) - msg->qos = *qos; - if (vcc) - msg->sap = vcc->sap; - if (svc) - msg->svc = *svc; - if (vcc) - msg->local = vcc->local; - if (pvc) - msg->pvc = *pvc; - if (vcc) { - if (type == as_connect && test_bit(ATM_VF_SESSION, &vcc->flags)) - msg->session = ++session; - /* every new pmp connect gets the next session number */ - } - sigd_put_skb(skb); - if (vcc) - set_bit(ATM_VF_REGIS, &vcc->flags); -} - -void sigd_enq(struct atm_vcc *vcc, enum atmsvc_msg_type type, - struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc, - const struct sockaddr_atmsvc *svc) -{ - sigd_enq2(vcc, type, listen_vcc, pvc, svc, vcc ? &vcc->qos : NULL, 0); - /* other ISP applications may use "reply" */ -} - -static void purge_vcc(struct atm_vcc *vcc) -{ - if (sk_atm(vcc)->sk_family == PF_ATMSVC && - !test_bit(ATM_VF_META, &vcc->flags)) { - set_bit(ATM_VF_RELEASED, &vcc->flags); - clear_bit(ATM_VF_REGIS, &vcc->flags); - vcc_release_async(vcc, -EUNATCH); - } -} - -static void sigd_close(struct atm_vcc *vcc) -{ - struct sock *s; - int i; - - pr_debug("\n"); - sigd = NULL; - if (skb_peek(&sk_atm(vcc)->sk_receive_queue)) - pr_err("closing with requests pending\n"); - skb_queue_purge(&sk_atm(vcc)->sk_receive_queue); - - read_lock(&vcc_sklist_lock); - for (i = 0; i < VCC_HTABLE_SIZE; ++i) { - struct hlist_head *head = &vcc_hash[i]; - - sk_for_each(s, head) { - vcc = atm_sk(s); - - purge_vcc(vcc); - } - } - read_unlock(&vcc_sklist_lock); -} - -static const struct atmdev_ops sigd_dev_ops = { - .close = sigd_close, - .send = sigd_send -}; - -static struct atm_dev sigd_dev = { - .ops = &sigd_dev_ops, - .type = "sig", - .number = 999, - .lock = __SPIN_LOCK_UNLOCKED(sigd_dev.lock) -}; - -int sigd_attach(struct atm_vcc *vcc) -{ - if (sigd) - return -EADDRINUSE; - pr_debug("\n"); - sigd = vcc; - vcc->dev = &sigd_dev; - vcc_insert_socket(sk_atm(vcc)); - set_bit(ATM_VF_META, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); - return 0; -} diff --git a/net/atm/signaling.h b/net/atm/signaling.h deleted file mode 100644 index 2df8220f7ab5..000000000000 --- a/net/atm/signaling.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* net/atm/signaling.h - ATM signaling */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - - -#ifndef NET_ATM_SIGNALING_H -#define NET_ATM_SIGNALING_H - -#include -#include -#include - - -extern struct atm_vcc *sigd; /* needed in svc_release */ - - -/* - * sigd_enq is a wrapper for sigd_enq2, covering the more common cases, and - * avoiding huge lists of null values. - */ - -void sigd_enq2(struct atm_vcc *vcc,enum atmsvc_msg_type type, - struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc, - const struct sockaddr_atmsvc *svc,const struct atm_qos *qos,int reply); -void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type, - struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc, - const struct sockaddr_atmsvc *svc); -int sigd_attach(struct atm_vcc *vcc); - -#endif diff --git a/net/atm/svc.c b/net/atm/svc.c deleted file mode 100644 index 005964250ecd..000000000000 --- a/net/atm/svc.c +++ /dev/null @@ -1,696 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* net/atm/svc.c - ATM SVC sockets */ - -/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ - -#include -#include /* struct socket, struct proto_ops */ -#include /* error codes */ -#include /* printk */ -#include -#include -#include -#include /* O_NONBLOCK */ -#include -#include /* ATM stuff */ -#include -#include -#include -#include -#include /* for sock_no_* */ -#include -#include - -#include "resources.h" -#include "common.h" /* common for PVCs and SVCs */ -#include "signaling.h" -#include "addr.h" - -#ifdef CONFIG_COMPAT -/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */ -#define COMPAT_ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL + 4, struct compat_atm_iobuf) -#endif - -static int svc_create(struct net *net, struct socket *sock, int protocol, - int kern); - -/* - * Note: since all this is still nicely synchronized with the signaling demon, - * there's no need to protect sleep loops with clis. If signaling is - * moved into the kernel, that would change. - */ - - -static int svc_shutdown(struct socket *sock, int how) -{ - return 0; -} - -static void svc_disconnect(struct atm_vcc *vcc) -{ - DEFINE_WAIT(wait); - struct sk_buff *skb; - struct sock *sk = sk_atm(vcc); - - pr_debug("%p\n", vcc); - if (test_bit(ATM_VF_REGIS, &vcc->flags)) { - sigd_enq(vcc, as_close, NULL, NULL, NULL); - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); - if (test_bit(ATM_VF_RELEASED, &vcc->flags) || !sigd) - break; - schedule(); - } - finish_wait(sk_sleep(sk), &wait); - } - /* beware - socket is still in use by atmsigd until the last - as_indicate has been answered */ - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - atm_return(vcc, skb->truesize); - pr_debug("LISTEN REL\n"); - sigd_enq2(NULL, as_reject, vcc, NULL, NULL, &vcc->qos, 0); - dev_kfree_skb(skb); - } - clear_bit(ATM_VF_REGIS, &vcc->flags); - /* ... may retry later */ -} - -static int svc_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct atm_vcc *vcc; - - if (sk) { - vcc = ATM_SD(sock); - pr_debug("%p\n", vcc); - clear_bit(ATM_VF_READY, &vcc->flags); - /* - * VCC pointer is used as a reference, - * so we must not free it (thereby subjecting it to re-use) - * before all pending connections are closed - */ - svc_disconnect(vcc); - vcc_release(sock); - } - return 0; -} - -static int svc_bind(struct socket *sock, struct sockaddr_unsized *sockaddr, - int sockaddr_len) -{ - DEFINE_WAIT(wait); - struct sock *sk = sock->sk; - struct sockaddr_atmsvc *addr; - struct atm_vcc *vcc; - int error; - - if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) - return -EINVAL; - lock_sock(sk); - if (sock->state == SS_CONNECTED) { - error = -EISCONN; - goto out; - } - if (sock->state != SS_UNCONNECTED) { - error = -EINVAL; - goto out; - } - vcc = ATM_SD(sock); - addr = (struct sockaddr_atmsvc *) sockaddr; - if (addr->sas_family != AF_ATMSVC) { - error = -EAFNOSUPPORT; - goto out; - } - clear_bit(ATM_VF_BOUND, &vcc->flags); - /* failing rebind will kill old binding */ - /* @@@ check memory (de)allocation on rebind */ - if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) { - error = -EBADFD; - goto out; - } - vcc->local = *addr; - set_bit(ATM_VF_WAITING, &vcc->flags); - sigd_enq(vcc, as_bind, NULL, NULL, &vcc->local); - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); - if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) - break; - schedule(); - } - finish_wait(sk_sleep(sk), &wait); - clear_bit(ATM_VF_REGIS, &vcc->flags); /* doesn't count */ - if (!sigd) { - error = -EUNATCH; - goto out; - } - if (!sk->sk_err) - set_bit(ATM_VF_BOUND, &vcc->flags); - error = -sk->sk_err; -out: - release_sock(sk); - return error; -} - -static int svc_connect(struct socket *sock, struct sockaddr_unsized *sockaddr, - int sockaddr_len, int flags) -{ - DEFINE_WAIT(wait); - struct sock *sk = sock->sk; - struct sockaddr_atmsvc *addr; - struct atm_vcc *vcc = ATM_SD(sock); - int error; - - pr_debug("%p\n", vcc); - lock_sock(sk); - if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) { - error = -EINVAL; - goto out; - } - - switch (sock->state) { - default: - error = -EINVAL; - goto out; - case SS_CONNECTED: - error = -EISCONN; - goto out; - case SS_CONNECTING: - if (test_bit(ATM_VF_WAITING, &vcc->flags)) { - error = -EALREADY; - goto out; - } - sock->state = SS_UNCONNECTED; - if (sk->sk_err) { - error = -sk->sk_err; - goto out; - } - break; - case SS_UNCONNECTED: - addr = (struct sockaddr_atmsvc *) sockaddr; - if (addr->sas_family != AF_ATMSVC) { - error = -EAFNOSUPPORT; - goto out; - } - if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) { - error = -EBADFD; - goto out; - } - if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS || - vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) { - error = -EINVAL; - goto out; - } - if (!vcc->qos.txtp.traffic_class && - !vcc->qos.rxtp.traffic_class) { - error = -EINVAL; - goto out; - } - vcc->remote = *addr; - set_bit(ATM_VF_WAITING, &vcc->flags); - sigd_enq(vcc, as_connect, NULL, NULL, &vcc->remote); - if (flags & O_NONBLOCK) { - sock->state = SS_CONNECTING; - error = -EINPROGRESS; - goto out; - } - error = 0; - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { - schedule(); - if (!signal_pending(current)) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - continue; - } - pr_debug("*ABORT*\n"); - /* - * This is tricky: - * Kernel ---close--> Demon - * Kernel <--close--- Demon - * or - * Kernel ---close--> Demon - * Kernel <--error--- Demon - * or - * Kernel ---close--> Demon - * Kernel <--okay---- Demon - * Kernel <--close--- Demon - */ - sigd_enq(vcc, as_close, NULL, NULL, NULL); - while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - schedule(); - } - if (!sk->sk_err) - while (!test_bit(ATM_VF_RELEASED, &vcc->flags) && - sigd) { - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - schedule(); - } - clear_bit(ATM_VF_REGIS, &vcc->flags); - clear_bit(ATM_VF_RELEASED, &vcc->flags); - clear_bit(ATM_VF_CLOSE, &vcc->flags); - /* we're gone now but may connect later */ - error = -EINTR; - break; - } - finish_wait(sk_sleep(sk), &wait); - if (error) - goto out; - if (!sigd) { - error = -EUNATCH; - goto out; - } - if (sk->sk_err) { - error = -sk->sk_err; - goto out; - } - } - - vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp); - vcc->qos.txtp.pcr = 0; - vcc->qos.txtp.min_pcr = 0; - - error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci); - if (!error) - sock->state = SS_CONNECTED; - else - (void)svc_disconnect(vcc); -out: - release_sock(sk); - return error; -} - -static int svc_listen(struct socket *sock, int backlog) -{ - DEFINE_WAIT(wait); - struct sock *sk = sock->sk; - struct atm_vcc *vcc = ATM_SD(sock); - int error; - - pr_debug("%p\n", vcc); - lock_sock(sk); - /* let server handle listen on unbound sockets */ - if (test_bit(ATM_VF_SESSION, &vcc->flags)) { - error = -EINVAL; - goto out; - } - if (test_bit(ATM_VF_LISTEN, &vcc->flags)) { - error = -EADDRINUSE; - goto out; - } - set_bit(ATM_VF_WAITING, &vcc->flags); - sigd_enq(vcc, as_listen, NULL, NULL, &vcc->local); - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); - if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) - break; - schedule(); - } - finish_wait(sk_sleep(sk), &wait); - if (!sigd) { - error = -EUNATCH; - goto out; - } - set_bit(ATM_VF_LISTEN, &vcc->flags); - vcc_insert_socket(sk); - sk->sk_max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT; - error = -sk->sk_err; -out: - release_sock(sk); - return error; -} - -static int svc_accept(struct socket *sock, struct socket *newsock, - struct proto_accept_arg *arg) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - struct atmsvc_msg *msg; - struct atm_vcc *old_vcc = ATM_SD(sock); - struct atm_vcc *new_vcc; - int error; - - lock_sock(sk); - - error = svc_create(sock_net(sk), newsock, 0, arg->kern); - if (error) - goto out; - - new_vcc = ATM_SD(newsock); - - pr_debug("%p -> %p\n", old_vcc, new_vcc); - while (1) { - DEFINE_WAIT(wait); - - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - while (!(skb = skb_dequeue(&sk->sk_receive_queue)) && - sigd) { - if (test_bit(ATM_VF_RELEASED, &old_vcc->flags)) - break; - if (test_bit(ATM_VF_CLOSE, &old_vcc->flags)) { - error = -sk->sk_err; - break; - } - if (arg->flags & O_NONBLOCK) { - error = -EAGAIN; - break; - } - release_sock(sk); - schedule(); - lock_sock(sk); - if (signal_pending(current)) { - error = -ERESTARTSYS; - break; - } - prepare_to_wait(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - } - finish_wait(sk_sleep(sk), &wait); - if (error) - goto out; - if (!skb) { - error = -EUNATCH; - goto out; - } - msg = (struct atmsvc_msg *)skb->data; - new_vcc->qos = msg->qos; - set_bit(ATM_VF_HASQOS, &new_vcc->flags); - new_vcc->remote = msg->svc; - new_vcc->local = msg->local; - new_vcc->sap = msg->sap; - error = vcc_connect(newsock, msg->pvc.sap_addr.itf, - msg->pvc.sap_addr.vpi, - msg->pvc.sap_addr.vci); - dev_kfree_skb(skb); - sk_acceptq_removed(sk); - if (error) { - sigd_enq2(NULL, as_reject, old_vcc, NULL, NULL, - &old_vcc->qos, error); - error = error == -EAGAIN ? -EBUSY : error; - goto out; - } - /* wait should be short, so we ignore the non-blocking flag */ - set_bit(ATM_VF_WAITING, &new_vcc->flags); - sigd_enq(new_vcc, as_accept, old_vcc, NULL, NULL); - for (;;) { - prepare_to_wait(sk_sleep(sk_atm(new_vcc)), &wait, - TASK_UNINTERRUPTIBLE); - if (!test_bit(ATM_VF_WAITING, &new_vcc->flags) || !sigd) - break; - release_sock(sk); - schedule(); - lock_sock(sk); - } - finish_wait(sk_sleep(sk_atm(new_vcc)), &wait); - if (!sigd) { - error = -EUNATCH; - goto out; - } - if (!sk_atm(new_vcc)->sk_err) - break; - if (sk_atm(new_vcc)->sk_err != ERESTARTSYS) { - error = -sk_atm(new_vcc)->sk_err; - goto out; - } - } - newsock->state = SS_CONNECTED; -out: - release_sock(sk); - return error; -} - -static int svc_getname(struct socket *sock, struct sockaddr *sockaddr, - int peer) -{ - struct sockaddr_atmsvc *addr; - - addr = (struct sockaddr_atmsvc *) sockaddr; - memcpy(addr, peer ? &ATM_SD(sock)->remote : &ATM_SD(sock)->local, - sizeof(struct sockaddr_atmsvc)); - return sizeof(struct sockaddr_atmsvc); -} - -int svc_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) -{ - struct sock *sk = sk_atm(vcc); - DEFINE_WAIT(wait); - - set_bit(ATM_VF_WAITING, &vcc->flags); - sigd_enq2(vcc, as_modify, NULL, NULL, &vcc->local, qos, 0); - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); - if (!test_bit(ATM_VF_WAITING, &vcc->flags) || - test_bit(ATM_VF_RELEASED, &vcc->flags) || !sigd) { - break; - } - schedule(); - } - finish_wait(sk_sleep(sk), &wait); - if (!sigd) - return -EUNATCH; - return -sk->sk_err; -} - -static int svc_setsockopt(struct socket *sock, int level, int optname, - sockptr_t optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct atm_vcc *vcc = ATM_SD(sock); - int value, error = 0; - - lock_sock(sk); - switch (optname) { - case SO_ATMSAP: - if (level != SOL_ATM || optlen != sizeof(struct atm_sap)) { - error = -EINVAL; - goto out; - } - if (copy_from_sockptr(&vcc->sap, optval, optlen)) { - error = -EFAULT; - goto out; - } - set_bit(ATM_VF_HASSAP, &vcc->flags); - break; - case SO_MULTIPOINT: - if (level != SOL_ATM || optlen != sizeof(int)) { - error = -EINVAL; - goto out; - } - if (copy_from_sockptr(&value, optval, sizeof(int))) { - error = -EFAULT; - goto out; - } - if (value == 1) - set_bit(ATM_VF_SESSION, &vcc->flags); - else if (value == 0) - clear_bit(ATM_VF_SESSION, &vcc->flags); - else - error = -EINVAL; - break; - default: - error = vcc_setsockopt(sock, level, optname, optval, optlen); - } - -out: - release_sock(sk); - return error; -} - -static int svc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - int error = 0, len; - - lock_sock(sk); - if (!__SO_LEVEL_MATCH(optname, level) || optname != SO_ATMSAP) { - error = vcc_getsockopt(sock, level, optname, optval, optlen); - goto out; - } - if (get_user(len, optlen)) { - error = -EFAULT; - goto out; - } - if (len != sizeof(struct atm_sap)) { - error = -EINVAL; - goto out; - } - if (copy_to_user(optval, &ATM_SD(sock)->sap, sizeof(struct atm_sap))) { - error = -EFAULT; - goto out; - } -out: - release_sock(sk); - return error; -} - -static int svc_addparty(struct socket *sock, struct sockaddr *sockaddr, - int sockaddr_len, int flags) -{ - DEFINE_WAIT(wait); - struct sock *sk = sock->sk; - struct atm_vcc *vcc = ATM_SD(sock); - int error; - - lock_sock(sk); - set_bit(ATM_VF_WAITING, &vcc->flags); - sigd_enq(vcc, as_addparty, NULL, NULL, - (struct sockaddr_atmsvc *) sockaddr); - if (flags & O_NONBLOCK) { - error = -EINPROGRESS; - goto out; - } - pr_debug("added wait queue\n"); - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) - break; - schedule(); - } - finish_wait(sk_sleep(sk), &wait); - error = -xchg(&sk->sk_err_soft, 0); -out: - release_sock(sk); - return error; -} - -static int svc_dropparty(struct socket *sock, int ep_ref) -{ - DEFINE_WAIT(wait); - struct sock *sk = sock->sk; - struct atm_vcc *vcc = ATM_SD(sock); - int error; - - lock_sock(sk); - set_bit(ATM_VF_WAITING, &vcc->flags); - sigd_enq2(vcc, as_dropparty, NULL, NULL, NULL, NULL, ep_ref); - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) - break; - schedule(); - } - finish_wait(sk_sleep(sk), &wait); - if (!sigd) { - error = -EUNATCH; - goto out; - } - error = -xchg(&sk->sk_err_soft, 0); -out: - release_sock(sk); - return error; -} - -static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int error, ep_ref; - struct sockaddr_atmsvc sa; - struct atm_vcc *vcc = ATM_SD(sock); - - switch (cmd) { - case ATM_ADDPARTY: - if (!test_bit(ATM_VF_SESSION, &vcc->flags)) - return -EINVAL; - if (copy_from_user(&sa, (void __user *) arg, sizeof(sa))) - return -EFAULT; - error = svc_addparty(sock, (struct sockaddr *)&sa, sizeof(sa), - 0); - break; - case ATM_DROPPARTY: - if (!test_bit(ATM_VF_SESSION, &vcc->flags)) - return -EINVAL; - if (copy_from_user(&ep_ref, (void __user *) arg, sizeof(int))) - return -EFAULT; - error = svc_dropparty(sock, ep_ref); - break; - default: - error = vcc_ioctl(sock, cmd, arg); - } - - return error; -} - -#ifdef CONFIG_COMPAT -static int svc_compat_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg) -{ - /* The definition of ATM_ADDPARTY uses the size of struct atm_iobuf. - But actually it takes a struct sockaddr_atmsvc, which doesn't need - compat handling. So all we have to do is fix up cmd... */ - if (cmd == COMPAT_ATM_ADDPARTY) - cmd = ATM_ADDPARTY; - - if (cmd == ATM_ADDPARTY || cmd == ATM_DROPPARTY) - return svc_ioctl(sock, cmd, arg); - else - return vcc_compat_ioctl(sock, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - -static const struct proto_ops svc_proto_ops = { - .family = PF_ATMSVC, - .owner = THIS_MODULE, - - .release = svc_release, - .bind = svc_bind, - .connect = svc_connect, - .socketpair = sock_no_socketpair, - .accept = svc_accept, - .getname = svc_getname, - .poll = vcc_poll, - .ioctl = svc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = svc_compat_ioctl, -#endif - .gettstamp = sock_gettstamp, - .listen = svc_listen, - .shutdown = svc_shutdown, - .setsockopt = svc_setsockopt, - .getsockopt = svc_getsockopt, - .sendmsg = vcc_sendmsg, - .recvmsg = vcc_recvmsg, - .mmap = sock_no_mmap, -}; - - -static int svc_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - int error; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - sock->ops = &svc_proto_ops; - error = vcc_create(net, sock, protocol, AF_ATMSVC, kern); - if (error) - return error; - ATM_SD(sock)->local.sas_family = AF_ATMSVC; - ATM_SD(sock)->remote.sas_family = AF_ATMSVC; - return 0; -} - -static const struct net_proto_family svc_family_ops = { - .family = PF_ATMSVC, - .create = svc_create, - .owner = THIS_MODULE, -}; - - -/* - * Initialize the ATM SVC protocol family - */ - -int __init atmsvc_init(void) -{ - return sock_register(&svc_family_ops); -} - -void atmsvc_exit(void) -{ - sock_unregister(PF_ATMSVC); -} diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index d3c4d4143c14..5d7456f1240d 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -30,5 +30,6 @@ batman-adv-$(CONFIG_BATMAN_ADV_TRACING) += trace.o batman-adv-y += tp_meter.o batman-adv-y += translation-table.o batman-adv-y += tvlv.o +batman-adv-y += version.o CFLAGS_trace.o := -I$(src) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b8b1b997960a..7588e64e7ba6 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -194,14 +195,17 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface) get_random_bytes(&random_seqno, sizeof(random_seqno)); atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno); - hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN; - ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC); + hard_iface->bat_iv.ogm_buff.len = BATADV_OGM_HLEN; + hard_iface->bat_iv.ogm_buff.capacity = BATADV_OGM_HLEN; + hard_iface->bat_iv.ogm_buff.header_length = BATADV_OGM_HLEN; + + ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff.capacity, GFP_ATOMIC); if (!ogm_buff) { mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); return -ENOMEM; } - hard_iface->bat_iv.ogm_buff = ogm_buff; + hard_iface->bat_iv.ogm_buff.buf = ogm_buff; batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; batadv_ogm_packet->packet_type = BATADV_IV_OGM; @@ -220,12 +224,13 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) { mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex); - kfree(hard_iface->bat_iv.ogm_buff); - hard_iface->bat_iv.ogm_buff = NULL; + kfree(hard_iface->bat_iv.ogm_buff.buf); + memset(&hard_iface->bat_iv.ogm_buff, 0, + sizeof(hard_iface->bat_iv.ogm_buff)); mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); - cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work); + disable_delayed_work_sync(&hard_iface->bat_iv.reschedule_work); } static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface) @@ -235,7 +240,7 @@ static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface) mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex); - ogm_buff = hard_iface->bat_iv.ogm_buff; + ogm_buff = hard_iface->bat_iv.ogm_buff.buf; if (!ogm_buff) goto unlock; @@ -257,7 +262,7 @@ batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface) mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex); - ogm_buff = hard_iface->bat_iv.ogm_buff; + ogm_buff = hard_iface->bat_iv.ogm_buff.buf; if (!ogm_buff) goto unlock; @@ -274,7 +279,7 @@ batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv) { unsigned int msecs; - msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER; + msecs = READ_ONCE(bat_priv->orig_interval) - BATADV_JITTER; msecs += get_random_u32_below(2 * BATADV_JITTER); return jiffies + msecs_to_jiffies(msecs); @@ -289,7 +294,7 @@ static unsigned long batadv_iv_ogm_fwd_send_time(void) /* apply hop penalty for a normal link */ static u8 batadv_hop_penalty(u8 tq, const struct batadv_priv *bat_priv) { - int hop_penalty = atomic_read(&bat_priv->hop_penalty); + int hop_penalty = READ_ONCE(bat_priv->hop_penalty); int new_tq; new_tq = tq * (BATADV_TQ_MAX_VALUE - hop_penalty); @@ -555,7 +560,7 @@ static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, unsigned int skb_size; atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left; - if (atomic_read(&bat_priv->aggregated_ogms)) + if (READ_ONCE(bat_priv->aggregated_ogms)) skb_size = max_t(unsigned int, BATADV_MAX_AGGREGATION_BYTES, packet_len); else @@ -641,6 +646,7 @@ static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, struct batadv_ogm_packet *batadv_ogm_packet; bool direct_link; unsigned long max_aggregation_jiffies; + bool aggregated_ogms; batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff; direct_link = !!(batadv_ogm_packet->flags & BATADV_DIRECTLINK); @@ -648,8 +654,10 @@ static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, /* find position for the packet in the forward queue */ spin_lock_bh(&bat_priv->forw_bat_list_lock); + aggregated_ogms = READ_ONCE(bat_priv->aggregated_ogms); + /* own packets are not to be aggregated */ - if (atomic_read(&bat_priv->aggregated_ogms) && !own_packet) { + if (aggregated_ogms && !own_packet) { hlist_for_each_entry(forw_packet_pos, &bat_priv->forw_bat_list, list) { if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet, @@ -675,7 +683,7 @@ static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, * we hold it back for a while, so that it might be aggregated * later on */ - if (!own_packet && atomic_read(&bat_priv->aggregated_ogms)) + if (!own_packet && aggregated_ogms) send_time += max_aggregation_jiffies; return batadv_iv_ogm_aggregate_new(packet_buff, packet_len, @@ -792,10 +800,9 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface) static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); - unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff; + struct batadv_ogm_buf *ogm_buff = &hard_iface->bat_iv.ogm_buff; struct batadv_ogm_packet *batadv_ogm_packet; struct batadv_hard_iface *primary_if, *tmp_hard_iface; - int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len; struct list_head *iter; u32 seqno; u16 tvlv_len = 0; @@ -807,7 +814,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); /* interface already disabled by batadv_iv_ogm_iface_disable */ - if (!*ogm_buff) + if (!ogm_buff->buf) return; /* the interface gets activated here to avoid race conditions between @@ -826,9 +833,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) * appended as it may alter the tt tvlv container */ batadv_tt_local_commit_changes(bat_priv); - ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, - ogm_buff_len, - BATADV_OGM_HLEN); + ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff); if (ret < 0) { reschedule = true; goto out; @@ -837,7 +842,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) tvlv_len = ret; } - batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff); + batadv_ogm_packet = ogm_buff->buf; batadv_ogm_packet->tvlv_len = htons(tvlv_len); /* change sequence number to network order */ @@ -853,7 +858,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) /* OGMs from secondary interfaces are only scheduled on their * respective interfaces. */ - scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, + scheduled = batadv_iv_ogm_queue_add(bat_priv, ogm_buff->buf, ogm_buff->len, hard_iface, hard_iface, 1, send_time); if (!scheduled) reschedule = true; @@ -869,8 +874,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) if (!kref_get_unless_zero(&tmp_hard_iface->refcount)) continue; - scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, - *ogm_buff_len, hard_iface, + scheduled = batadv_iv_ogm_queue_add(bat_priv, ogm_buff->buf, + ogm_buff->len, hard_iface, tmp_hard_iface, 1, send_time); batadv_hardif_put(tmp_hard_iface); @@ -888,7 +893,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) */ queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_iv.reschedule_work, - msecs_to_jiffies(atomic_read(&bat_priv->orig_interval))); + msecs_to_jiffies(READ_ONCE(bat_priv->orig_interval))); } batadv_hardif_put(primary_if); @@ -1214,7 +1219,7 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, inv_asym_penalty = BATADV_TQ_MAX_VALUE * neigh_rq_inv_cube; inv_asym_penalty /= neigh_rq_max_cube; tq_asym_penalty = BATADV_TQ_MAX_VALUE - inv_asym_penalty; - tq_iface_hop_penalty -= atomic_read(&if_incoming->hop_penalty); + tq_iface_hop_penalty -= READ_ONCE(if_incoming->hop_penalty); /* penalize if the OGM is forwarded on the same interface. WiFi * interfaces and other half duplex devices suffer from throughput @@ -1773,7 +1778,7 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work) delayed_work); bat_priv = netdev_priv(forw_packet->if_incoming->mesh_iface); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { + if (READ_ONCE(bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { dropped = true; goto out; } @@ -2321,7 +2326,7 @@ static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface) static void batadv_iv_init_sel_class(struct batadv_priv *bat_priv) { /* set default TQ difference threshold to 20 */ - atomic_set(&bat_priv->gw.sel_class, 20); + WRITE_ONCE(bat_priv->gw.sel_class, 20); } static struct batadv_gw_node * @@ -2353,7 +2358,7 @@ batadv_iv_gw_get_best_gw_node(struct batadv_priv *bat_priv) tq_avg = router_ifinfo->bat_iv.tq_avg; - switch (atomic_read(&bat_priv->gw.sel_class)) { + switch (READ_ONCE(bat_priv->gw.sel_class)) { case 1: /* fast connection */ tmp_gw_factor = tq_avg * tq_avg; tmp_gw_factor *= gw_node->bandwidth_down; @@ -2407,13 +2412,14 @@ static bool batadv_iv_gw_is_eligible(struct batadv_priv *bat_priv, { struct batadv_neigh_ifinfo *router_orig_ifinfo = NULL; struct batadv_neigh_ifinfo *router_gw_ifinfo = NULL; + u32 sel_class = READ_ONCE(bat_priv->gw.sel_class); struct batadv_neigh_node *router_gw = NULL; struct batadv_neigh_node *router_orig = NULL; u8 gw_tq_avg, orig_tq_avg; bool ret = false; /* dynamic re-election is performed only on fast or late switch */ - if (atomic_read(&bat_priv->gw.sel_class) <= 2) + if (sel_class <= 2) return false; router_gw = batadv_orig_router_get(curr_gw_orig, BATADV_IF_DEFAULT); @@ -2448,8 +2454,7 @@ static bool batadv_iv_gw_is_eligible(struct batadv_priv *bat_priv, /* if the routing class is greater than 3 the value tells us how much * greater the TQ value of the new gateway must be */ - if ((atomic_read(&bat_priv->gw.sel_class) > 3) && - (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw.sel_class))) + if (sel_class > 3 && orig_tq_avg - gw_tq_avg < sel_class) goto out; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index de9444714264..fe7c0113d0df 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -7,8 +7,8 @@ #include "bat_v.h" #include "main.h" -#include #include +#include #include #include #include @@ -505,7 +505,7 @@ static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1, static void batadv_v_init_sel_class(struct batadv_priv *bat_priv) { /* set default throughput difference threshold to 5Mbps */ - atomic_set(&bat_priv->gw.sel_class, 50); + WRITE_ONCE(bat_priv->gw.sel_class, 50); } /** @@ -587,7 +587,7 @@ batadv_v_gw_get_best_gw_node(struct batadv_priv *bat_priv) } /** - * batadv_v_gw_is_eligible() - check if a originator would be selected as GW + * batadv_v_gw_is_eligible() - check if an originator would be selected as GW * @bat_priv: the bat priv with all the mesh interface information * @curr_gw_orig: originator representing the currently selected GW * @orig_node: the originator representing the new candidate @@ -602,7 +602,7 @@ static bool batadv_v_gw_is_eligible(struct batadv_priv *bat_priv, u32 gw_throughput, orig_throughput, threshold; bool ret = false; - threshold = atomic_read(&bat_priv->gw.sel_class); + threshold = READ_ONCE(bat_priv->gw.sel_class); curr_gw = batadv_gw_node_get(bat_priv, curr_gw_orig); if (!curr_gw) { @@ -812,13 +812,15 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) /* enable link throughput auto-detection by setting the throughput * override to zero */ - atomic_set(&hard_iface->bat_v.throughput_override, 0); - atomic_set(&hard_iface->bat_v.elp_interval, 500); + WRITE_ONCE(hard_iface->bat_v.throughput_override, 0); + WRITE_ONCE(hard_iface->bat_v.elp_interval, 500); hard_iface->bat_v.aggr_len = 0; skb_queue_head_init(&hard_iface->bat_v.aggr_list); INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, batadv_v_ogm_aggr_work); + /* make sure it doesn't run until interface gets enabled */ + disable_delayed_work(&hard_iface->bat_v.aggr_wq); } /** diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index fdc2abe96d77..4841f0f1a9b1 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,7 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface) { unsigned int msecs; - msecs = atomic_read(&hard_iface->bat_v.elp_interval) - BATADV_JITTER; + msecs = READ_ONCE(hard_iface->bat_v.elp_interval) - BATADV_JITTER; msecs += get_random_u32_below(2 * BATADV_JITTER); queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.elp_wq, @@ -85,6 +86,7 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh, struct ethtool_link_ksettings link_settings; struct net_device *real_netdev; struct station_info sinfo; + u32 wifi_flags; u32 throughput; int ret; @@ -97,7 +99,7 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh, /* if the user specified a customised value for this interface, then * return it directly */ - throughput = atomic_read(&hard_iface->bat_v.throughput_override); + throughput = READ_ONCE(hard_iface->bat_v.throughput_override); if (throughput != 0) { *pthroughput = throughput; return true; @@ -106,13 +108,14 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh, /* if this is a wireless device, then ask its throughput through * cfg80211 API */ - if (batadv_is_wifi_hardif(hard_iface)) { - if (!batadv_is_cfg80211_hardif(hard_iface)) + wifi_flags = batadv_hardif_get_wifi_flags(hard_iface); + if (batadv_is_wifi(wifi_flags)) { + if (!batadv_is_cfg80211(wifi_flags)) /* unsupported WiFi driver version */ goto default_throughput; /* only use rtnl_trylock because the elp worker will be cancelled while - * the rntl_lock is held. the cancel_delayed_work_sync() would otherwise + * the rntl_lock is held. the disable_delayed_work_sync() would otherwise * wait forever when the elp work_item was started and it is then also * trying to rtnl_lock */ @@ -159,7 +162,7 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh, } /* only use rtnl_trylock because the elp worker will be cancelled while - * the rntl_lock is held. the cancel_delayed_work_sync() would otherwise + * the rntl_lock is held. the disable_delayed_work_sync() would otherwise * wait forever when the elp work_item was started and it is then also * trying to rtnl_lock */ @@ -304,7 +307,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work) hard_iface = container_of(bat_v, struct batadv_hard_iface, bat_v); bat_priv = netdev_priv(hard_iface->mesh_iface); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + if (READ_ONCE(bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out; /* we are in the process of shutting this interface down */ @@ -322,7 +325,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work) elp_packet = (struct batadv_elp_packet *)skb->data; elp_packet->seqno = htonl(atomic_read(&hard_iface->bat_v.elp_seqno)); - elp_interval = atomic_read(&hard_iface->bat_v.elp_interval); + elp_interval = READ_ONCE(hard_iface->bat_v.elp_interval); elp_packet->elp_interval = htonl(elp_interval); batadv_dbg(BATADV_DBG_BATMAN, bat_priv, @@ -444,7 +447,7 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) */ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface) { - cancel_delayed_work_sync(&hard_iface->bat_v.elp_wq); + disable_delayed_work_sync(&hard_iface->bat_v.elp_wq); dev_kfree_skb(hard_iface->bat_v.elp_skb); hard_iface->bat_v.elp_skb = NULL; diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index d66ca77b1aaa..81926ef9c02c 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -8,7 +8,9 @@ #include "main.h" #include +#include #include +#include #include #include #include @@ -105,7 +107,7 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) if (delayed_work_pending(&bat_priv->bat_v.ogm_wq)) return; - msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER; + msecs = READ_ONCE(bat_priv->orig_interval) - BATADV_JITTER; msecs += get_random_u32_below(2 * BATADV_JITTER); queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.ogm_wq, msecs_to_jiffies(msecs)); @@ -246,7 +248,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, return; } - if (!atomic_read(&bat_priv->aggregated_ogms)) { + if (!READ_ONCE(bat_priv->aggregated_ogms)) { batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface); return; } @@ -268,39 +270,35 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv) { struct batadv_hard_iface *hard_iface; struct batadv_ogm2_packet *ogm_packet; + struct batadv_ogm_buf *ogm_buff; struct sk_buff *skb, *skb_tmp; - unsigned char **ogm_buff; struct list_head *iter; - int *ogm_buff_len; u16 tvlv_len; int ret; lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + if (READ_ONCE(bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out; ogm_buff = &bat_priv->bat_v.ogm_buff; - ogm_buff_len = &bat_priv->bat_v.ogm_buff_len; /* tt changes have to be committed before the tvlv data is * appended as it may alter the tt tvlv container */ batadv_tt_local_commit_changes(bat_priv); - ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, - ogm_buff_len, - BATADV_OGM2_HLEN); + ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff); if (ret < 0) goto reschedule; tvlv_len = ret; - skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len); + skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff->len); if (!skb) goto reschedule; skb_reserve(skb, ETH_HLEN); - skb_put_data(skb, *ogm_buff, *ogm_buff_len); + skb_put_data(skb, ogm_buff->buf, ogm_buff->len); ogm_packet = (struct batadv_ogm2_packet *)skb->data; ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno)); @@ -417,6 +415,8 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + enable_delayed_work(&hard_iface->bat_v.aggr_wq); + batadv_v_ogm_start_queue_timer(hard_iface); batadv_v_ogm_start_timer(bat_priv); @@ -429,7 +429,7 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) */ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) { - cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + disable_delayed_work_sync(&hard_iface->bat_v.aggr_wq); spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); batadv_v_ogm_aggr_list_free(hard_iface); @@ -446,10 +446,10 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface) struct batadv_ogm2_packet *ogm_packet; mutex_lock(&bat_priv->bat_v.ogm_buff_mutex); - if (!bat_priv->bat_v.ogm_buff) + if (!bat_priv->bat_v.ogm_buff.buf) goto unlock; - ogm_packet = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff; + ogm_packet = bat_priv->bat_v.ogm_buff.buf; ether_addr_copy(ogm_packet->orig, primary_iface->net_dev->dev_addr); unlock: @@ -484,9 +484,9 @@ static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv, struct batadv_hard_iface *if_outgoing, u32 throughput) { - int if_hop_penalty = atomic_read(&if_incoming->hop_penalty); - int hop_penalty = atomic_read(&bat_priv->hop_penalty); - int hop_penalty_max = BATADV_TQ_MAX_VALUE; + u32 if_hop_penalty = READ_ONCE(if_incoming->hop_penalty); + u32 hop_penalty = READ_ONCE(bat_priv->hop_penalty); + u32 hop_penalty_max = BATADV_TQ_MAX_VALUE; /* Apply per hardif hop penalty */ throughput = throughput * (hop_penalty_max - if_hop_penalty) / @@ -515,7 +515,7 @@ static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv, * @bat_priv: the bat priv with all the mesh interface information * @ogm_received: previously received OGM to be forwarded * @orig_node: the originator which has been updated - * @neigh_node: the neigh_node through with the OGM has been received + * @neigh_node: the neigh_node through which the OGM has been received * @if_incoming: the interface on which this OGM was received on * @if_outgoing: the interface to which the OGM has to be forwarded to * @@ -602,7 +602,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, * @bat_priv: the bat priv with all the mesh interface information * @ogm2: OGM2 structure * @orig_node: Originator structure for which the OGM has been received - * @neigh_node: the neigh_node through with the OGM has been received + * @neigh_node: the neigh_node through which the OGM has been received * @if_incoming: the interface where this packet was received * @if_outgoing: the interface for which the packet should be considered * @@ -686,7 +686,7 @@ static int batadv_v_ogm_metric_update(struct batadv_priv *bat_priv, * @ethhdr: the Ethernet header of the OGM2 * @ogm2: OGM2 structure * @orig_node: Originator structure for which the OGM has been received - * @neigh_node: the neigh_node through with the OGM has been received + * @neigh_node: the neigh_node through which the OGM has been received * @if_incoming: the interface where this packet was received * @if_outgoing: the interface for which the packet should be considered * @@ -721,7 +721,7 @@ static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv, * don't route towards it */ router = batadv_orig_router_get(orig_node, if_outgoing); - if (router && router->orig_node != orig_node && !orig_neigh_router) { + if (router && ACCESS_PRIVATE(router, orig_node_id) != orig_node && !orig_neigh_router) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: OGM via unknown neighbor!\n"); goto out; @@ -781,7 +781,7 @@ static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv, * @ethhdr: the Ethernet header of the OGM2 * @ogm2: OGM2 structure * @orig_node: Originator structure for which the OGM has been received - * @neigh_node: the neigh_node through with the OGM has been received + * @neigh_node: the neigh_node through which the OGM has been received * @if_incoming: the interface where this packet was received * @if_outgoing: the interface for which the packet should be considered */ @@ -984,7 +984,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, * @if_incoming: the interface where this OGM has been received * * Return: NET_RX_SUCCESS and consume the skb on success or returns NET_RX_DROP - * (without freeing the skb) on failure + * (freeing the skb) on failure */ int batadv_v_ogm_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming) @@ -1050,12 +1050,15 @@ int batadv_v_ogm_init(struct batadv_priv *bat_priv) unsigned char *ogm_buff; u32 random_seqno; - bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN; - ogm_buff = kzalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC); + bat_priv->bat_v.ogm_buff.len = BATADV_OGM2_HLEN; + bat_priv->bat_v.ogm_buff.capacity = BATADV_OGM2_HLEN; + bat_priv->bat_v.ogm_buff.header_length = BATADV_OGM2_HLEN; + + ogm_buff = kzalloc(bat_priv->bat_v.ogm_buff.capacity, GFP_ATOMIC); if (!ogm_buff) return -ENOMEM; - bat_priv->bat_v.ogm_buff = ogm_buff; + bat_priv->bat_v.ogm_buff.buf = ogm_buff; ogm_packet = (struct batadv_ogm2_packet *)ogm_buff; ogm_packet->packet_type = BATADV_OGM2; ogm_packet->version = BATADV_COMPAT_VERSION; @@ -1079,13 +1082,12 @@ int batadv_v_ogm_init(struct batadv_priv *bat_priv) */ void batadv_v_ogm_free(struct batadv_priv *bat_priv) { - cancel_delayed_work_sync(&bat_priv->bat_v.ogm_wq); + disable_delayed_work_sync(&bat_priv->bat_v.ogm_wq); mutex_lock(&bat_priv->bat_v.ogm_buff_mutex); - kfree(bat_priv->bat_v.ogm_buff); - bat_priv->bat_v.ogm_buff = NULL; - bat_priv->bat_v.ogm_buff_len = 0; + kfree(bat_priv->bat_v.ogm_buff.buf); + memset(&bat_priv->bat_v.ogm_buff, 0, sizeof(bat_priv->bat_v.ogm_buff)); mutex_unlock(&bat_priv->bat_v.ogm_buff_mutex); } diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index 2c49b2711650..67cb356332bf 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -24,7 +24,7 @@ static void batadv_bitmap_shift_left(unsigned long *seq_bits, s32 n) * batadv_bit_get_packet() - receive and process one packet within the sequence * number window * @priv: the bat priv with all the mesh interface information - * @seq_bits: pointer to the sequence number receive packet + * @seq_bits: pointer to the sequence number bitmap of received packets * @seq_num_diff: difference between the current/received sequence number and * the last sequence number * @set_mark: whether this packet should be marked in seq_bits diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index 37f7ae413bc6..2b9f5dbb3ec4 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -17,7 +17,7 @@ /** * batadv_test_bit() - check if bit is set in the current window * - * @seq_bits: pointer to the sequence number receive packet + * @seq_bits: pointer to the sequence number bitmap of received packets * @last_seqno: latest sequence number in seq_bits * @curr_seqno: sequence number to test for * diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index ffe854018bd3..5c73f6ba16cf 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -122,7 +123,7 @@ static bool batadv_compare_backbone_gw(const struct hlist_node *node, * @node: list node of the first entry to compare * @data2: pointer to the second claims * - * Return: true if the claim have the same data, 0 otherwise + * Return: true if the claims have the same data, false otherwise */ static bool batadv_compare_claim(const struct hlist_node *node, const void *data2) @@ -512,7 +513,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, return NULL; entry->vid = vid; - entry->lasttime = jiffies; + WRITE_ONCE(entry->lasttime, jiffies); entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; spin_lock_init(&entry->crc_lock); @@ -580,7 +581,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, if (unlikely(!backbone_gw)) return; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_backbone_gw_put(backbone_gw); } @@ -714,7 +715,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, ether_addr_copy(claim->addr, mac); spin_lock_init(&claim->backbone_lock); claim->vid = vid; - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); kref_get(&backbone_gw->refcount); claim->backbone_gw = backbone_gw; kref_init(&claim->refcount); @@ -736,7 +737,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, return; } } else { - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); if (claim->backbone_gw == backbone_gw) /* no need to register a new backbone */ goto claim_free_ref; @@ -769,7 +770,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, spin_lock_bh(&backbone_gw->crc_lock); backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); spin_unlock_bh(&backbone_gw->crc_lock); - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); claim_free_ref: batadv_claim_put(claim); @@ -858,7 +859,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, return true; /* handle as ANNOUNCE frame */ - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); crc = ntohs(*((__force __be16 *)(&an_addr[4]))); batadv_dbg(BATADV_DBG_BLA, bat_priv, @@ -1253,7 +1254,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) head, hash_entry) { if (now) goto purge_now; - if (!batadv_has_timed_out(backbone_gw->lasttime, + if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime), BATADV_BLA_BACKBONE_TIMEOUT)) continue; @@ -1281,7 +1282,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) if (purged) { /* reference for pending report_work */ - if (cancel_work_sync(&backbone_gw->report_work)) + if (disable_work_sync(&backbone_gw->report_work)) batadv_backbone_gw_put(backbone_gw); /* reference for hash_entry */ @@ -1334,7 +1335,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, primary_if->net_dev->dev_addr)) goto skip; - if (!batadv_has_timed_out(claim->lasttime, + if (!batadv_has_timed_out(READ_ONCE(claim->lasttime), BATADV_BLA_CLAIM_TIMEOUT)) goto skip; @@ -1379,7 +1380,7 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv, bat_priv->bla.claim_dest.group = group; /* purge everything when bridge loop avoidance is turned off */ - if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(bat_priv->bridge_loop_avoidance)) oldif = NULL; if (!oldif) { @@ -1483,7 +1484,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) batadv_bla_purge_claims(bat_priv, primary_if, 0); batadv_bla_purge_backbone_gw(bat_priv, 0); - if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(bat_priv->bridge_loop_avoidance)) goto out; if (atomic_dec_and_test(&bat_priv->bla.loopdetect_next)) { @@ -1494,7 +1495,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) eth_random_addr(bat_priv->bla.loopdetect_addr); bat_priv->bla.loopdetect_addr[0] = 0xba; bat_priv->bla.loopdetect_addr[1] = 0xbe; - bat_priv->bla.loopdetect_lasttime = jiffies; + WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies); atomic_set(&bat_priv->bla.loopdetect_next, BATADV_BLA_LOOPDETECT_PERIODS); @@ -1515,7 +1516,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) primary_if->net_dev->dev_addr)) continue; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_bla_send_announce(bat_priv, backbone_gw); if (send_loopdetect) @@ -1782,7 +1783,7 @@ bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, u8 *orig, struct batadv_bla_backbone_gw *backbone_gw; int i; - if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(bat_priv->bridge_loop_avoidance)) return false; if (!hash) @@ -1820,7 +1821,7 @@ bool batadv_bla_is_backbone_gw(struct sk_buff *skb, struct batadv_bla_backbone_gw *backbone_gw; unsigned short vid; - if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(orig_node->bat_priv->bridge_loop_avoidance)) return false; /* first, find out the vid. */ @@ -1849,7 +1850,7 @@ void batadv_bla_free(struct batadv_priv *bat_priv) { struct batadv_hard_iface *primary_if; - cancel_delayed_work_sync(&bat_priv->bla.work); + disable_delayed_work_sync(&bat_priv->bla.work); primary_if = batadv_primary_if_get_selected(bat_priv); if (bat_priv->bla.claim_hash) { @@ -1899,7 +1900,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, /* If the packet came too late, don't forward it on the mesh * but don't consider that as loop. It might be a coincidence. */ - if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime, + if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime), BATADV_BLA_LOOPDETECT_TIMEOUT)) return true; @@ -1952,7 +1953,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (!primary_if) goto handled; - if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(bat_priv->bridge_loop_avoidance)) goto allow; if (batadv_bla_loopdetect_check(bat_priv, skb, primary_if, vid)) @@ -2014,7 +2015,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (own_claim) { /* ... allow it in any case */ - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); goto allow; } @@ -2084,7 +2085,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (!primary_if) goto out; - if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(bat_priv->bridge_loop_avoidance)) goto allow; if (batadv_bla_process_claim(bat_priv, primary_if, skb)) @@ -2116,7 +2117,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* if yes, the client has roamed and we have * to unclaim it. */ - if (batadv_has_timed_out(claim->lasttime, 100)) { + if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) { /* only unclaim if the last claim entry is * older than 100 ms to make sure we really * have a roaming client here. @@ -2361,7 +2362,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, backbone_crc = backbone_gw->crc; spin_unlock_bh(&backbone_gw->crc_lock); - msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime); + msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime)); if (is_own) if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) { @@ -2504,7 +2505,7 @@ bool batadv_bla_check_claim(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if = NULL; bool ret = true; - if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + if (!READ_ONCE(bat_priv->bridge_loop_avoidance)) return ret; primary_if = batadv_primary_if_get_selected(bat_priv); diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index 8673a265995f..06c7dfb8c677 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -21,7 +21,7 @@ * detect frame sent by bridge loop avoidance * @mac: mac address to check * - * Return: true if the it looks like a loop detect frame + * Return: true if it looks like a loop detect frame * (mac starts with BA:BE), false otherwise */ static inline bool batadv_bla_is_loopdetect_mac(const uint8_t *mac) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 0a8bd95e2f99..aaea155b9403 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -742,7 +743,7 @@ static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv) { char dat_mode; - dat_mode = atomic_read(&bat_priv->distributed_arp_table); + dat_mode = READ_ONCE(bat_priv->distributed_arp_table); switch (dat_mode) { case 0: @@ -837,7 +838,7 @@ void batadv_dat_free(struct batadv_priv *bat_priv) batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1); batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1); - cancel_delayed_work_sync(&bat_priv->dat.work); + disable_delayed_work_sync(&bat_priv->dat.work); batadv_dat_hash_free(bat_priv); } @@ -1138,7 +1139,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, int hdr_size = 0; unsigned short vid; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; vid = batadv_dat_get_vid(skb, &hdr_size); @@ -1233,7 +1234,7 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, unsigned short vid; int err; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; vid = batadv_dat_get_vid(skb, &hdr_size); @@ -1295,7 +1296,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, int hdr_size = 0; unsigned short vid; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) return; vid = batadv_dat_get_vid(skb, &hdr_size); @@ -1343,7 +1344,7 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, bool dropped = false; unsigned short vid; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; vid = batadv_dat_get_vid(skb, &hdr_size); @@ -1713,7 +1714,7 @@ void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv, u8 chaddr[BATADV_DHCP_CHADDR_LEN]; __be32 ip_src, yiaddr; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) return; if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) @@ -1743,7 +1744,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, __be16 proto; u8 *hw_src; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) return; if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN))) @@ -1788,7 +1789,7 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, int hdr_size = sizeof(struct batadv_bcast_packet); unsigned short vid; - if (!atomic_read(&bat_priv->distributed_arp_table)) + if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; /* If this packet is an ARP_REQUEST and the node already has the diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index e9553db42349..1e42cf99f8b3 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -308,7 +309,8 @@ batadv_frag_merge_packets(struct hlist_head *chain) * batadv_skb_is_frag() - check if newly merged skb contains unicast fragment * @skb: newly merged skb * - * Return: if newly merged skb is of type BATADV_UNICAST_FRAG + * Return: true if the newly merged skb is of type BATADV_UNICAST_FRAG, false + * otherwise */ static bool batadv_skb_is_frag(struct sk_buff *skb) { diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index a9d0346e8332..305488a74a25 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -170,7 +171,7 @@ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv) { struct batadv_gw_node *curr_gw; - if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT) + if (READ_ONCE(bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT) return; curr_gw = batadv_gw_get_selected_gw_node(bat_priv); @@ -202,7 +203,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) struct batadv_neigh_ifinfo *router_ifinfo = NULL; char gw_addr[18] = { '\0' }; - if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT) + if (READ_ONCE(bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT) goto out; if (!bat_priv->algo_ops->gw.get_best_gw_node) @@ -210,7 +211,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) curr_gw = batadv_gw_get_selected_gw_node(bat_priv); - if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw) + if (atomic_xchg(&bat_priv->gw.reselect, 0) == 0 && curr_gw) goto out; /* if gw.reselect is set to 1 it means that a previous call to @@ -702,7 +703,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!gw_node) goto out; - switch (atomic_read(&bat_priv->gw.mode)) { + switch (READ_ONCE(bat_priv->gw.mode)) { case BATADV_GW_MODE_SERVER: /* If we are a GW then we are our best GW. We can artificially * set the tq towards ourself as the maximum value diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 315fa90f0c94..675ebf098d4e 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -7,8 +7,8 @@ #include "gateway_common.h" #include "main.h" -#include #include +#include #include #include #include @@ -25,10 +25,10 @@ void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv) { struct batadv_tvlv_gateway_data gw; + enum batadv_gw_modes gw_mode; u32 down, up; - char gw_mode; - gw_mode = atomic_read(&bat_priv->gw.mode); + gw_mode = READ_ONCE(bat_priv->gw.mode); switch (gw_mode) { case BATADV_GW_MODE_OFF: @@ -36,8 +36,8 @@ void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv) batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); break; case BATADV_GW_MODE_SERVER: - down = atomic_read(&bat_priv->gw.bandwidth_down); - up = atomic_read(&bat_priv->gw.bandwidth_up); + down = READ_ONCE(bat_priv->gw.bandwidth_down); + up = READ_ONCE(bat_priv->gw.bandwidth_up); gw.bandwidth_down = htonl(down); gw.bandwidth_up = htonl(up); batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1, @@ -83,7 +83,7 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, /* restart gateway selection */ if (gateway.bandwidth_down != 0 && - atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT) + READ_ONCE(bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT) batadv_gw_check_election(bat_priv, orig); } @@ -96,7 +96,7 @@ void batadv_gw_init(struct batadv_priv *bat_priv) if (bat_priv->algo_ops->gw.init_sel_class) bat_priv->algo_ops->gw.init_sel_class(bat_priv); else - atomic_set(&bat_priv->gw.sel_class, 1); + WRITE_ONCE(bat_priv->gw.sel_class, 1); batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1, NULL, NULL, BATADV_TVLV_GW, 1, diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index d6732c34aeaf..60cee2c2f2f4 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -7,7 +7,7 @@ #include "hard-interface.h" #include "main.h" -#include +#include #include #include #include @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -42,6 +44,15 @@ #include "send.h" #include "translation-table.h" +static const struct rhashtable_params batadv_wifi_net_devices_params = { + .key_len = sizeof(struct net_device *), + .key_offset = offsetof(struct batadv_wifi_net_device_state, netdev), + .head_offset = offsetof(struct batadv_wifi_net_device_state, l), + .automatic_shrinking = true, +}; + +static struct rhashtable batadv_wifi_net_devices; + /** * batadv_hardif_release() - release hard interface from lists and queue for * free after rcu grace period @@ -355,21 +366,39 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) } /** - * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi - * interface + * batadv_netdev_get_wifi_flags() - retrieve wifi flags for net_device + * @net_dev: the device to check + * + * Return: batadv_hard_iface_wifi_flags flags of the device + */ +u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev) +{ + struct batadv_wifi_net_device_state *device_state; + u32 wifi_flags = 0; + + rcu_read_lock(); + device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices, + &net_dev, + batadv_wifi_net_devices_params); + if (device_state) + wifi_flags = READ_ONCE(device_state->wifi_flags); + rcu_read_unlock(); + + return wifi_flags; +} + +/** + * batadv_hardif_get_wifi_flags() - retrieve wifi flags for hard_iface * @hard_iface: the device to check * - * Return: true if the net device is a cfg80211 wireless device, false - * otherwise. + * Return: batadv_hard_iface_wifi_flags flags of the device */ -bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) +u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface) { - u32 allowed_flags = 0; + if (!hard_iface) + return 0; - allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; - allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; - - return !!(hard_iface->wifi_flags & allowed_flags); + return batadv_netdev_get_wifi_flags(hard_iface->net_dev); } /** @@ -380,10 +409,9 @@ bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface) */ bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface) { - if (!hard_iface) - return false; + u32 wifi_flags = batadv_hardif_get_wifi_flags(hard_iface); - return hard_iface->wifi_flags != 0; + return batadv_is_wifi(wifi_flags); } /** @@ -594,7 +622,7 @@ int batadv_hardif_min_mtu(struct net_device *mesh_iface) } rcu_read_unlock(); - if (atomic_read(&bat_priv->fragmentation) == 0) + if (READ_ONCE(bat_priv->fragmentation) == 0) goto out; /* with fragmentation enabled the maximum size of internally generated @@ -611,7 +639,7 @@ int batadv_hardif_min_mtu(struct net_device *mesh_iface) * overhead). For example, this value is used by TT to compute the * maximum local table size */ - atomic_set(&bat_priv->packet_size_max, min_mtu); + WRITE_ONCE(bat_priv->packet_size_max, min_mtu); /* the real mesh-interface MTU is computed by removing the payload * overhead from the maximum amount of bytes that was just computed. @@ -709,6 +737,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, int max_header_len = batadv_max_header_len(); unsigned int required_mtu; unsigned int hardif_mtu; + bool fragmentation; int ret; hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu); @@ -746,15 +775,14 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, batadv_info(hard_iface->mesh_iface, "Adding interface: %s\n", hard_iface->net_dev->name); - if (atomic_read(&bat_priv->fragmentation) && - hardif_mtu < required_mtu) + fragmentation = READ_ONCE(bat_priv->fragmentation); + if (fragmentation && hardif_mtu < required_mtu) batadv_info(hard_iface->mesh_iface, "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n", hard_iface->net_dev->name, hardif_mtu, required_mtu); - if (!atomic_read(&bat_priv->fragmentation) && - hardif_mtu < required_mtu) + if (!fragmentation && hardif_mtu < required_mtu) batadv_info(hard_iface->mesh_iface, "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n", hard_iface->net_dev->name, hardif_mtu, @@ -889,11 +917,10 @@ batadv_hardif_add_interface(struct net_device *net_dev) kref_init(&hard_iface->refcount); hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; - hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); if (batadv_is_wifi_hardif(hard_iface)) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; - atomic_set(&hard_iface->hop_penalty, 0); + WRITE_ONCE(hard_iface->hop_penalty, 0); batadv_v_hardif_init(hard_iface); @@ -941,6 +968,130 @@ static int batadv_hard_if_event_meshif(unsigned long event, return NOTIFY_DONE; } +/** + * batadv_wifi_net_device_insert() - save information about wifi net_device + * @net_dev: net_device to add to batadv_wifi_net_devices + * @wifi_flags: extracted batadv_hard_iface_wifi_flags of a net_device + * + * Return: 0 on success, negative value on error + */ +static int +batadv_wifi_net_device_insert(struct net_device *net_dev, u32 wifi_flags) +{ + struct batadv_wifi_net_device_state *device_state; + int ret; + + ASSERT_RTNL(); + + device_state = kzalloc_obj(*device_state, GFP_KERNEL); + if (!device_state) + return -ENOMEM; + + netdev_hold(net_dev, &device_state->dev_tracker, GFP_KERNEL); + device_state->netdev = net_dev; + WRITE_ONCE(device_state->wifi_flags, wifi_flags); + + ret = rhashtable_insert_fast(&batadv_wifi_net_devices, &device_state->l, + batadv_wifi_net_devices_params); + if (ret < 0) + goto err_free; + + return 0; + +err_free: + netdev_put(device_state->netdev, &device_state->dev_tracker); + kfree(device_state); + return ret; +} + +/** + * batadv_wifi_net_device_remove() - remove information about wifi net_device + * @device_state: wifi net_device state to remove from batadv_wifi_net_devices + */ +static void +batadv_wifi_net_device_remove(struct batadv_wifi_net_device_state *device_state) +{ + ASSERT_RTNL(); + + rhashtable_remove_fast(&batadv_wifi_net_devices, &device_state->l, + batadv_wifi_net_devices_params); + netdev_put(device_state->netdev, &device_state->dev_tracker); + kfree_rcu(device_state, rcu); +} + +/** + * batadv_wifi_net_device_update() - update wifi state of net_device + * @net_dev: net_device to update in batadv_wifi_net_devices + * + * The device will only be stored in batadv_wifi_net_devices when + * it could be identified as wifi device. If the net_device is no + * longer a wifi device, it is automatically removed from + * batadv_wifi_net_devices. + */ +static void +batadv_wifi_net_device_update(struct net_device *net_dev) +{ + struct batadv_wifi_net_device_state *device_state; + u32 wifi_flags; + + ASSERT_RTNL(); + + wifi_flags = batadv_wifi_flags_evaluate(net_dev); + device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices, + &net_dev, + batadv_wifi_net_devices_params); + + if (device_state) { + if (batadv_is_wifi(wifi_flags)) + WRITE_ONCE(device_state->wifi_flags, wifi_flags); + else + batadv_wifi_net_device_remove(device_state); + } else if (batadv_is_wifi(wifi_flags)) { + batadv_wifi_net_device_insert(net_dev, wifi_flags); + } +} + +/** + * batadv_wifi_net_device_unregister() - remove wifi state of net_device + * @net_dev: net_device to remove from batadv_wifi_net_devices + */ +static void +batadv_wifi_net_device_unregister(struct net_device *net_dev) +{ + struct batadv_wifi_net_device_state *device_state; + + ASSERT_RTNL(); + + device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices, + &net_dev, + batadv_wifi_net_devices_params); + if (!device_state) + return; + + batadv_wifi_net_device_remove(device_state); +} + +/** + * batadv_wifi_net_device_event() - handle network events for batadv_wifi_net_devices + * @event: enum netdev_cmd event to handle + * @net_dev: net_device to update in batadv_wifi_net_devices + */ +static void batadv_wifi_net_device_event(unsigned long event, + struct net_device *net_dev) +{ + switch (event) { + case NETDEV_REGISTER: + case NETDEV_POST_TYPE_CHANGE: + case NETDEV_CHANGEUPPER: + batadv_wifi_net_device_update(net_dev); + break; + case NETDEV_UNREGISTER: + case NETDEV_PRE_TYPE_CHANGE: + batadv_wifi_net_device_unregister(net_dev); + break; + } +} + static int batadv_hard_if_event(struct notifier_block *this, unsigned long event, void *ptr) { @@ -952,6 +1103,8 @@ static int batadv_hard_if_event(struct notifier_block *this, if (batadv_meshif_is_valid(net_dev)) return batadv_hard_if_event_meshif(event, net_dev); + batadv_wifi_net_device_event(event, net_dev); + hard_iface = batadv_hardif_get_by_netdev(net_dev); if (!hard_iface && (event == NETDEV_REGISTER || event == NETDEV_POST_TYPE_CHANGE)) @@ -995,8 +1148,9 @@ static int batadv_hard_if_event(struct notifier_block *this, if (hard_iface == primary_if) batadv_primary_if_update_addr(bat_priv, NULL); break; + case NETDEV_REGISTER: + case NETDEV_POST_TYPE_CHANGE: case NETDEV_CHANGEUPPER: - hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev); if (batadv_is_wifi_hardif(hard_iface)) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; break; @@ -1014,3 +1168,26 @@ static int batadv_hard_if_event(struct notifier_block *this, struct notifier_block batadv_hard_if_notifier = { .notifier_call = batadv_hard_if_event, }; + +/** + * batadv_wifi_net_devices_init() - Initialize wifi devices cache + * + * Return: 0 on success, negative error code on failure + */ +int __init batadv_wifi_net_devices_init(void) +{ + return rhashtable_init(&batadv_wifi_net_devices, + &batadv_wifi_net_devices_params); +} + +/** + * batadv_wifi_net_devices_deinit() - Deinitialize wifi devices cache + */ +void batadv_wifi_net_devices_deinit(void) +{ + /* just destroy table. entries should have been removed by + * unregister_netdevice_notifier() and the corresponding + * NETDEV_UNREGISTER events + */ + rhashtable_destroy(&batadv_wifi_net_devices); +} diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 9ba8fb2bdceb..af31696c3978 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -10,6 +10,7 @@ #include "main.h" #include +#include #include #include #include @@ -69,7 +70,8 @@ extern struct notifier_block batadv_hard_if_notifier; struct net_device *__batadv_get_real_netdev(struct net_device *net_device); struct net_device *batadv_get_real_netdev(struct net_device *net_device); -bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface); +u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev); +u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface); bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface); struct batadv_hard_iface* batadv_hardif_get_by_netdev(const struct net_device *net_dev); @@ -81,6 +83,8 @@ void batadv_update_min_mtu(struct net_device *mesh_iface); void batadv_hardif_release(struct kref *ref); int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, u8 *orig_addr, u8 *orig_neigh); +int __init batadv_wifi_net_devices_init(void); +void batadv_wifi_net_devices_deinit(void); /** * batadv_hardif_put() - decrement the hard interface refcounter and possibly @@ -119,4 +123,33 @@ batadv_primary_if_get_selected(struct batadv_priv *bat_priv) return hard_iface; } +/** + * batadv_is_cfg80211() - check if the given hardif is a cfg80211 + * wifi interface + * @wifi_flags: extracted batadv_hard_iface_wifi_flags of a net_device + * + * Return: true if the net device is a cfg80211 wireless device, false + * otherwise. + */ +static inline bool batadv_is_cfg80211(u32 wifi_flags) +{ + u32 allowed_flags = 0; + + allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT; + allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT; + + return !!(wifi_flags & allowed_flags); +} + +/** + * batadv_is_wifi() - check if flags belong to wifi interface + * @wifi_flags: extracted batadv_hard_iface_wifi_flags of a net_device + * + * Return: true if the net device is a 802.11 wireless device, false otherwise. + */ +static inline bool batadv_is_wifi(u32 wifi_flags) +{ + return wifi_flags != 0; +} + #endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index fb251c385a1b..86a2c20000dc 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -120,10 +120,10 @@ static inline int batadv_hash_add(struct batadv_hashtable *hash, * @choose: callback calculating the hash index * @data: data passed to the aforementioned callbacks as argument * - * ata could be the structure you use with just the key filled, we just need + * data could be the structure you use with just the key filled, we just need * the key for comparing. * - * Return: returns pointer do data on success, so you can remove the used + * Return: returns pointer to data on success, so you can remove the used * structure yourself, or NULL on error */ static inline void *batadv_hash_remove(struct batadv_hashtable *hash, diff --git a/net/batman-adv/log.h b/net/batman-adv/log.h index 225b747a2048..a0d2b0d64b2b 100644 --- a/net/batman-adv/log.h +++ b/net/batman-adv/log.h @@ -76,7 +76,7 @@ __printf(2, 3); #define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \ do { \ struct batadv_priv *__batpriv = (bat_priv); \ - if (atomic_read(&__batpriv->log_level) & (type) && \ + if (READ_ONCE(__batpriv->log_level) & (type) && \ (!(ratelimited) || net_ratelimit())) \ batadv_debug_log(__batpriv, fmt, ## arg); \ } \ diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index a4d33ee0fda5..3c4572284b53 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -104,22 +105,33 @@ static int __init batadv_init(void) batadv_tp_meter_init(); batadv_event_workqueue = create_singlethread_workqueue("bat_events"); - if (!batadv_event_workqueue) + if (!batadv_event_workqueue) { + ret = -ENOMEM; goto err_create_wq; + } + + ret = batadv_wifi_net_devices_init(); + if (ret < 0) + goto err_init_wifi; register_netdevice_notifier(&batadv_hard_if_notifier); rtnl_link_register(&batadv_link_ops); batadv_netlink_register(); pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) loaded\n", - BATADV_SOURCE_VERSION, BATADV_COMPAT_VERSION); + init_utsname()->release, BATADV_COMPAT_VERSION); return 0; +err_init_wifi: + destroy_workqueue(batadv_event_workqueue); + batadv_event_workqueue = NULL; + rcu_barrier(); + err_create_wq: batadv_tt_cache_destroy(); - return -ENOMEM; + return ret; } static void __exit batadv_exit(void) @@ -133,6 +145,7 @@ static void __exit batadv_exit(void) rcu_barrier(); + batadv_wifi_net_devices_deinit(); batadv_tt_cache_destroy(); } @@ -181,37 +194,38 @@ int batadv_mesh_init(struct net_device *mesh_iface) INIT_HLIST_HEAD(&bat_priv->tvlv.container_list); INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list); INIT_HLIST_HEAD(&bat_priv->meshif_vlan_list); - INIT_HLIST_HEAD(&bat_priv->tp_list); + INIT_HLIST_HEAD(&bat_priv->tp_sender_list); + INIT_HLIST_HEAD(&bat_priv->tp_receiver_list); bat_priv->gw.generation = 0; ret = batadv_originator_init(bat_priv); if (ret < 0) { - atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); goto err_orig; } ret = batadv_tt_init(bat_priv); if (ret < 0) { - atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); goto err_tt; } ret = batadv_v_mesh_init(bat_priv); if (ret < 0) { - atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); goto err_v; } ret = batadv_bla_init(bat_priv); if (ret < 0) { - atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); goto err_bla; } ret = batadv_dat_init(bat_priv); if (ret < 0) { - atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); goto err_dat; } @@ -219,7 +233,7 @@ int batadv_mesh_init(struct net_device *mesh_iface) batadv_mcast_init(bat_priv); atomic_set(&bat_priv->gw.reselect, 0); - atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_ACTIVE); return 0; @@ -233,7 +247,7 @@ int batadv_mesh_init(struct net_device *mesh_iface) batadv_originator_free(bat_priv); err_orig: batadv_purge_outstanding_packets(bat_priv, NULL); - atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_INACTIVE); return ret; } @@ -246,7 +260,7 @@ void batadv_mesh_free(struct net_device *mesh_iface) { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); - atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); batadv_purge_outstanding_packets(bat_priv, NULL); batadv_tp_stop_all(bat_priv); @@ -277,7 +291,7 @@ void batadv_mesh_free(struct net_device *mesh_iface) free_percpu(bat_priv->bat_counters); bat_priv->bat_counters = NULL; - atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_INACTIVE); } /** @@ -441,7 +455,7 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, bat_priv = netdev_priv(hard_iface->mesh_iface); - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto err_free; /* discard frames on not active interfaces */ @@ -614,7 +628,7 @@ bool batadv_vlan_ap_isola_get(struct batadv_priv *bat_priv, unsigned short vid) */ vlan = batadv_meshif_vlan_get(bat_priv, vid); if (vlan) { - ap_isolation_enabled = atomic_read(&vlan->ap_isolation); + ap_isolation_enabled = READ_ONCE(vlan->ap_isolation); batadv_meshif_vlan_put(vlan); } @@ -684,6 +698,5 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR(BATADV_DRIVER_AUTHOR); MODULE_DESCRIPTION(BATADV_DRIVER_DESC); -MODULE_VERSION(BATADV_SOURCE_VERSION); MODULE_ALIAS_RTNL_LINK("batadv"); MODULE_ALIAS_GENL_FAMILY(BATADV_NL_NAME); diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index af230b017bc1..f68fc8b7239c 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -12,10 +12,6 @@ #define BATADV_DRIVER_DESC "B.A.T.M.A.N. advanced" #define BATADV_DRIVER_DEVICE "batman-adv" -#ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2025.5" -#endif - /* B.A.T.M.A.N. parameters */ #define BATADV_TQ_MAX_VALUE 255 diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index e7aa45bc6b7a..44026810b99c 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -123,7 +125,7 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) eth_hw_addr_set(dev, addr->sa_data); /* only modify transtable if it has been initialized before */ - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) return 0; rcu_read_lock(); @@ -190,7 +192,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, int network_offset = ETH_HLEN; __be16 proto; - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; /* reset control block to avoid left overs from previous users */ @@ -255,7 +257,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) goto dropped; - gw_mode = atomic_read(&bat_priv->gw.mode); + gw_mode = READ_ONCE(bat_priv->gw.mode); if (is_multicast_ether_addr(ethhdr->h_dest)) { /* if gw mode is off, broadcast every packet */ if (gw_mode == BATADV_GW_MODE_OFF) { @@ -390,13 +392,13 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, * @orig_node: originator from which the batman-adv packet was sent * * Sends an ethernet frame to the receive path of the local @mesh_iface. - * skb->data has still point to the batman-adv header with the size @hdr_size. + * skb->data must still point to the batman-adv header with the size @hdr_size. * The caller has to have parsed this header already and made sure that at least * @hdr_size bytes are still available for pull in @skb. * * The packet may still get dropped. This can happen when the encapsulated - * ethernet frame is invalid or contains again an batman-adv packet. Also - * unicast packets will be dropped directly when it was sent between two + * ethernet frame is invalid or contains again a batman-adv packet. Also + * unicast packets will be dropped directly when they were sent between two * isolated clients. */ void batadv_interface_rx(struct net_device *mesh_iface, @@ -565,7 +567,7 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) vlan->vid = vid; kref_init(&vlan->refcount); - atomic_set(&vlan->ap_isolation, 0); + WRITE_ONCE(vlan->ap_isolation, 0); kref_get(&vlan->refcount); hlist_add_head_rcu(&vlan->list, &bat_priv->meshif_vlan_list); @@ -752,36 +754,36 @@ static int batadv_meshif_init_late(struct net_device *dev) if (!bat_priv->bat_counters) return -ENOMEM; - atomic_set(&bat_priv->aggregated_ogms, 1); - atomic_set(&bat_priv->bonding, 0); + WRITE_ONCE(bat_priv->aggregated_ogms, 1); + WRITE_ONCE(bat_priv->bonding, 0); #ifdef CONFIG_BATMAN_ADV_BLA - atomic_set(&bat_priv->bridge_loop_avoidance, 1); + WRITE_ONCE(bat_priv->bridge_loop_avoidance, 1); #endif #ifdef CONFIG_BATMAN_ADV_DAT - atomic_set(&bat_priv->distributed_arp_table, 1); + WRITE_ONCE(bat_priv->distributed_arp_table, 1); #endif #ifdef CONFIG_BATMAN_ADV_MCAST - atomic_set(&bat_priv->multicast_mode, 1); - atomic_set(&bat_priv->multicast_fanout, 16); + WRITE_ONCE(bat_priv->multicast_mode, 1); + WRITE_ONCE(bat_priv->multicast_fanout, 16); atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0); atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0); atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0); atomic_set(&bat_priv->mcast.num_no_mc_ptype_capa, 0); #endif - atomic_set(&bat_priv->gw.mode, BATADV_GW_MODE_OFF); - atomic_set(&bat_priv->gw.bandwidth_down, 100); - atomic_set(&bat_priv->gw.bandwidth_up, 20); - atomic_set(&bat_priv->orig_interval, 1000); - atomic_set(&bat_priv->hop_penalty, 30); + WRITE_ONCE(bat_priv->gw.mode, BATADV_GW_MODE_OFF); + WRITE_ONCE(bat_priv->gw.bandwidth_down, 100); + WRITE_ONCE(bat_priv->gw.bandwidth_up, 20); + WRITE_ONCE(bat_priv->orig_interval, 1000); + WRITE_ONCE(bat_priv->hop_penalty, 30); #ifdef CONFIG_BATMAN_ADV_DEBUG - atomic_set(&bat_priv->log_level, 0); + WRITE_ONCE(bat_priv->log_level, 0); #endif - atomic_set(&bat_priv->fragmentation, 1); - atomic_set(&bat_priv->packet_size_max, BATADV_MAX_MTU); + WRITE_ONCE(bat_priv->fragmentation, 1); + WRITE_ONCE(bat_priv->packet_size_max, BATADV_MAX_MTU); atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN); atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN); - atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); + WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_INACTIVE); atomic_set(&bat_priv->bcast_seqno, 1); atomic_set(&bat_priv->tt.vn, 0); atomic_set(&bat_priv->tt.ogm_append_cnt, 0); @@ -892,7 +894,7 @@ static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { strscpy(info->driver, "B.A.T.M.A.N. advanced", sizeof(info->driver)); - strscpy(info->version, BATADV_SOURCE_VERSION, sizeof(info->version)); + strscpy(info->version, init_utsname()->release, sizeof(info->version)); strscpy(info->fw_version, "N/A", sizeof(info->fw_version)); strscpy(info->bus_info, "batman", sizeof(info->bus_info)); } diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index a3d3efe22d30..1c5315e55c04 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -1099,7 +1099,7 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, { struct ethhdr *ethhdr = eth_hdr(skb); - if (!atomic_read(&bat_priv->multicast_mode)) + if (!READ_ONCE(bat_priv->multicast_mode)) return -EINVAL; switch (ntohs(ethhdr->h_proto)) { @@ -1204,7 +1204,7 @@ batadv_mcast_forw_mode_by_count(struct batadv_priv *bat_priv, batadv_mcast_forw_push(bat_priv, skb, vid, is_routable, count)) return BATADV_FORW_MCAST; - if (count <= atomic_read(&bat_priv->multicast_fanout)) + if (count <= READ_ONCE(bat_priv->multicast_fanout)) return BATADV_FORW_UCASTS; return BATADV_FORW_BCAST; @@ -2161,7 +2161,7 @@ int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb) */ void batadv_mcast_free(struct batadv_priv *bat_priv) { - cancel_delayed_work_sync(&bat_priv->mcast.work); + disable_delayed_work_sync(&bat_priv->mcast.work); batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2); batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST_TRACKER, 1); diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 78c651f634cd..4cf9e3c54ad3 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -179,7 +181,7 @@ static int batadv_netlink_mesh_fill_ap_isolation(struct sk_buff *msg, if (!vlan) return 0; - ap_isolation = atomic_read(&vlan->ap_isolation); + ap_isolation = READ_ONCE(vlan->ap_isolation); batadv_meshif_vlan_put(vlan); return nla_put_u8(msg, BATADV_ATTR_AP_ISOLATION_ENABLED, @@ -202,7 +204,7 @@ static int batadv_netlink_set_mesh_ap_isolation(struct nlattr *attr, if (!vlan) return -ENOENT; - atomic_set(&vlan->ap_isolation, !!nla_get_u8(attr)); + WRITE_ONCE(vlan->ap_isolation, !!nla_get_u8(attr)); batadv_meshif_vlan_put(vlan); return 0; @@ -233,7 +235,7 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, if (!hdr) return -ENOBUFS; - if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) || + if (nla_put_string(msg, BATADV_ATTR_VERSION, init_utsname()->release) || nla_put_string(msg, BATADV_ATTR_ALGO_NAME, bat_priv->algo_ops->name) || nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, mesh_iface->ifindex) || @@ -267,7 +269,7 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, } if (nla_put_u8(msg, BATADV_ATTR_AGGREGATED_OGMS_ENABLED, - !!atomic_read(&bat_priv->aggregated_ogms))) + !!READ_ONCE(bat_priv->aggregated_ogms))) goto nla_put_failure; if (batadv_netlink_mesh_fill_ap_isolation(msg, bat_priv)) @@ -282,35 +284,35 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, goto nla_put_failure; if (nla_put_u8(msg, BATADV_ATTR_BONDING_ENABLED, - !!atomic_read(&bat_priv->bonding))) + !!READ_ONCE(bat_priv->bonding))) goto nla_put_failure; #ifdef CONFIG_BATMAN_ADV_BLA if (nla_put_u8(msg, BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED, - !!atomic_read(&bat_priv->bridge_loop_avoidance))) + !!READ_ONCE(bat_priv->bridge_loop_avoidance))) goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_BLA */ #ifdef CONFIG_BATMAN_ADV_DAT if (nla_put_u8(msg, BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED, - !!atomic_read(&bat_priv->distributed_arp_table))) + !!READ_ONCE(bat_priv->distributed_arp_table))) goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_DAT */ if (nla_put_u8(msg, BATADV_ATTR_FRAGMENTATION_ENABLED, - !!atomic_read(&bat_priv->fragmentation))) + !!READ_ONCE(bat_priv->fragmentation))) goto nla_put_failure; if (nla_put_u32(msg, BATADV_ATTR_GW_BANDWIDTH_DOWN, - atomic_read(&bat_priv->gw.bandwidth_down))) + READ_ONCE(bat_priv->gw.bandwidth_down))) goto nla_put_failure; if (nla_put_u32(msg, BATADV_ATTR_GW_BANDWIDTH_UP, - atomic_read(&bat_priv->gw.bandwidth_up))) + READ_ONCE(bat_priv->gw.bandwidth_up))) goto nla_put_failure; if (nla_put_u8(msg, BATADV_ATTR_GW_MODE, - atomic_read(&bat_priv->gw.mode))) + READ_ONCE(bat_priv->gw.mode))) goto nla_put_failure; if (bat_priv->algo_ops->gw.get_best_gw_node && @@ -319,32 +321,32 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg, * in use does not implement the GW API */ if (nla_put_u32(msg, BATADV_ATTR_GW_SEL_CLASS, - atomic_read(&bat_priv->gw.sel_class))) + READ_ONCE(bat_priv->gw.sel_class))) goto nla_put_failure; } if (nla_put_u8(msg, BATADV_ATTR_HOP_PENALTY, - atomic_read(&bat_priv->hop_penalty))) + READ_ONCE(bat_priv->hop_penalty))) goto nla_put_failure; #ifdef CONFIG_BATMAN_ADV_DEBUG if (nla_put_u32(msg, BATADV_ATTR_LOG_LEVEL, - atomic_read(&bat_priv->log_level))) + READ_ONCE(bat_priv->log_level))) goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_DEBUG */ #ifdef CONFIG_BATMAN_ADV_MCAST if (nla_put_u8(msg, BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED, - !atomic_read(&bat_priv->multicast_mode))) + !READ_ONCE(bat_priv->multicast_mode))) goto nla_put_failure; if (nla_put_u32(msg, BATADV_ATTR_MULTICAST_FANOUT, - atomic_read(&bat_priv->multicast_fanout))) + READ_ONCE(bat_priv->multicast_fanout))) goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_MCAST */ if (nla_put_u32(msg, BATADV_ATTR_ORIG_INTERVAL, - atomic_read(&bat_priv->orig_interval))) + READ_ONCE(bat_priv->orig_interval))) goto nla_put_failure; batadv_hardif_put(primary_if); @@ -432,7 +434,7 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_AGGREGATED_OGMS_ENABLED]) { attr = info->attrs[BATADV_ATTR_AGGREGATED_OGMS_ENABLED]; - atomic_set(&bat_priv->aggregated_ogms, !!nla_get_u8(attr)); + WRITE_ONCE(bat_priv->aggregated_ogms, !!nla_get_u8(attr)); } if (info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]) { @@ -456,14 +458,14 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_BONDING_ENABLED]) { attr = info->attrs[BATADV_ATTR_BONDING_ENABLED]; - atomic_set(&bat_priv->bonding, !!nla_get_u8(attr)); + WRITE_ONCE(bat_priv->bonding, !!nla_get_u8(attr)); } #ifdef CONFIG_BATMAN_ADV_BLA if (info->attrs[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]) { attr = info->attrs[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]; - atomic_set(&bat_priv->bridge_loop_avoidance, + WRITE_ONCE(bat_priv->bridge_loop_avoidance, !!nla_get_u8(attr)); batadv_bla_status_update(bat_priv->mesh_iface); } @@ -473,7 +475,7 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]) { attr = info->attrs[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]; - atomic_set(&bat_priv->distributed_arp_table, + WRITE_ONCE(bat_priv->distributed_arp_table, !!nla_get_u8(attr)); batadv_dat_status_update(bat_priv->mesh_iface); } @@ -482,7 +484,7 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]) { attr = info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]; - atomic_set(&bat_priv->fragmentation, !!nla_get_u8(attr)); + WRITE_ONCE(bat_priv->fragmentation, !!nla_get_u8(attr)); rtnl_lock(); batadv_update_min_mtu(bat_priv->mesh_iface); @@ -492,14 +494,14 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_GW_BANDWIDTH_DOWN]) { attr = info->attrs[BATADV_ATTR_GW_BANDWIDTH_DOWN]; - atomic_set(&bat_priv->gw.bandwidth_down, nla_get_u32(attr)); + WRITE_ONCE(bat_priv->gw.bandwidth_down, nla_get_u32(attr)); batadv_gw_tvlv_container_update(bat_priv); } if (info->attrs[BATADV_ATTR_GW_BANDWIDTH_UP]) { attr = info->attrs[BATADV_ATTR_GW_BANDWIDTH_UP]; - atomic_set(&bat_priv->gw.bandwidth_up, nla_get_u32(attr)); + WRITE_ONCE(bat_priv->gw.bandwidth_up, nla_get_u32(attr)); batadv_gw_tvlv_container_update(bat_priv); } @@ -527,7 +529,7 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) * changing the gateway state */ batadv_gw_check_client_stop(bat_priv); - atomic_set(&bat_priv->gw.mode, gw_mode); + WRITE_ONCE(bat_priv->gw.mode, gw_mode); batadv_gw_tvlv_container_update(bat_priv); } } @@ -546,7 +548,7 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) sel_class = nla_get_u32(attr); if (sel_class >= 1 && sel_class <= sel_class_max) { - atomic_set(&bat_priv->gw.sel_class, sel_class); + WRITE_ONCE(bat_priv->gw.sel_class, sel_class); batadv_gw_reselect(bat_priv); } } @@ -554,14 +556,14 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_HOP_PENALTY]) { attr = info->attrs[BATADV_ATTR_HOP_PENALTY]; - atomic_set(&bat_priv->hop_penalty, nla_get_u8(attr)); + WRITE_ONCE(bat_priv->hop_penalty, nla_get_u8(attr)); } #ifdef CONFIG_BATMAN_ADV_DEBUG if (info->attrs[BATADV_ATTR_LOG_LEVEL]) { attr = info->attrs[BATADV_ATTR_LOG_LEVEL]; - atomic_set(&bat_priv->log_level, + WRITE_ONCE(bat_priv->log_level, nla_get_u32(attr) & BATADV_DBG_ALL); } #endif /* CONFIG_BATMAN_ADV_DEBUG */ @@ -570,13 +572,13 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]) { attr = info->attrs[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]; - atomic_set(&bat_priv->multicast_mode, !nla_get_u8(attr)); + WRITE_ONCE(bat_priv->multicast_mode, !nla_get_u8(attr)); } if (info->attrs[BATADV_ATTR_MULTICAST_FANOUT]) { attr = info->attrs[BATADV_ATTR_MULTICAST_FANOUT]; - atomic_set(&bat_priv->multicast_fanout, nla_get_u32(attr)); + WRITE_ONCE(bat_priv->multicast_fanout, nla_get_u32(attr)); } #endif /* CONFIG_BATMAN_ADV_MCAST */ @@ -589,7 +591,7 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) orig_interval = min_t(u32, orig_interval, INT_MAX); orig_interval = max_t(u32, orig_interval, 2 * BATADV_JITTER); - atomic_set(&bat_priv->orig_interval, orig_interval); + WRITE_ONCE(bat_priv->orig_interval, orig_interval); } batadv_netlink_notify_mesh(bat_priv); @@ -809,16 +811,16 @@ static int batadv_netlink_hardif_fill(struct sk_buff *msg, } if (nla_put_u8(msg, BATADV_ATTR_HOP_PENALTY, - atomic_read(&hard_iface->hop_penalty))) + READ_ONCE(hard_iface->hop_penalty))) goto nla_put_failure; #ifdef CONFIG_BATMAN_ADV_BATMAN_V if (nla_put_u32(msg, BATADV_ATTR_ELP_INTERVAL, - atomic_read(&hard_iface->bat_v.elp_interval))) + READ_ONCE(hard_iface->bat_v.elp_interval))) goto nla_put_failure; if (nla_put_u32(msg, BATADV_ATTR_THROUGHPUT_OVERRIDE, - atomic_read(&hard_iface->bat_v.throughput_override))) + READ_ONCE(hard_iface->bat_v.throughput_override))) goto nla_put_failure; #endif /* CONFIG_BATMAN_ADV_BATMAN_V */ @@ -911,21 +913,27 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, if (info->attrs[BATADV_ATTR_HOP_PENALTY]) { attr = info->attrs[BATADV_ATTR_HOP_PENALTY]; - atomic_set(&hard_iface->hop_penalty, nla_get_u8(attr)); + WRITE_ONCE(hard_iface->hop_penalty, nla_get_u8(attr)); } #ifdef CONFIG_BATMAN_ADV_BATMAN_V if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) { - attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; + u32 elp_interval; - atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr)); + attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; + elp_interval = nla_get_u32(attr); + + elp_interval = min_t(u32, elp_interval, INT_MAX); + elp_interval = max_t(u32, elp_interval, BATADV_JITTER); + + WRITE_ONCE(hard_iface->bat_v.elp_interval, elp_interval); } if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) { attr = info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]; - atomic_set(&hard_iface->bat_v.throughput_override, + WRITE_ONCE(hard_iface->bat_v.throughput_override, nla_get_u32(attr)); } #endif /* CONFIG_BATMAN_ADV_BATMAN_V */ @@ -936,7 +944,7 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, } /** - * batadv_netlink_dump_hardif() - Dump all hard interface into a messages + * batadv_netlink_dump_hardif() - Dump all hard interfaces into a message * @msg: Netlink message to dump into * @cb: Parameters from query * @@ -1020,7 +1028,7 @@ static int batadv_netlink_vlan_fill(struct sk_buff *msg, goto nla_put_failure; if (nla_put_u8(msg, BATADV_ATTR_AP_ISOLATION_ENABLED, - !!atomic_read(&vlan->ap_isolation))) + !!READ_ONCE(vlan->ap_isolation))) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -1093,7 +1101,7 @@ static int batadv_netlink_get_vlan(struct sk_buff *skb, struct genl_info *info) } /** - * batadv_netlink_set_vlan() - Get vlan attributes + * batadv_netlink_set_vlan() - Set vlan attributes * @skb: Netlink message with request data * @info: receiver information * @@ -1108,7 +1116,7 @@ static int batadv_netlink_set_vlan(struct sk_buff *skb, struct genl_info *info) if (info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]) { attr = info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]; - atomic_set(&vlan->ap_isolation, !!nla_get_u8(attr)); + WRITE_ONCE(vlan->ap_isolation, !!nla_get_u8(attr)); } batadv_netlink_notify_vlan(bat_priv, vlan); diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index ad4921b659d9..9b38bd9e8da7 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -7,7 +7,6 @@ #include "originator.h" #include "main.h" -#include #include #include #include @@ -440,7 +439,7 @@ batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node, } /** - * batadv_neigh_ifinfo_get() - find the ifinfo from an neigh_node + * batadv_neigh_ifinfo_get() - find the ifinfo from a neigh_node * @neigh: the neigh node to be queried * @if_outgoing: the interface for which the ifinfo should be acquired * @@ -473,7 +472,7 @@ batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh, } /** - * batadv_neigh_ifinfo_new() - search and possibly create an neigh_ifinfo object + * batadv_neigh_ifinfo_new() - search and possibly create a neigh_ifinfo object * @neigh: the neigh node to be queried * @if_outgoing: the interface for which the ifinfo should be acquired * @@ -694,9 +693,12 @@ batadv_neigh_node_create(struct batadv_orig_node *orig_node, kref_get(&hard_iface->refcount); ether_addr_copy(neigh_node->addr, neigh_addr); neigh_node->if_incoming = hard_iface; - neigh_node->orig_node = orig_node; neigh_node->last_seen = jiffies; +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + ACCESS_PRIVATE(neigh_node, orig_node_id) = orig_node; +#endif + /* increment unique neighbor refcount */ kref_get(&hardif_neigh->refcount); neigh_node->hardif_neigh = hardif_neigh; @@ -906,7 +908,7 @@ void batadv_originator_free(struct batadv_priv *bat_priv) if (!hash) return; - cancel_delayed_work_sync(&bat_priv->orig_work); + disable_delayed_work_sync(&bat_priv->orig_work); bat_priv->orig_hash = NULL; @@ -966,7 +968,7 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, orig_node->bat_priv = bat_priv; ether_addr_copy(orig_node->orig, addr); batadv_dat_init_orig_node_addr(orig_node); - atomic_set(&orig_node->last_ttvn, 0); + WRITE_ONCE(orig_node->last_ttvn, 0); orig_node->tt_buff = NULL; orig_node->tt_buff_len = 0; orig_node->last_seen = jiffies; diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 12c16f81cc51..cd4368b846ad 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -543,7 +543,7 @@ batadv_find_router(struct batadv_priv *bat_priv, /* only consider bonding for recv_if == BATADV_IF_DEFAULT (first hop) * and if activated. */ - if (!(recv_if == BATADV_IF_DEFAULT && atomic_read(&bat_priv->bonding))) + if (!(recv_if == BATADV_IF_DEFAULT && READ_ONCE(bat_priv->bonding))) return router; /* bonding: loop through the list of possible routers found @@ -765,7 +765,7 @@ batadv_reroute_unicast_packet(struct batadv_priv *bat_priv, struct sk_buff *skb, goto out; orig_addr = orig_node->orig; - orig_ttvn = (u8)atomic_read(&orig_node->last_ttvn); + orig_ttvn = READ_ONCE(orig_node->last_ttvn); } /* update the packet header */ @@ -845,7 +845,7 @@ static bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, if (!orig_node) return false; - curr_ttvn = (u8)atomic_read(&orig_node->last_ttvn); + curr_ttvn = READ_ONCE(orig_node->last_ttvn); batadv_orig_node_put(orig_node); } diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 60cd67ec9cea..7f449338a490 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -185,7 +185,7 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, /* Check if the skb is too large to send in one piece and fragment * it if needed. */ - if (atomic_read(&bat_priv->fragmentation) && + if (READ_ONCE(bat_priv->fragmentation) && skb->len > neigh_node->if_incoming->net_dev->mtu) { /* Fragment and send packet. */ ret = batadv_frag_send_packet(skb, orig_node, neigh_node); @@ -222,7 +222,7 @@ batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size, struct batadv_orig_node *orig_node) { struct batadv_unicast_packet *unicast_packet; - u8 ttvn = (u8)atomic_read(&orig_node->last_ttvn); + u8 ttvn = READ_ONCE(orig_node->last_ttvn); if (batadv_skb_head_push(skb, hdr_size) < 0) return false; @@ -629,7 +629,7 @@ static void batadv_forw_packet_list_free(struct hlist_head *head) hlist_for_each_entry_safe(forw_packet, safe_tmp_node, head, cleanup_list) { - cancel_delayed_work_sync(&forw_packet->delayed_work); + disable_delayed_work_sync(&forw_packet->delayed_work); hlist_del(&forw_packet->cleanup_list); batadv_forw_packet_free(forw_packet, true); @@ -1047,7 +1047,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) delayed_work); bat_priv = netdev_priv(forw_packet->if_incoming->mesh_iface); - if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { + if (READ_ONCE(bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) { dropped = true; goto out; } diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 0fc4ca78e84e..7e98cbfbbb70 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -142,30 +142,27 @@ static u32 batadv_tp_cwnd(u32 base, u32 increment, u32 min) * 2) if the session is in Congestion Avoidance, the CWND has to be * increased by MSS * MSS / CWND for every unique received ACK */ -static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss) +static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss) + __must_hold(&tp_vars->cc_lock) { - spin_lock_bh(&tp_vars->cwnd_lock); - /* slow start... */ - if (tp_vars->cwnd <= tp_vars->ss_threshold) { - tp_vars->dec_cwnd = 0; - tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, mss, mss); - spin_unlock_bh(&tp_vars->cwnd_lock); + if (tp_vars->cc.cwnd <= tp_vars->cc.ss_threshold) { + tp_vars->cc.dec_cwnd = 0; + tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.cwnd, mss, mss); return; } + /* prevent overflow in (mss * mss) << 3 */ + mss = min_t(u32, mss, (1U << 14) - 1); + /* increment CWND at least of 1 (section 3.1 of RFC5681) */ - tp_vars->dec_cwnd += max_t(u32, 1U << 3, - ((mss * mss) << 6) / (tp_vars->cwnd << 3)); - if (tp_vars->dec_cwnd < (mss << 3)) { - spin_unlock_bh(&tp_vars->cwnd_lock); + tp_vars->cc.dec_cwnd += max_t(u32, 1U << 3, + ((mss * mss) << 3) / tp_vars->cc.cwnd); + if (tp_vars->cc.dec_cwnd < (mss << 3)) return; - } - tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, mss, mss); - tp_vars->dec_cwnd = 0; - - spin_unlock_bh(&tp_vars->cwnd_lock); + tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.cwnd, mss, mss); + tp_vars->cc.dec_cwnd = 0; } /** @@ -173,8 +170,9 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss) * @tp_vars: the private data of the current TP meter session * @new_rtt: new roundtrip time in msec */ -static void batadv_tp_update_rto(struct batadv_tp_vars *tp_vars, +static void batadv_tp_update_rto(struct batadv_tp_sender *tp_vars, u32 new_rtt) + __must_hold(&tp_vars->cc_lock) { long m = new_rtt; @@ -184,24 +182,24 @@ static void batadv_tp_update_rto(struct batadv_tp_vars *tp_vars, * It's tricky to understand. Don't lose hair please. * Inspired by tcp_rtt_estimator() tcp_input.c */ - if (tp_vars->srtt != 0) { - m -= (tp_vars->srtt >> 3); /* m is now error in rtt est */ - tp_vars->srtt += m; /* rtt = 7/8 srtt + 1/8 new */ + if (tp_vars->cc.srtt != 0) { + m -= (tp_vars->cc.srtt >> 3); /* m is now error in rtt est */ + tp_vars->cc.srtt += m; /* rtt = 7/8 srtt + 1/8 new */ if (m < 0) m = -m; - m -= (tp_vars->rttvar >> 2); - tp_vars->rttvar += m; /* mdev ~= 3/4 rttvar + 1/4 new */ + m -= (tp_vars->cc.rttvar >> 2); + tp_vars->cc.rttvar += m; /* mdev ~= 3/4 rttvar + 1/4 new */ } else { /* first measure getting in */ - tp_vars->srtt = m << 3; /* take the measured time to be srtt */ - tp_vars->rttvar = m << 1; /* new_rtt / 2 */ + tp_vars->cc.srtt = m << 3; /* take the measured time to be srtt */ + tp_vars->cc.rttvar = m << 1; /* new_rtt / 2 */ } /* rto = srtt + 4 * rttvar. * rttvar is scaled by 4, therefore doesn't need to be multiplied */ - tp_vars->rto = (tp_vars->srtt >> 3) + tp_vars->rttvar; + WRITE_ONCE(tp_vars->cc.rto, (tp_vars->cc.srtt >> 3) + tp_vars->cc.rttvar); } /** @@ -252,35 +250,30 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, } /** - * batadv_tp_list_find() - find a tp_vars object in the global list + * batadv_tp_list_find_sender() - find a sender tp_vars object in the global list * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for - * @role: role of the session * * Look for a tp_vars object matching dst as end_point and return it after * having increment the refcounter. Return NULL is not found * * Return: matching tp_vars or NULL when no tp_vars with @dst was found */ -static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, - const u8 *dst, - enum batadv_tp_meter_role role) +static struct batadv_tp_sender * +batadv_tp_list_find_sender(struct batadv_priv *bat_priv, const u8 *dst) { - struct batadv_tp_vars *pos, *tp_vars = NULL; + struct batadv_tp_sender *pos, *tp_vars = NULL; rcu_read_lock(); - hlist_for_each_entry_rcu(pos, &bat_priv->tp_list, list) { - if (!batadv_compare_eth(pos->other_end, dst)) - continue; - - if (pos->role != role) + hlist_for_each_entry_rcu(pos, &bat_priv->tp_sender_list, common.list) { + if (!batadv_compare_eth(pos->common.other_end, dst)) continue; /* most of the time this function is invoked during the normal * process..it makes sens to pay more when the session is * finished and to speed the process up during the measurement */ - if (unlikely(!kref_get_unless_zero(&pos->refcount))) + if (unlikely(!kref_get_unless_zero(&pos->common.refcount))) continue; tp_vars = pos; @@ -296,15 +289,21 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for * - * Return: if matching session with @dst was found + * Return: true if a matching session with @dst was found, false otherwise */ static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst) __must_hold(&bat_priv->tp_list_lock) { - struct batadv_tp_vars *tp_vars; + struct batadv_tp_receiver *tp_receiver; + struct batadv_tp_sender *tp_sender; - hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) { - if (batadv_compare_eth(tp_vars->other_end, dst)) + hlist_for_each_entry_rcu(tp_sender, &bat_priv->tp_sender_list, common.list) { + if (batadv_compare_eth(tp_sender->common.other_end, dst)) + return true; + } + + hlist_for_each_entry_rcu(tp_receiver, &bat_priv->tp_receiver_list, common.list) { + if (batadv_compare_eth(tp_receiver->common.other_end, dst)) return true; } @@ -312,12 +311,11 @@ static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst) } /** - * batadv_tp_list_find_session() - find tp_vars session object in the global - * list + * batadv_tp_list_find_sender_session() - find tp_vars sender session + * object in the global list * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for * @session: session identifier - * @role: role of the session * * Look for a tp_vars object matching dst as end_point, session as tp meter * session and return it after having increment the refcounter. Return NULL @@ -325,28 +323,25 @@ static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst) * * Return: matching tp_vars or NULL when no tp_vars was found */ -static struct batadv_tp_vars * -batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, - const u8 *session, enum batadv_tp_meter_role role) +static struct batadv_tp_sender * +batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst, + const u8 *session) { - struct batadv_tp_vars *pos, *tp_vars = NULL; + struct batadv_tp_sender *pos, *tp_vars = NULL; rcu_read_lock(); - hlist_for_each_entry_rcu(pos, &bat_priv->tp_list, list) { - if (!batadv_compare_eth(pos->other_end, dst)) + hlist_for_each_entry_rcu(pos, &bat_priv->tp_sender_list, common.list) { + if (!batadv_compare_eth(pos->common.other_end, dst)) continue; - if (memcmp(pos->session, session, sizeof(pos->session)) != 0) - continue; - - if (pos->role != role) + if (memcmp(pos->common.session, session, sizeof(pos->common.session)) != 0) continue; /* most of the time this function is invoked during the normal * process..it makes sense to pay more when the session is * finished and to speed the process up during the measurement */ - if (unlikely(!kref_get_unless_zero(&pos->refcount))) + if (unlikely(!kref_get_unless_zero(&pos->common.refcount))) continue; tp_vars = pos; @@ -358,16 +353,16 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, } /** - * batadv_tp_vars_release() - release batadv_tp_vars from lists and queue for - * free after rcu grace period - * @ref: kref pointer of the batadv_tp_vars + * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists + * and queue for free after rcu grace period + * @ref: kref pointer of the batadv_tp_vars_common */ -static void batadv_tp_vars_release(struct kref *ref) +static void batadv_tp_vars_common_release(struct kref *ref) { - struct batadv_tp_vars *tp_vars; + struct batadv_tp_vars_common *tp_vars; struct batadv_tp_unacked *un, *safe; - tp_vars = container_of(ref, struct batadv_tp_vars, refcount); + tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount); /* lock should not be needed because this object is now out of any * context! @@ -383,23 +378,25 @@ static void batadv_tp_vars_release(struct kref *ref) } /** - * batadv_tp_vars_put() - decrement the batadv_tp_vars refcounter and possibly - * release it + * batadv_tp_sender_put() - decrement the batadv_tp_sender + * refcounter and possibly release it * @tp_vars: the private data of the current TP meter session to be free'd */ -static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars) +static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars) { if (!tp_vars) return; - kref_put(&tp_vars->refcount, batadv_tp_vars_release); + kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release); } /** * batadv_tp_list_detach() - remove tp session from mesh session list once * @tp_vars: the private data of the current TP meter session + * + * Return: whether tp_vars was detached from list and reference must be freed */ -static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars) +static bool batadv_tp_list_detach(struct batadv_tp_vars_common *tp_vars) { bool detached = false; @@ -411,27 +408,27 @@ static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars) spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock); if (!detached) - return; + return false; atomic_dec(&tp_vars->bat_priv->tp_num); - /* drop list reference */ - batadv_tp_vars_put(tp_vars); + return true; } /** * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer * @tp_vars: the private data of the current TP meter session to cleanup */ -static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) +static void batadv_tp_sender_cleanup(struct batadv_tp_sender *tp_vars) { - cancel_delayed_work_sync(&tp_vars->finish_work); + disable_delayed_work_sync(&tp_vars->finish_work); - batadv_tp_list_detach(tp_vars); + if (batadv_tp_list_detach(&tp_vars->common)) + batadv_tp_sender_put(tp_vars); /* kill the timer and remove its reference */ - timer_shutdown_sync(&tp_vars->timer); - batadv_tp_vars_put(tp_vars); + timer_shutdown_sync(&tp_vars->common.timer); + batadv_tp_sender_put(tp_vars); } /** @@ -440,7 +437,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) * @tp_vars: the private data of the current TP meter session */ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, - struct batadv_tp_vars *tp_vars) + struct batadv_tp_sender *tp_vars) { enum batadv_tp_meter_reason reason; u32 session_cookie; @@ -449,21 +446,21 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Test towards %pM finished..shutting down (reason=%d)\n", - tp_vars->other_end, reason); + tp_vars->common.other_end, reason); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n", - tp_vars->srtt >> 3, tp_vars->rttvar >> 2, tp_vars->rto); + tp_vars->cc.srtt >> 3, tp_vars->cc.rttvar >> 2, tp_vars->cc.rto); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Final values: cwnd=%u ss_threshold=%u\n", - tp_vars->cwnd, tp_vars->ss_threshold); + tp_vars->cc.cwnd, tp_vars->cc.ss_threshold); - session_cookie = batadv_tp_session_cookie(tp_vars->session, + session_cookie = batadv_tp_session_cookie(tp_vars->common.session, tp_vars->icmp_uid); batadv_tp_batctl_notify(reason, - tp_vars->other_end, + tp_vars->common.other_end, bat_priv, tp_vars->start_time, atomic64_read(&tp_vars->tot_sent), @@ -475,7 +472,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, * @tp_vars: the private data of the current TP meter session * @reason: reason for tp meter session stop */ -static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars, +static void batadv_tp_sender_shutdown(struct batadv_tp_sender *tp_vars, enum batadv_tp_meter_reason reason) { atomic_cmpxchg(&tp_vars->send_result, 0, reason); @@ -487,7 +484,7 @@ static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars, * * Return: whether stop reason was found */ -static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars) +static bool batadv_tp_sender_stopped(struct batadv_tp_sender *tp_vars) { return atomic_read(&tp_vars->send_result) != 0; } @@ -499,10 +496,10 @@ static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars) static void batadv_tp_sender_finish(struct work_struct *work) { struct delayed_work *delayed_work; - struct batadv_tp_vars *tp_vars; + struct batadv_tp_sender *tp_vars; delayed_work = to_delayed_work(work); - tp_vars = container_of(delayed_work, struct batadv_tp_vars, + tp_vars = container_of(delayed_work, struct batadv_tp_sender, finish_work); batadv_tp_sender_shutdown(tp_vars, BATADV_TP_REASON_COMPLETE); @@ -512,9 +509,9 @@ static void batadv_tp_sender_finish(struct work_struct *work) * batadv_tp_reset_sender_timer() - reschedule the sender timer * @tp_vars: the private TP meter data for this session * - * Reschedule the timer using tp_vars->rto as delay + * Reschedule the timer using tp_vars->cc.rto as delay */ -static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars) +static void batadv_tp_reset_sender_timer(struct batadv_tp_sender *tp_vars) { /* most of the time this function is invoked while normal packet * reception... @@ -523,7 +520,8 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars) /* timer ref will be dropped in batadv_tp_sender_cleanup */ return; - mod_timer(&tp_vars->timer, jiffies + msecs_to_jiffies(tp_vars->rto)); + mod_timer(&tp_vars->common.timer, + jiffies + msecs_to_jiffies(READ_ONCE(tp_vars->cc.rto))); } /** @@ -536,14 +534,17 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars) */ static void batadv_tp_sender_timeout(struct timer_list *t) { - struct batadv_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer); - struct batadv_priv *bat_priv = tp_vars->bat_priv; + struct batadv_tp_sender *tp_vars = timer_container_of(tp_vars, t, common.timer); + struct batadv_priv *bat_priv = tp_vars->common.bat_priv; if (batadv_tp_sender_stopped(tp_vars)) return; + spin_lock_bh(&tp_vars->cc_lock); + /* if the user waited long enough...shutdown the test */ - if (unlikely(tp_vars->rto >= BATADV_TP_MAX_RTO)) { + if (unlikely(tp_vars->cc.rto >= BATADV_TP_MAX_RTO)) { + spin_unlock_bh(&tp_vars->cc_lock); batadv_tp_sender_shutdown(tp_vars, BATADV_TP_REASON_DST_UNREACHABLE); return; @@ -552,25 +553,24 @@ static void batadv_tp_sender_timeout(struct timer_list *t) /* RTO exponential backoff * Details in Section 5.5 of RFC6298 */ - tp_vars->rto <<= 1; + WRITE_ONCE(tp_vars->cc.rto, tp_vars->cc.rto * 2); - spin_lock_bh(&tp_vars->cwnd_lock); - - tp_vars->ss_threshold = tp_vars->cwnd >> 1; - if (tp_vars->ss_threshold < BATADV_TP_PLEN * 2) - tp_vars->ss_threshold = BATADV_TP_PLEN * 2; + tp_vars->cc.ss_threshold = tp_vars->cc.cwnd >> 1; + if (tp_vars->cc.ss_threshold < BATADV_TP_PLEN * 2) + tp_vars->cc.ss_threshold = BATADV_TP_PLEN * 2; batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: RTO fired during test towards %pM! cwnd=%u new ss_thr=%u, resetting last_sent to %u\n", - tp_vars->other_end, tp_vars->cwnd, tp_vars->ss_threshold, - atomic_read(&tp_vars->last_acked)); + tp_vars->common.other_end, tp_vars->cc.cwnd, tp_vars->cc.ss_threshold, + tp_vars->cc.last_acked); - tp_vars->cwnd = BATADV_TP_PLEN * 3; + tp_vars->cc.cwnd = BATADV_TP_PLEN * 3; - spin_unlock_bh(&tp_vars->cwnd_lock); + WRITE_ONCE(tp_vars->cc.last_sent, tp_vars->cc.last_acked); + + spin_unlock_bh(&tp_vars->cc_lock); /* resend the non-ACKed packets.. */ - tp_vars->last_sent = atomic_read(&tp_vars->last_acked); wake_up(&tp_vars->more_bytes); batadv_tp_reset_sender_timer(tp_vars); @@ -582,7 +582,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t) * @buf: Buffer to fill with bytes * @nbytes: amount of pseudorandom bytes */ -static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars, +static void batadv_tp_fill_prerandom(struct batadv_tp_sender *tp_vars, u8 *buf, size_t nbytes) { u32 local_offset; @@ -621,11 +621,11 @@ static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars, * * Create and send a single TP Meter message. * - * Return: 0 on success, BATADV_TP_REASON_DST_UNREACHABLE if the destination is - * not reachable, BATADV_TP_REASON_MEMORY_ERROR if the packet couldn't be - * allocated + * Return: 0 on success, BATADV_TP_REASON_MEMORY_ERROR if the packet couldn't + * be allocated, BATADV_TP_REASON_CANT_SEND if the packet could not be + * transmitted */ -static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src, +static int batadv_tp_send_msg(struct batadv_tp_sender *tp_vars, const u8 *src, struct batadv_orig_node *orig_node, u32 seqno, size_t len, const u8 *session, int uid, u32 timestamp) @@ -668,6 +668,115 @@ static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src, return BATADV_TP_REASON_CANT_SEND; } +/** + * enum batadv_tp_ack_reaction - expected reaction to ack packet + */ +enum batadv_tp_ack_reaction { + /** @BATADV_TP_ACK_REACTION_OLD_ACK: ignore old ack packet */ + BATADV_TP_ACK_REACTION_OLD_ACK, + + /** @BATADV_TP_ACK_REACTION_IGNORE: ignore duplicated ack but reset timer */ + BATADV_TP_ACK_REACTION_IGNORE, + + /** @BATADV_TP_ACK_REACTION_RESEND_WAKEUP: resend data and wakeup "more_bytes" */ + BATADV_TP_ACK_REACTION_RESEND_WAKEUP, + + /** @BATADV_TP_ACK_REACTION_WAKEUP: wakeup "more_bytes" */ + BATADV_TP_ACK_REACTION_WAKEUP, +}; + +/** + * batadv_tp_handle_ack() - Calculate reaction to ACK and update congestion control + * @bat_priv: the bat priv with all the mesh interface information + * @tp_vars: the private data of the current TP meter session + * @recv_ack: received ACK seqno + * @mss: maximum segment size for transmission + * + * Return: expected reaction to this ack + */ +static enum batadv_tp_ack_reaction +batadv_tp_handle_ack(struct batadv_priv *bat_priv, + struct batadv_tp_sender *tp_vars, + u32 recv_ack, size_t mss) + __must_hold(&tp_vars->cc_lock) +{ + enum batadv_tp_ack_reaction reaction; + + if (batadv_seq_before(recv_ack, tp_vars->cc.last_acked)) + return BATADV_TP_ACK_REACTION_OLD_ACK; + + /* check if this ACK is a duplicate */ + if (tp_vars->cc.last_acked == recv_ack) { + /* if this is the third duplicate ACK do Fast Retransmit */ + if (tp_vars->cc.dup_acks > 3) + return BATADV_TP_ACK_REACTION_IGNORE; + + tp_vars->cc.dup_acks++; + if (tp_vars->cc.dup_acks != 3) + return BATADV_TP_ACK_REACTION_IGNORE; + + if (!batadv_seq_before(tp_vars->cc.recover, recv_ack)) + return BATADV_TP_ACK_REACTION_IGNORE; + + /* Fast Recovery */ + tp_vars->cc.fast_recovery = true; + + /* Set recover to the last outstanding seqno when Fast Recovery + * is entered. RFC6582, Section 3.2, step 1 + */ + tp_vars->cc.recover = tp_vars->cc.last_sent; + tp_vars->cc.ss_threshold = tp_vars->cc.cwnd >> 1; + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: Fast Recovery, (cur cwnd=%u) ss_thr=%u last_sent=%u recv_ack=%u\n", + tp_vars->cc.cwnd, tp_vars->cc.ss_threshold, + tp_vars->cc.last_sent, recv_ack); + tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.ss_threshold, 3 * mss, + mss); + tp_vars->cc.dec_cwnd = 0; + WRITE_ONCE(tp_vars->cc.last_sent, recv_ack); + + return BATADV_TP_ACK_REACTION_RESEND_WAKEUP; + } + + /* count the acked data */ + atomic64_add(recv_ack - tp_vars->cc.last_acked, &tp_vars->tot_sent); + + /* reset the duplicate ACKs counter */ + tp_vars->cc.dup_acks = 0; + + if (tp_vars->cc.fast_recovery) { + /* partial ACK */ + if (batadv_seq_before(recv_ack, tp_vars->cc.recover)) { + /* this is another hole in the window. React + * immediately as specified by NewReno (see + * Section 3.2 of RFC6582 for details) + */ + reaction = BATADV_TP_ACK_REACTION_RESEND_WAKEUP; + tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.cwnd, + mss, mss); + } else { + tp_vars->cc.fast_recovery = false; + /* set cwnd to the value of ss_threshold at the + * moment that Fast Recovery was entered. + * RFC6582, Section 3.2, step 3 + */ + tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.ss_threshold, + 0, mss); + reaction = BATADV_TP_ACK_REACTION_WAKEUP; + } + } else { + if (recv_ack - tp_vars->cc.last_acked >= mss) + batadv_tp_update_cwnd(tp_vars, mss); + + reaction = BATADV_TP_ACK_REACTION_WAKEUP; + } + + /* move the Transmit Window */ + WRITE_ONCE(tp_vars->cc.last_acked, recv_ack); + + return reaction; +} + /** * batadv_tp_recv_ack() - ACK receiving function * @bat_priv: the bat priv with all the mesh interface information @@ -681,20 +790,23 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; const struct batadv_icmp_tp_packet *icmp; - struct batadv_tp_vars *tp_vars; - const unsigned char *dev_addr; - size_t packet_len, mss; - u32 rtt, recv_ack, cwnd; + enum batadv_tp_ack_reaction reaction; + struct batadv_tp_sender *tp_vars; + size_t packet_len; + u32 recv_ack; + size_t mss; + u32 rtt; packet_len = BATADV_TP_PLEN; mss = BATADV_TP_PLEN; packet_len += sizeof(struct batadv_unicast_packet); icmp = (struct batadv_icmp_tp_packet *)skb->data; + recv_ack = ntohl(icmp->seqno); /* find the tp_vars */ - tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, - icmp->session, BATADV_TP_SENDER); + tp_vars = batadv_tp_list_find_sender_session(bat_priv, icmp->orig, + icmp->session); if (unlikely(!tp_vars)) return; @@ -702,8 +814,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, goto out; /* old ACK? silently drop it.. */ - if (batadv_seq_before(ntohl(icmp->seqno), - (u32)atomic_read(&tp_vars->last_acked))) + if (batadv_seq_before(recv_ack, READ_ONCE(tp_vars->cc.last_acked))) goto out; primary_if = batadv_primary_if_get_selected(bat_priv); @@ -714,97 +825,40 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (unlikely(!orig_node)) goto out; + spin_lock_bh(&tp_vars->cc_lock); /* update RTO with the new sampled RTT, if any */ rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp); if (icmp->timestamp && rtt) batadv_tp_update_rto(tp_vars, rtt); + reaction = batadv_tp_handle_ack(bat_priv, tp_vars, recv_ack, mss); + spin_unlock_bh(&tp_vars->cc_lock); + + if (reaction == BATADV_TP_ACK_REACTION_OLD_ACK) + goto out; + /* ACK for new data... reset the timer */ batadv_tp_reset_sender_timer(tp_vars); - recv_ack = ntohl(icmp->seqno); - - /* check if this ACK is a duplicate */ - if (atomic_read(&tp_vars->last_acked) == recv_ack) { - atomic_inc(&tp_vars->dup_acks); - if (atomic_read(&tp_vars->dup_acks) != 3) - goto out; - - if (recv_ack >= tp_vars->recover) - goto out; - - /* if this is the third duplicate ACK do Fast Retransmit */ + switch (reaction) { + default: + case BATADV_TP_ACK_REACTION_IGNORE: + goto out; + case BATADV_TP_ACK_REACTION_RESEND_WAKEUP: batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr, orig_node, recv_ack, packet_len, icmp->session, icmp->uid, jiffies_to_msecs(jiffies)); - - spin_lock_bh(&tp_vars->cwnd_lock); - - /* Fast Recovery */ - tp_vars->fast_recovery = true; - /* Set recover to the last outstanding seqno when Fast Recovery - * is entered. RFC6582, Section 3.2, step 1 - */ - tp_vars->recover = tp_vars->last_sent; - tp_vars->ss_threshold = tp_vars->cwnd >> 1; - batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: Fast Recovery, (cur cwnd=%u) ss_thr=%u last_sent=%u recv_ack=%u\n", - tp_vars->cwnd, tp_vars->ss_threshold, - tp_vars->last_sent, recv_ack); - tp_vars->cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 3 * mss, - mss); - tp_vars->dec_cwnd = 0; - tp_vars->last_sent = recv_ack; - - spin_unlock_bh(&tp_vars->cwnd_lock); - } else { - /* count the acked data */ - atomic64_add(recv_ack - atomic_read(&tp_vars->last_acked), - &tp_vars->tot_sent); - /* reset the duplicate ACKs counter */ - atomic_set(&tp_vars->dup_acks, 0); - - if (tp_vars->fast_recovery) { - /* partial ACK */ - if (batadv_seq_before(recv_ack, tp_vars->recover)) { - /* this is another hole in the window. React - * immediately as specified by NewReno (see - * Section 3.2 of RFC6582 for details) - */ - dev_addr = primary_if->net_dev->dev_addr; - batadv_tp_send_msg(tp_vars, dev_addr, - orig_node, recv_ack, - packet_len, icmp->session, - icmp->uid, - jiffies_to_msecs(jiffies)); - tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, - mss, mss); - } else { - tp_vars->fast_recovery = false; - /* set cwnd to the value of ss_threshold at the - * moment that Fast Recovery was entered. - * RFC6582, Section 3.2, step 3 - */ - cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 0, - mss); - tp_vars->cwnd = cwnd; - } - goto move_twnd; - } - - if (recv_ack - atomic_read(&tp_vars->last_acked) >= mss) - batadv_tp_update_cwnd(tp_vars, mss); -move_twnd: - /* move the Transmit Window */ - atomic_set(&tp_vars->last_acked, recv_ack); + fallthrough; + case BATADV_TP_ACK_REACTION_WAKEUP: + wake_up(&tp_vars->more_bytes); + break; } - wake_up(&tp_vars->more_bytes); out: batadv_hardif_put(primary_if); batadv_orig_node_put(orig_node); - batadv_tp_vars_put(tp_vars); + batadv_tp_sender_put(tp_vars); } /** @@ -814,13 +868,21 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, * * Return: true when congestion window is not full, false otherwise */ -static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars, +static bool batadv_tp_avail(struct batadv_tp_sender *tp_vars, size_t payload_len) { u32 win_left, win_limit; - win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd; - win_left = win_limit - tp_vars->last_sent; + spin_lock_bh(&tp_vars->cc_lock); + + win_limit = tp_vars->cc.last_acked + tp_vars->cc.cwnd; + + if (batadv_seq_before(tp_vars->cc.last_sent, win_limit)) + win_left = win_limit - tp_vars->cc.last_sent; + else + win_left = 0; + + spin_unlock_bh(&tp_vars->cc_lock); return win_left >= payload_len; } @@ -836,7 +898,7 @@ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars, * remaining jiffies (at least 1) if the condition evaluated to true before * the timeout elapsed, or -ERESTARTSYS if it was interrupted by a signal. */ -static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen) +static int batadv_tp_wait_available(struct batadv_tp_sender *tp_vars, size_t plen) { int ret; @@ -851,24 +913,19 @@ static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen) * batadv_tp_send() - main sending thread of a tp meter session * @arg: address of the related tp_vars * - * Return: nothing, this function never returns + * Return: 0 */ static int batadv_tp_send(void *arg) { - struct batadv_tp_vars *tp_vars = arg; - struct batadv_priv *bat_priv = tp_vars->bat_priv; + struct batadv_tp_sender *tp_vars = arg; + struct batadv_priv *bat_priv = tp_vars->common.bat_priv; struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; size_t payload_len, packet_len; + u32 last_sent; int err = 0; - if (unlikely(tp_vars->role != BATADV_TP_SENDER)) { - err = BATADV_TP_REASON_DST_UNREACHABLE; - batadv_tp_sender_shutdown(tp_vars, err); - goto out; - } - - orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); + orig_node = batadv_orig_hash_find(bat_priv, tp_vars->common.other_end); if (unlikely(!orig_node)) { err = BATADV_TP_REASON_DST_UNREACHABLE; batadv_tp_sender_shutdown(tp_vars, err); @@ -907,11 +964,11 @@ static int batadv_tp_send(void *arg) * the size of the unicast header */ packet_len = payload_len + sizeof(struct batadv_unicast_packet); + last_sent = READ_ONCE(tp_vars->cc.last_sent); err = batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr, - orig_node, tp_vars->last_sent, - packet_len, - tp_vars->session, tp_vars->icmp_uid, + orig_node, last_sent, packet_len, + tp_vars->common.session, tp_vars->icmp_uid, jiffies_to_msecs(jiffies)); /* something went wrong during the preparation/transmission */ @@ -925,8 +982,12 @@ static int batadv_tp_send(void *arg) } /* right-shift the TWND */ - if (!err) - tp_vars->last_sent += payload_len; + if (!err) { + spin_lock_bh(&tp_vars->cc_lock); + if (tp_vars->cc.last_sent == last_sent) + WRITE_ONCE(tp_vars->cc.last_sent, last_sent + payload_len); + spin_unlock_bh(&tp_vars->cc_lock); + } cond_resched(); } @@ -939,7 +1000,7 @@ static int batadv_tp_send(void *arg) batadv_tp_sender_cleanup(tp_vars); complete(&tp_vars->finished); - batadv_tp_vars_put(tp_vars); + batadv_tp_sender_put(tp_vars); return 0; } @@ -949,24 +1010,24 @@ static int batadv_tp_send(void *arg) * sender * @tp_vars: the private data of the current TP meter session */ -static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars) +static void batadv_tp_start_kthread(struct batadv_tp_sender *tp_vars) { struct task_struct *kthread; - struct batadv_priv *bat_priv = tp_vars->bat_priv; + struct batadv_priv *bat_priv = tp_vars->common.bat_priv; u32 session_cookie; - kref_get(&tp_vars->refcount); + kref_get(&tp_vars->common.refcount); kthread = kthread_create(batadv_tp_send, tp_vars, "kbatadv_tp_meter"); if (IS_ERR(kthread)) { - session_cookie = batadv_tp_session_cookie(tp_vars->session, + session_cookie = batadv_tp_session_cookie(tp_vars->common.session, tp_vars->icmp_uid); pr_err("batadv: cannot create tp meter kthread\n"); batadv_tp_batctl_error_notify(BATADV_TP_REASON_MEMORY_ERROR, - tp_vars->other_end, + tp_vars->common.other_end, bat_priv, session_cookie); /* drop reserved reference for kthread */ - batadv_tp_vars_put(tp_vars); + batadv_tp_sender_put(tp_vars); /* cleanup of failed tp meter variables */ batadv_tp_sender_cleanup(tp_vars); @@ -987,7 +1048,7 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars) void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, u32 test_length, u32 *cookie) { - struct batadv_tp_vars *tp_vars; + struct batadv_tp_sender *tp_vars; u8 session_id[2]; u8 icmp_uid; u32 session_cookie; @@ -999,7 +1060,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, /* look for an already existing test towards this node */ spin_lock_bh(&bat_priv->tp_list_lock); - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) { + if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) { spin_unlock_bh(&bat_priv->tp_list_lock); batadv_tp_batctl_error_notify(BATADV_TP_REASON_DST_UNREACHABLE, dst, bat_priv, session_cookie); @@ -1037,76 +1098,78 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, } /* initialize tp_vars */ - ether_addr_copy(tp_vars->other_end, dst); - kref_init(&tp_vars->refcount); - tp_vars->role = BATADV_TP_SENDER; + ether_addr_copy(tp_vars->common.other_end, dst); + kref_init(&tp_vars->common.refcount); atomic_set(&tp_vars->send_result, 0); - memcpy(tp_vars->session, session_id, sizeof(session_id)); + memcpy(tp_vars->common.session, session_id, sizeof(session_id)); tp_vars->icmp_uid = icmp_uid; - tp_vars->last_sent = BATADV_TP_FIRST_SEQ; - atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ); - tp_vars->fast_recovery = false; - tp_vars->recover = BATADV_TP_FIRST_SEQ; + WRITE_ONCE(tp_vars->cc.last_sent, BATADV_TP_FIRST_SEQ); + WRITE_ONCE(tp_vars->cc.dup_acks, 0); + WRITE_ONCE(tp_vars->cc.last_acked, BATADV_TP_FIRST_SEQ); + tp_vars->cc.fast_recovery = false; + tp_vars->cc.recover = BATADV_TP_FIRST_SEQ; /* initialise the CWND to 3*MSS (Section 3.1 in RFC5681). * For batman-adv the MSS is the size of the payload received by the * mesh_interface, hence its MTU */ - tp_vars->cwnd = BATADV_TP_PLEN * 3; + tp_vars->cc.cwnd = BATADV_TP_PLEN * 3; + tp_vars->cc.dec_cwnd = 0; + /* at the beginning initialise the SS threshold to the biggest possible * window size, hence the AWND size */ - tp_vars->ss_threshold = BATADV_TP_AWND; + tp_vars->cc.ss_threshold = BATADV_TP_AWND; /* RTO initial value is 3 seconds. * Details in Section 2.1 of RFC6298 */ - tp_vars->rto = 1000; - tp_vars->srtt = 0; - tp_vars->rttvar = 0; + WRITE_ONCE(tp_vars->cc.rto, 1000); + tp_vars->cc.srtt = 0; + tp_vars->cc.rttvar = 0; atomic64_set(&tp_vars->tot_sent, 0); - kref_get(&tp_vars->refcount); - timer_setup(&tp_vars->timer, batadv_tp_sender_timeout, 0); + kref_get(&tp_vars->common.refcount); + timer_setup(&tp_vars->common.timer, batadv_tp_sender_timeout, 0); - tp_vars->bat_priv = bat_priv; + tp_vars->common.bat_priv = bat_priv; tp_vars->start_time = jiffies; init_waitqueue_head(&tp_vars->more_bytes); init_completion(&tp_vars->finished); - spin_lock_init(&tp_vars->unacked_lock); - INIT_LIST_HEAD(&tp_vars->unacked_list); + spin_lock_init(&tp_vars->common.unacked_lock); + INIT_LIST_HEAD(&tp_vars->common.unacked_list); - spin_lock_init(&tp_vars->cwnd_lock); + spin_lock_init(&tp_vars->cc_lock); tp_vars->prerandom_offset = 0; spin_lock_init(&tp_vars->prerandom_lock); - kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); - spin_unlock_bh(&bat_priv->tp_list_lock); - tp_vars->test_length = test_length; if (!tp_vars->test_length) tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH; + /* init work item for finished tp tests */ + INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); + + kref_get(&tp_vars->common.refcount); + hlist_add_head_rcu(&tp_vars->common.list, &bat_priv->tp_sender_list); + spin_unlock_bh(&bat_priv->tp_list_lock); + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: starting throughput meter towards %pM (length=%ums)\n", dst, test_length); - /* init work item for finished tp tests */ - INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); - /* start tp kthread. This way the write() call issued from userspace can * happily return and avoid to block */ batadv_tp_start_kthread(tp_vars); /* don't return reference to new tp_vars */ - batadv_tp_vars_put(tp_vars); + batadv_tp_sender_put(tp_vars); } /** @@ -1119,7 +1182,7 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, u8 return_value) { struct batadv_orig_node *orig_node; - struct batadv_tp_vars *tp_vars; + struct batadv_tp_sender *tp_vars; batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: stopping test towards %pM\n", dst); @@ -1128,7 +1191,7 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, if (!orig_node) return; - tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER); + tp_vars = batadv_tp_list_find_sender(bat_priv, orig_node->orig); if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: trying to interrupt an already over connection\n"); @@ -1136,20 +1199,75 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, } batadv_tp_sender_shutdown(tp_vars, return_value); - batadv_tp_vars_put(tp_vars); + batadv_tp_sender_put(tp_vars); out_put_orig_node: batadv_orig_node_put(orig_node); } +/** + * batadv_tp_list_find_receiver_session() - find tp_vars receiver session + * object in the global list + * @bat_priv: the bat priv with all the mesh interface information + * @dst: the other endpoint MAC address to look for + * @session: session identifier + * + * Look for a tp_vars object matching dst as end_point, session as tp meter + * session and return it after having increment the refcounter. Return NULL + * is not found + * + * Return: matching tp_vars or NULL when no tp_vars was found + */ +static struct batadv_tp_receiver * +batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst, + const u8 *session) +{ + struct batadv_tp_receiver *pos, *tp_vars = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(pos, &bat_priv->tp_receiver_list, common.list) { + if (!batadv_compare_eth(pos->common.other_end, dst)) + continue; + + if (memcmp(pos->common.session, session, sizeof(pos->common.session)) != 0) + continue; + + /* most of the time this function is invoked during the normal + * process..it makes sense to pay more when the session is + * finished and to speed the process up during the measurement + */ + if (unlikely(!kref_get_unless_zero(&pos->common.refcount))) + continue; + + tp_vars = pos; + break; + } + rcu_read_unlock(); + + return tp_vars; +} + +/** + * batadv_tp_receiver_put() - decrement the batadv_tp_receiver + * refcounter and possibly release it + * @tp_vars: the private data of the current TP meter session to be free'd + */ +static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars) +{ + if (!tp_vars) + return; + + kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release); +} + /** * batadv_tp_reset_receiver_timer() - reset the receiver shutdown timer * @tp_vars: the private data of the current TP meter session * * start the receiver shutdown timer or reset it if already started */ -static void batadv_tp_reset_receiver_timer(struct batadv_tp_vars *tp_vars) +static void batadv_tp_reset_receiver_timer(struct batadv_tp_receiver *tp_vars) { - mod_timer(&tp_vars->timer, + mod_timer(&tp_vars->common.timer, jiffies + msecs_to_jiffies(BATADV_TP_RECV_TIMEOUT)); } @@ -1160,11 +1278,11 @@ static void batadv_tp_reset_receiver_timer(struct batadv_tp_vars *tp_vars) */ static void batadv_tp_receiver_shutdown(struct timer_list *t) { - struct batadv_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer); + struct batadv_tp_receiver *tp_vars = timer_container_of(tp_vars, t, common.timer); struct batadv_tp_unacked *un, *safe; struct batadv_priv *bat_priv; - bat_priv = tp_vars->bat_priv; + bat_priv = tp_vars->common.bat_priv; /* if there is recent activity rearm the timer */ if (!batadv_has_timed_out(tp_vars->last_recv_time, @@ -1176,22 +1294,23 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Shutting down for inactivity (more than %dms) from %pM\n", - BATADV_TP_RECV_TIMEOUT, tp_vars->other_end); + BATADV_TP_RECV_TIMEOUT, tp_vars->common.other_end); - batadv_tp_list_detach(tp_vars); + if (batadv_tp_list_detach(&tp_vars->common)) + batadv_tp_receiver_put(tp_vars); - spin_lock_bh(&tp_vars->unacked_lock); - list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + spin_lock_bh(&tp_vars->common.unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) { list_del(&un->list); kfree(un); } - spin_unlock_bh(&tp_vars->unacked_lock); + spin_unlock_bh(&tp_vars->common.unacked_lock); /* drop reference of timer */ if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1)) return; - batadv_tp_vars_put(tp_vars); + batadv_tp_receiver_put(tp_vars); } /** @@ -1275,7 +1394,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, * * Return: true if the packed has been successfully processed, false otherwise */ -static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, const struct sk_buff *skb) { const struct batadv_icmp_tp_packet *icmp; @@ -1293,10 +1412,10 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, payload_len = skb->len - sizeof(struct batadv_unicast_packet); new->len = payload_len; - spin_lock_bh(&tp_vars->unacked_lock); + spin_lock_bh(&tp_vars->common.unacked_lock); /* if the list is empty immediately attach this new object */ - if (list_empty(&tp_vars->unacked_list)) { - list_add(&new->list, &tp_vars->unacked_list); + if (list_empty(&tp_vars->common.unacked_list)) { + list_add(&new->list, &tp_vars->common.unacked_list); goto out; } @@ -1307,7 +1426,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * the last received packet (the one being processed now) has a bigger * seqno than all the others already stored. */ - list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) { + list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) { /* check for duplicates */ if (new->seqno == un->seqno) { if (new->len > un->len) @@ -1325,17 +1444,17 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * one is attached _after_ it. In this way the list is kept in * ascending order */ - list_add_tail(&new->list, &un->list); + list_add(&new->list, &un->list); added = true; break; } /* received packet with smallest seqno out of order; add it to front */ if (!added) - list_add(&new->list, &tp_vars->unacked_list); + list_add(&new->list, &tp_vars->common.unacked_list); out: - spin_unlock_bh(&tp_vars->unacked_lock); + spin_unlock_bh(&tp_vars->common.unacked_lock); return true; } @@ -1345,7 +1464,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, * without gaps * @tp_vars: the private data of the current TP meter session */ -static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) +static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) { struct batadv_tp_unacked *un, *safe; u32 to_ack; @@ -1353,8 +1472,8 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) /* go through the unacked packet list and possibly ACK them as * well */ - spin_lock_bh(&tp_vars->unacked_lock); - list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + spin_lock_bh(&tp_vars->common.unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon * there is a gap between the last acked seqno and the seqno of * the packet under inspection @@ -1370,7 +1489,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) list_del(&un->list); kfree(un); } - spin_unlock_bh(&tp_vars->unacked_lock); + spin_unlock_bh(&tp_vars->common.unacked_lock); } /** @@ -1380,20 +1499,22 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) * * Return: corresponding tp_vars or NULL on errors */ -static struct batadv_tp_vars * +static struct batadv_tp_receiver * batadv_tp_init_recv(struct batadv_priv *bat_priv, const struct batadv_icmp_tp_packet *icmp) { - struct batadv_tp_vars *tp_vars = NULL; + struct batadv_tp_receiver *tp_vars = NULL; spin_lock_bh(&bat_priv->tp_list_lock); - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto out_unlock; - tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, - icmp->session, BATADV_TP_RECEIVER); - if (tp_vars) + tp_vars = batadv_tp_list_find_receiver_session(bat_priv, icmp->orig, + icmp->session); + if (tp_vars) { + tp_vars->last_recv_time = jiffies; goto out_unlock; + } if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, @@ -1407,22 +1528,23 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, goto out_unlock; } - ether_addr_copy(tp_vars->other_end, icmp->orig); - tp_vars->role = BATADV_TP_RECEIVER; + ether_addr_copy(tp_vars->common.other_end, icmp->orig); atomic_set(&tp_vars->receiving, 1); - memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session)); + memcpy(tp_vars->common.session, icmp->session, sizeof(tp_vars->common.session)); tp_vars->last_recv = BATADV_TP_FIRST_SEQ; - tp_vars->bat_priv = bat_priv; - kref_init(&tp_vars->refcount); + tp_vars->common.bat_priv = bat_priv; + kref_init(&tp_vars->common.refcount); - spin_lock_init(&tp_vars->unacked_lock); - INIT_LIST_HEAD(&tp_vars->unacked_list); + spin_lock_init(&tp_vars->common.unacked_lock); + INIT_LIST_HEAD(&tp_vars->common.unacked_list); - kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + kref_get(&tp_vars->common.refcount); + timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0); - kref_get(&tp_vars->refcount); - timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + tp_vars->last_recv_time = jiffies; + + kref_get(&tp_vars->common.refcount); + hlist_add_head_rcu(&tp_vars->common.list, &bat_priv->tp_receiver_list); batadv_tp_reset_receiver_timer(tp_vars); @@ -1443,7 +1565,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, const struct sk_buff *skb) { const struct batadv_icmp_tp_packet *icmp; - struct batadv_tp_vars *tp_vars; + struct batadv_tp_receiver *tp_vars; size_t packet_size; u32 seqno; @@ -1461,17 +1583,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, goto out; } } else { - tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, - icmp->session, BATADV_TP_RECEIVER); + tp_vars = batadv_tp_list_find_receiver_session(bat_priv, icmp->orig, + icmp->session); if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Unexpected packet from %pM!\n", icmp->orig); goto out; } - } - tp_vars->last_recv_time = jiffies; + tp_vars->last_recv_time = jiffies; + } /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK @@ -1506,7 +1628,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, icmp->timestamp, icmp->session, icmp->uid); out: - batadv_tp_vars_put(tp_vars); + batadv_tp_receiver_put(tp_vars); } /** @@ -1518,7 +1640,7 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb) { struct batadv_icmp_tp_packet *icmp; - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto out; icmp = (struct batadv_icmp_tp_packet *)skb->data; @@ -1546,45 +1668,58 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb) */ void batadv_tp_stop_all(struct batadv_priv *bat_priv) { - struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM]; - struct batadv_tp_vars *tp_var; - size_t count = 0; + struct batadv_tp_receiver *tp_receivers[BATADV_TP_MAX_NUM]; + struct batadv_tp_sender *tp_senders[BATADV_TP_MAX_NUM]; + struct batadv_tp_receiver *tp_receiver; + struct batadv_tp_sender *tp_sender; + size_t receiver_count = 0; + size_t sender_count = 0; size_t i; spin_lock_bh(&bat_priv->tp_list_lock); - hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) { - if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM)) + hlist_for_each_entry(tp_receiver, &bat_priv->tp_receiver_list, common.list) { + if (WARN_ON_ONCE(receiver_count >= BATADV_TP_MAX_NUM)) break; - if (!kref_get_unless_zero(&tp_var->refcount)) + if (!kref_get_unless_zero(&tp_receiver->common.refcount)) continue; - tp_vars[count++] = tp_var; + tp_receivers[receiver_count++] = tp_receiver; + } + + hlist_for_each_entry(tp_sender, &bat_priv->tp_sender_list, common.list) { + if (WARN_ON_ONCE(sender_count >= BATADV_TP_MAX_NUM)) + break; + + if (!kref_get_unless_zero(&tp_sender->common.refcount)) + continue; + + tp_senders[sender_count++] = tp_sender; } spin_unlock_bh(&bat_priv->tp_list_lock); - for (i = 0; i < count; i++) { - tp_var = tp_vars[i]; + for (i = 0; i < receiver_count; i++) { + tp_receiver = tp_receivers[i]; - switch (tp_var->role) { - case BATADV_TP_SENDER: - batadv_tp_sender_shutdown(tp_var, - BATADV_TP_REASON_CANCEL); - wake_up(&tp_var->more_bytes); - wait_for_completion(&tp_var->finished); - break; - case BATADV_TP_RECEIVER: - batadv_tp_list_detach(tp_var); - timer_shutdown_sync(&tp_var->timer); + if (batadv_tp_list_detach(&tp_receiver->common)) + batadv_tp_receiver_put(tp_receiver); - if (atomic_xchg(&tp_var->receiving, 0) != 1) - break; + timer_shutdown_sync(&tp_receiver->common.timer); - batadv_tp_vars_put(tp_var); - break; - } + if (atomic_xchg(&tp_receiver->receiving, 0) != 0) + batadv_tp_receiver_put(tp_receiver); - batadv_tp_vars_put(tp_var); + batadv_tp_receiver_put(tp_receiver); + } + + for (i = 0; i < sender_count; i++) { + tp_sender = tp_senders[i]; + + batadv_tp_sender_shutdown(tp_sender, BATADV_TP_REASON_CANCEL); + wake_up(&tp_sender->more_bytes); + wait_for_completion(&tp_sender->finished); + + batadv_tp_sender_put(tp_sender); } synchronize_net(); diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 9f6e67771ffa..8b6c49c32c89 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -595,20 +596,23 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, struct net *net = dev_net(mesh_iface); struct batadv_meshif_vlan *vlan; struct net_device *in_dev = NULL; - struct batadv_hard_iface *in_hardif = NULL; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; int hash_added, table_size, packet_size_max; bool ret = false; bool roamed_back = false; + bool iif_is_wifi = false; u8 remote_flags; u32 match_mark; if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(net, ifindex); - if (in_dev) - in_hardif = batadv_hardif_get_by_netdev(in_dev); + if (in_dev) { + u32 wifi_flags = batadv_netdev_get_wifi_flags(in_dev); + + iif_is_wifi = batadv_is_wifi(wifi_flags); + } tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid); @@ -648,7 +652,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, /* Ignore the client if we cannot send it in a full table response. */ table_size = batadv_tt_local_table_transmit_size(bat_priv); table_size += batadv_tt_len(1); - packet_size_max = atomic_read(&bat_priv->packet_size_max); + packet_size_max = READ_ONCE(bat_priv->packet_size_max); if (table_size > packet_size_max) { net_ratelimited_function(batadv_info, mesh_iface, "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n", @@ -683,7 +687,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; tt_local->common.vid = vid; - if (batadv_is_wifi_hardif(in_hardif)) + if (iif_is_wifi) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; kref_init(&tt_local->common.refcount); tt_local->last_seen = jiffies; @@ -742,7 +746,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, */ remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK; - if (batadv_is_wifi_hardif(in_hardif)) + if (iif_is_wifi) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; else tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI; @@ -766,7 +770,6 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, ret = true; out: - batadv_hardif_put(in_hardif); dev_put(in_dev); batadv_tt_local_entry_put(tt_local); batadv_tt_global_entry_put(tt_global); @@ -796,21 +799,22 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, struct batadv_tvlv_tt_change **tt_change, s32 *tt_len) { - u16 num_vlan = 0; - u16 tvlv_len = 0; - unsigned int change_offset; struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_orig_node_vlan *vlan; u16 total_entries = 0; + size_t change_offset; u8 *tt_change_ptr; + u16 num_vlan = 0; int vlan_entries; u16 sum_entries; + u16 tvlv_len; spin_lock_bh(&orig_node->vlan_list_lock); hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { vlan_entries = atomic_read(&vlan->tt.num_entries); if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { + tvlv_len = 0; *tt_len = 0; goto out; } @@ -825,22 +829,21 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, if (*tt_len < 0) *tt_len = batadv_tt_len(total_entries); - if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) { + if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { + tvlv_len = 0; *tt_len = 0; goto out; } - tvlv_len = *tt_len; - tvlv_len += change_offset; - *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); if (!*tt_data) { + tvlv_len = 0; *tt_len = 0; goto out; } (*tt_data)->flags = BATADV_NO_FLAGS; - (*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn); + (*tt_data)->ttvn = READ_ONCE(orig_node->last_ttvn); (*tt_data)->num_vlan = htons(num_vlan); tt_vlan = (*tt_data)->vlan_data; @@ -869,6 +872,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, out: spin_unlock_bh(&orig_node->vlan_list_lock); + return tvlv_len; } @@ -898,13 +902,13 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, { struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_meshif_vlan *vlan; - size_t change_offset; - u16 num_vlan = 0; u16 total_entries = 0; - u16 tvlv_len; + size_t change_offset; u8 *tt_change_ptr; + u16 num_vlan = 0; int vlan_entries; u16 sum_entries; + u16 tvlv_len; spin_lock_bh(&bat_priv->meshif_vlan_list_lock); hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) { @@ -912,6 +916,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { tvlv_len = 0; + *tt_len = 0; goto out; } @@ -927,12 +932,14 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { tvlv_len = 0; + *tt_len = 0; goto out; } *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); if (!*tt_data) { tvlv_len = 0; + *tt_len = 0; goto out; } @@ -966,6 +973,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, out: spin_unlock_bh(&bat_priv->meshif_vlan_list_lock); + return tvlv_len; } @@ -1821,7 +1829,7 @@ batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq, if (!hdr) return -ENOBUFS; - last_ttvn = atomic_read(&orig->orig_node->last_ttvn); + last_ttvn = READ_ONCE(orig->orig_node->last_ttvn); if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) || nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, @@ -3012,7 +3020,7 @@ static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv, if (!res_dst_orig_node) goto out; - orig_ttvn = (u8)atomic_read(&req_dst_orig_node->last_ttvn); + orig_ttvn = READ_ONCE(req_dst_orig_node->last_ttvn); req_ttvn = tt_data->ttvn; /* this node doesn't have the requested data */ @@ -3068,7 +3076,7 @@ static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv, /* Don't send the response, if larger than fragmented packet. */ tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len; - if (tt_len > atomic_read(&bat_priv->packet_size_max)) { + if (tt_len > READ_ONCE(bat_priv->packet_size_max)) { net_ratelimited_function(batadv_info, bat_priv->mesh_iface, "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n", res_dst_orig_node->orig); @@ -3299,7 +3307,7 @@ static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, orig_node->tt_buff = NULL; spin_unlock_bh(&orig_node->tt_buff_lock); - atomic_set(&orig_node->last_ttvn, ttvn); + WRITE_ONCE(orig_node->last_ttvn, ttvn); out: batadv_orig_node_put(orig_node); @@ -3315,7 +3323,7 @@ static void batadv_tt_update_changes(struct batadv_priv *bat_priv, batadv_tt_save_orig_buffer(bat_priv, orig_node, tt_change, batadv_tt_len(tt_num_changes)); - atomic_set(&orig_node->last_ttvn, ttvn); + WRITE_ONCE(orig_node->last_ttvn, ttvn); } /** @@ -3566,7 +3574,7 @@ void batadv_tt_free(struct batadv_priv *bat_priv) batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_TT, 1); batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_TT, 1); - cancel_delayed_work_sync(&bat_priv->tt.work); + disable_delayed_work_sync(&bat_priv->tt.work); batadv_tt_local_table_free(bat_priv); batadv_tt_global_table_free(bat_priv); @@ -3729,7 +3737,7 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst, if (!vlan) return false; - if (!atomic_read(&vlan->ap_isolation)) + if (!READ_ONCE(vlan->ap_isolation)) goto vlan_put; tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid); @@ -3768,7 +3776,7 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv, struct batadv_tvlv_tt_change *tt_change, u16 tt_num_changes, u8 ttvn) { - u8 orig_ttvn = (u8)atomic_read(&orig_node->last_ttvn); + u8 orig_ttvn = READ_ONCE(orig_node->last_ttvn); struct batadv_tvlv_tt_vlan_data *tt_vlan; bool full_table = true; bool has_tt_init; @@ -3910,7 +3918,7 @@ bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, if (!batadv_tt_global_add(bat_priv, orig_node, addr, vid, BATADV_TT_CLIENT_TEMP, - atomic_read(&orig_node->last_ttvn))) + READ_ONCE(orig_node->last_ttvn))) return false; batadv_dbg(BATADV_DBG_TT, bat_priv, @@ -3931,7 +3939,7 @@ bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, void batadv_tt_local_resize_to_mtu(struct net_device *mesh_iface) { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); - int packet_size_max = atomic_read(&bat_priv->packet_size_max); + int packet_size_max = READ_ONCE(bat_priv->packet_size_max); int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2; bool reduced = false; @@ -4124,7 +4132,7 @@ static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, batadv_tt_global_add(bat_priv, orig_node, roaming_adv->client, ntohs(roaming_adv->vid), BATADV_TT_CLIENT_ROAM, - atomic_read(&orig_node->last_ttvn) + 1); + READ_ONCE(orig_node->last_ttvn) + 1); out: batadv_orig_node_put(orig_node); diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index cc6ac580c620..403c85456870 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -6,6 +6,7 @@ #include "main.h" +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +48,7 @@ static void batadv_tvlv_handler_release(struct kref *ref) } /** - * batadv_tvlv_handler_put() - decrement the tvlv container refcounter and + * batadv_tvlv_handler_put() - decrement the tvlv handler refcounter and * possibly release it * @tvlv_handler: the tvlv handler to free */ @@ -266,32 +268,48 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, /** * batadv_tvlv_realloc_packet_buff() - reallocate packet buffer to accommodate * requested packet size - * @packet_buff: packet buffer - * @packet_buff_len: packet buffer size - * @min_packet_len: requested packet minimum size + * @ogm_buff: ogm packet buffer * @additional_packet_len: requested additional packet size on top of minimum * size * - * Return: true of the packet buffer could be changed to the requested size, + * Return: true if the packet buffer could be changed to the requested size, * false otherwise. */ -static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, - int *packet_buff_len, - int min_packet_len, - int additional_packet_len) +static bool batadv_tvlv_realloc_packet_buff(struct batadv_ogm_buf *ogm_buff, + size_t additional_packet_len) { unsigned char *new_buff; + size_t newcapacity; + size_t newlen; - new_buff = kmalloc(min_packet_len + additional_packet_len, GFP_ATOMIC); + newlen = ogm_buff->header_length + additional_packet_len; + newcapacity = roundup_pow_of_two(newlen); + + /* nothing to reallocate */ + if (newcapacity == ogm_buff->capacity) { + ogm_buff->len = newlen; + return true; + } + + new_buff = kmalloc(newcapacity, GFP_ATOMIC); /* keep old buffer if kmalloc should fail */ - if (!new_buff) - return false; + if (!new_buff) { + /* continue to use oversize buffer if new data fits */ + if (newlen <= ogm_buff->capacity) { + ogm_buff->len = newlen; + return true; + } - memcpy(new_buff, *packet_buff, min_packet_len); - kfree(*packet_buff); - *packet_buff = new_buff; - *packet_buff_len = min_packet_len + additional_packet_len; + return false; + } + + memcpy(new_buff, ogm_buff->buf, ogm_buff->header_length); + kfree(ogm_buff->buf); + + ogm_buff->buf = new_buff; + ogm_buff->len = newlen; + ogm_buff->capacity = newcapacity; return true; } @@ -300,10 +318,7 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, * batadv_tvlv_container_ogm_append() - append tvlv container content to given * OGM packet buffer * @bat_priv: the bat priv with all the mesh interface information - * @packet_buff: ogm packet buffer - * @packet_buff_len: ogm packet buffer size including ogm header and tvlv - * content - * @packet_min_len: ogm header size to be preserved for the OGM itself + * @ogm_buff: ogm packet buffer * * The ogm packet might be enlarged or shrunk depending on the current size * and the size of the to-be-appended tvlv containers. @@ -312,8 +327,7 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, * if operation failed */ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, - unsigned char **packet_buff, - int *packet_buff_len, int packet_min_len) + struct batadv_ogm_buf *ogm_buff) { struct batadv_tvlv_container *tvlv; struct batadv_tvlv_hdr *tvlv_hdr; @@ -329,8 +343,7 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, goto end; } - ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, - packet_min_len, tvlv_value_len); + ret = batadv_tvlv_realloc_packet_buff(ogm_buff, tvlv_value_len); if (!ret) { tvlv_len_ret = -ENOMEM; goto end; @@ -341,7 +354,7 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, if (!tvlv_value_len) goto end; - tvlv_value = (*packet_buff) + packet_min_len; + tvlv_value = (u8 *)ogm_buff->buf + ogm_buff->header_length; hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) { tvlv_hdr = tvlv_value; diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index f96f6b3f44a0..fc9783850443 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -17,8 +17,7 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, u8 type, u8 version, void *tvlv_value, u16 tvlv_value_len); int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, - unsigned char **packet_buff, - int *packet_buff_len, int packet_min_len); + struct batadv_ogm_buf *ogm_buff); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, struct batadv_ogm_packet *batadv_ogm_packet, struct batadv_orig_node *orig_node); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index a01ee46d97f3..5fd5bd358a24 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -21,6 +21,7 @@ #include #include #include +#include #include /* for linux/wait.h */ #include #include @@ -70,15 +71,29 @@ enum batadv_dhcp_recipient { */ #define BATADV_TT_SYNC_MASK 0x00F0 +/** + * struct batadv_ogm_buf - Buffer to construct an OGM with TVLV + */ +struct batadv_ogm_buf { + /** @buf: buffer holding the OGM packet */ + void *buf; + + /** @len: length of the OGM packet buffer data */ + size_t len; + + /** @capacity: size of allocated buf */ + size_t capacity; + + /** @header_length: fixed size header length (must be <= len) */ + size_t header_length; +}; + /** * struct batadv_hard_iface_bat_iv - per hard-interface B.A.T.M.A.N. IV data */ struct batadv_hard_iface_bat_iv { /** @ogm_buff: buffer holding the OGM packet */ - unsigned char *ogm_buff; - - /** @ogm_buff_len: length of the OGM packet buffer */ - int ogm_buff_len; + struct batadv_ogm_buf ogm_buff; /** @ogm_seqno: OGM sequence number - used to identify each OGM */ atomic_t ogm_seqno; @@ -86,7 +101,7 @@ struct batadv_hard_iface_bat_iv { /** @reschedule_work: recover OGM schedule after schedule error */ struct delayed_work reschedule_work; - /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */ + /** @ogm_buff_mutex: lock protecting ogm_buff */ struct mutex ogm_buff_mutex; }; @@ -113,7 +128,7 @@ enum batadv_v_hard_iface_flags { */ struct batadv_hard_iface_bat_v { /** @elp_interval: time interval between two ELP transmissions */ - atomic_t elp_interval; + u32 elp_interval; /** @elp_seqno: current ELP sequence number */ atomic_t elp_seqno; @@ -137,7 +152,7 @@ struct batadv_hard_iface_bat_v { * @throughput_override: throughput override to disable link * auto-detection */ - atomic_t throughput_override; + u32 throughput_override; /** @flags: interface specific flags */ u8 flags; @@ -166,6 +181,29 @@ enum batadv_hard_iface_wifi_flags { BATADV_HARDIF_WIFI_CFG80211_INDIRECT = BIT(3), }; +/** + * struct batadv_wifi_net_device_state - cache of wifi information of net_devices + */ +struct batadv_wifi_net_device_state { + /** @l: anchor in rhashtable */ + struct rhash_head l; + + /** @netdev: pointer to the net_device */ + struct net_device *netdev; + + /** @dev_tracker: device tracker for @netdev */ + netdevice_tracker dev_tracker; + + /** + * @wifi_flags: flags whether this is (directly or indirectly) a wifi + * interface + */ + u32 wifi_flags; + + /** @rcu: struct used for freeing in an RCU-safe manner */ + struct rcu_head rcu; +}; + /** * struct batadv_hard_iface - network device known to batman-adv */ @@ -181,12 +219,6 @@ struct batadv_hard_iface { */ u8 num_bcasts; - /** - * @wifi_flags: flags whether this is (directly or indirectly) a wifi - * interface - */ - u32 wifi_flags; - /** @net_dev: pointer to the net_device */ struct net_device *net_dev; @@ -218,7 +250,7 @@ struct batadv_hard_iface { * @hop_penalty: penalty which will be applied to the tq-field * of an OGM received via this interface */ - atomic_t hop_penalty; + u8 hop_penalty; /** @bat_iv: per hard-interface B.A.T.M.A.N. IV data */ struct batadv_hard_iface_bat_iv bat_iv; @@ -446,7 +478,7 @@ struct batadv_orig_node { unsigned long capa_initialized; /** @last_ttvn: last seen translation table version number */ - atomic_t last_ttvn; + u8 last_ttvn; /** @tt_buff: last tt changeset this node received from the orig node */ unsigned char *tt_buff; @@ -631,8 +663,15 @@ struct batadv_neigh_node { /** @list: list node for &batadv_orig_node.neigh_list */ struct hlist_node list; - /** @orig_node: pointer to corresponding orig_node */ - struct batadv_orig_node *orig_node; +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + /** + * @orig_node_id: pointer to corresponding orig_node. It must only be used + * to identify the node but must NEVER be dereferenced. The reference counter + * was not increased when this was assigned because it would otherwise create + * a reference cycle. + */ + struct batadv_orig_node *__private orig_node_id; +#endif /** @addr: the MAC address of the neighboring interface */ u8 addr[ETH_ALEN]; @@ -1101,21 +1140,21 @@ struct batadv_priv_gw { /** * @mode: gateway operation: off, client or server (see batadv_gw_modes) */ - atomic_t mode; + enum batadv_gw_modes mode; /** @sel_class: gateway selection class (applies if gw_mode client) */ - atomic_t sel_class; + u32 sel_class; /** * @bandwidth_down: advertised uplink download bandwidth (if gw_mode * server) */ - atomic_t bandwidth_down; + u32 bandwidth_down; /** * @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server) */ - atomic_t bandwidth_up; + u32 bandwidth_up; /** @reselect: bool indicating a gateway re-selection is in progress */ atomic_t reselect; @@ -1292,26 +1331,15 @@ struct batadv_tp_unacked { /** @len: length of the packet */ u16 len; - /** @list: list node for &batadv_tp_vars.unacked_list */ + /** @list: list node for &batadv_tp_vars_common.unacked_list */ struct list_head list; }; /** - * enum batadv_tp_meter_role - Modus in tp meter session + * struct batadv_tp_vars_common - common tp meter private variables per session */ -enum batadv_tp_meter_role { - /** @BATADV_TP_RECEIVER: Initialized as receiver */ - BATADV_TP_RECEIVER, - - /** @BATADV_TP_SENDER: Initialized as sender */ - BATADV_TP_SENDER -}; - -/** - * struct batadv_tp_vars - tp meter private variables per session - */ -struct batadv_tp_vars { - /** @list: list node for &bat_priv.tp_list */ +struct batadv_tp_vars_common { + /** @list: list node for &bat_priv.tp_sender_list/&bat_priv.tp_receiver_list */ struct hlist_node list; /** @timer: timer for ack (receiver) and retry (sender) */ @@ -1320,40 +1348,34 @@ struct batadv_tp_vars { /** @bat_priv: pointer to the mesh object */ struct batadv_priv *bat_priv; - /** @start_time: start time in jiffies */ - unsigned long start_time; - /** @other_end: mac address of remote */ u8 other_end[ETH_ALEN]; - /** @role: receiver/sender modi */ - enum batadv_tp_meter_role role; - - /** - * @send_result: 0 when sending is ongoing and otherwise - * enum batadv_tp_meter_reason - */ - atomic_t send_result; - - /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ - atomic_t receiving; - - /** @finish_work: work item for the finishing procedure */ - struct delayed_work finish_work; - - /** @finished: completion signaled when a sender thread exits */ - struct completion finished; - - /** @test_length: test length in milliseconds */ - u32 test_length; - /** @session: TP session identifier */ u8 session[2]; - /** @icmp_uid: local ICMP "socket" index */ - u8 icmp_uid; + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; - /* sender variables */ + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + + /** @refcount: number of context where the object is used */ + struct kref refcount; + + /** @rcu: struct used for freeing in an RCU-safe manner */ + struct rcu_head rcu; +}; + +/** + * struct batadv_tp_sender_cc - congestion control variables + */ +struct batadv_tp_sender_cc { + /** @fast_recovery: true if in Fast Recovery mode */ + bool fast_recovery:1; + + /** @dup_acks: duplicate ACKs counter */ + u8 dup_acks; /** @dec_cwnd: decimal part of the cwnd used during linear growth */ u16 dec_cwnd; @@ -1361,9 +1383,6 @@ struct batadv_tp_vars { /** @cwnd: current size of the congestion window */ u32 cwnd; - /** @cwnd_lock: lock do protect @cwnd & @dec_cwnd */ - spinlock_t cwnd_lock; - /** * @ss_threshold: Slow Start threshold. Once cwnd exceeds this value the * connection switches to the Congestion Avoidance state @@ -1371,20 +1390,11 @@ struct batadv_tp_vars { u32 ss_threshold; /** @last_acked: last acked byte */ - atomic_t last_acked; + u32 last_acked; /** @last_sent: last sent byte, not yet acked */ u32 last_sent; - /** @tot_sent: amount of data sent/ACKed so far */ - atomic64_t tot_sent; - - /** @dup_acks: duplicate ACKs counter */ - atomic_t dup_acks; - - /** @fast_recovery: true if in Fast Recovery mode */ - unsigned char fast_recovery:1; - /** @recover: last sent seqno when entering Fast Recovery */ u32 recover; @@ -1396,6 +1406,44 @@ struct batadv_tp_vars { /** @rttvar: RTT variation scaled by 2^2 */ u32 rttvar; +}; + +/** + * struct batadv_tp_sender - sender tp meter private variables per session + */ +struct batadv_tp_sender { + /** @common: common batadv_tp_vars_common (must be first member) */ + struct batadv_tp_vars_common common; + + /** @start_time: start time in jiffies */ + unsigned long start_time; + + /** + * @send_result: 0 when sending is ongoing and otherwise + * enum batadv_tp_meter_reason + */ + atomic_t send_result; + + /** @finish_work: work item for the finishing procedure */ + struct delayed_work finish_work; + + /** @finished: completion signaled when a sender thread exits */ + struct completion finished; + + /** @test_length: test length in milliseconds */ + u32 test_length; + + /** @icmp_uid: local ICMP "socket" index */ + u8 icmp_uid; + + /** @cc: congestion control variables */ + struct batadv_tp_sender_cc cc; + + /** @cc_lock: lock to protect @cc */ + spinlock_t cc_lock; + + /** @tot_sent: amount of data sent/ACKed so far */ + atomic64_t tot_sent; /** * @more_bytes: waiting queue anchor when waiting for more ack/retry @@ -1408,26 +1456,23 @@ struct batadv_tp_vars { /** @prerandom_lock: spinlock protecting access to prerandom_offset */ spinlock_t prerandom_lock; +}; - /* receiver variables */ +/** + * struct batadv_tp_receiver - receiver tp meter private variables per session + */ +struct batadv_tp_receiver { + /** @common: common batadv_tp_vars_common (must be first member) */ + struct batadv_tp_vars_common common; + + /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ + atomic_t receiving; /** @last_recv: last in-order received packet */ u32 last_recv; - /** @unacked_list: list of unacked packets (meta-info only) */ - struct list_head unacked_list; - - /** @unacked_lock: protect unacked_list */ - spinlock_t unacked_lock; - /** @last_recv_time: time (jiffies) a msg was received */ unsigned long last_recv_time; - - /** @refcount: number of context where the object is used */ - struct kref refcount; - - /** @rcu: struct used for freeing in an RCU-safe manner */ - struct rcu_head rcu; }; /** @@ -1441,7 +1486,7 @@ struct batadv_meshif_vlan { unsigned short vid; /** @ap_isolation: AP isolation state */ - atomic_t ap_isolation; /* boolean */ + u8 ap_isolation; /* boolean */ /** @tt: TT private attributes (VLAN specific) */ struct batadv_vlan_tt tt; @@ -1463,15 +1508,12 @@ struct batadv_meshif_vlan { */ struct batadv_priv_bat_v { /** @ogm_buff: buffer holding the OGM packet */ - unsigned char *ogm_buff; - - /** @ogm_buff_len: length of the OGM packet buffer */ - int ogm_buff_len; + struct batadv_ogm_buf ogm_buff; /** @ogm_seqno: OGM sequence number - used to identify each OGM */ atomic_t ogm_seqno; - /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */ + /** @ogm_buff_mutex: lock protecting ogm_buff */ struct mutex ogm_buff_mutex; /** @ogm_wq: workqueue used to schedule OGM transmissions */ @@ -1486,7 +1528,7 @@ struct batadv_priv { * @mesh_state: current status of the mesh * (inactive/active/deactivating) */ - atomic_t mesh_state; + enum batadv_mesh_state mesh_state; /** @mesh_iface: net device which holds this struct as private data */ struct net_device *mesh_iface; @@ -1506,36 +1548,23 @@ struct batadv_priv { /** * @aggregated_ogms: bool indicating whether OGM aggregation is enabled */ - atomic_t aggregated_ogms; + u8 aggregated_ogms; /** @bonding: bool indicating whether traffic bonding is enabled */ - atomic_t bonding; + u8 bonding; /** * @fragmentation: bool indicating whether traffic fragmentation is * enabled */ - atomic_t fragmentation; - - /** - * @packet_size_max: max packet size that can be transmitted via - * multiple fragmented skbs or a single frame if fragmentation is - * disabled - */ - atomic_t packet_size_max; - - /** - * @frag_seqno: incremental counter to identify chains of egress - * fragments - */ - atomic_t frag_seqno; + u8 fragmentation; #ifdef CONFIG_BATMAN_ADV_BLA /** * @bridge_loop_avoidance: bool indicating whether bridge loop * avoidance is enabled */ - atomic_t bridge_loop_avoidance; + u8 bridge_loop_avoidance; #endif #ifdef CONFIG_BATMAN_ADV_DAT @@ -1543,7 +1572,7 @@ struct batadv_priv { * @distributed_arp_table: bool indicating whether distributed ARP table * is enabled */ - atomic_t distributed_arp_table; + u8 distributed_arp_table; #endif #ifdef CONFIG_BATMAN_ADV_MCAST @@ -1551,27 +1580,40 @@ struct batadv_priv { * @multicast_mode: Enable or disable multicast optimizations on this * node's sender/originating side */ - atomic_t multicast_mode; + u8 multicast_mode; /** * @multicast_fanout: Maximum number of packet copies to generate for a * multicast-to-unicast conversion */ - atomic_t multicast_fanout; + u32 multicast_fanout; #endif + /** + * @packet_size_max: max packet size that can be transmitted via + * multiple fragmented skbs or a single frame if fragmentation is + * disabled + */ + int packet_size_max; + + /** + * @frag_seqno: incremental counter to identify chains of egress + * fragments + */ + atomic_t frag_seqno; + /** @orig_interval: OGM broadcast interval in milliseconds */ - atomic_t orig_interval; + u32 orig_interval; /** * @hop_penalty: penalty which will be applied to an OGM's tq-field on * every hop */ - atomic_t hop_penalty; + u8 hop_penalty; #ifdef CONFIG_BATMAN_ADV_DEBUG /** @log_level: configured log level (see batadv_dbg_level) */ - atomic_t log_level; + u32 log_level; #endif /** @@ -1607,8 +1649,11 @@ struct batadv_priv { */ struct hlist_head forw_bcast_list; - /** @tp_list: list of tp sessions */ - struct hlist_head tp_list; + /** @tp_sender_list: list of tp sender sessions */ + struct hlist_head tp_sender_list; + + /** @tp_receiver_list: list of tp receiver sessions */ + struct hlist_head tp_receiver_list; /** @orig_hash: hash table containing mesh participants (orig nodes) */ struct batadv_hashtable *orig_hash; @@ -1619,7 +1664,7 @@ struct batadv_priv { /** @forw_bcast_list_lock: lock protecting forw_bcast_list */ spinlock_t forw_bcast_list_lock; - /** @tp_list_lock: spinlock protecting @tp_list */ + /** @tp_list_lock: spinlock protecting @tp_sender_list + @tp_receiver_list */ spinlock_t tp_list_lock; /** @tp_num: number of currently active tp sessions */ @@ -1678,22 +1723,26 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_BLA +/** + * enum batadv_bla_backbone_gw_state - state of a bridge loop avoidance + * backbone gateway + */ enum batadv_bla_backbone_gw_state { /** * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed - * and it must not longer work on requests + * and it must no longer work on requests */ BATADV_BLA_BACKBONE_GW_STOPPED, /** * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out - * of sync and a request was send. No traffic is forwarded until the + * of sync and a request was sent. No traffic is forwarded until the * situation is resolved */ BATADV_BLA_BACKBONE_GW_UNSYNCED, /** - * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in + * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is considered to be in * sync. traffic can be forwarded */ BATADV_BLA_BACKBONE_GW_SYNCED, diff --git a/net/batman-adv/version.c b/net/batman-adv/version.c new file mode 100644 index 000000000000..2b8006fe85b5 --- /dev/null +++ b/net/batman-adv/version.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +/* WARNING userspace tools like batctl were relying on + * /sys/module/batman_adv/version to check if the module was loaded. If it + * isn't present, they usually error out before finishing setup of the batadv + * interface. It should be kept until it is unlikely that there are active + * installations of these "broken" versions of these tools with recent kernels. + */ +MODULE_VERSION(UTS_RELEASE); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 960a19b3e26d..cb1e329d66fd 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -760,13 +760,33 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) return chan; } +static void unregister_dev(struct lowpan_btle_dev *dev) +{ + struct hci_dev *hdev = READ_ONCE(dev->hdev); + + /* If netdev holds last reference to hci_dev (its parent device), this + * leads to theoretical cyclic locking on lowpan_unregister_netdev: + * + * rtnl_lock -> put_device(parent) -> hci_release_dev -> + * destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata -> + * chan_ready_cb -> ifup -> rtnl_lock + * + * However, hci_rx_work is disabled in hci_unregister_dev, so this + * should not occur. Make lockdep happy by postponing hdev release after + * netdev put. + */ + hci_dev_hold(hdev); + lowpan_unregister_netdev(dev->netdev); + hci_dev_put(hdev); +} + static void delete_netdev(struct work_struct *work) { struct lowpan_btle_dev *entry = container_of(work, struct lowpan_btle_dev, delete_netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); /* The entry pointer is deleted by the netdev destructor. */ } @@ -1252,6 +1272,7 @@ static void disconnect_devices(void) break; new_dev->netdev = entry->netdev; + new_dev->hdev = entry->hdev; INIT_LIST_HEAD(&new_dev->list); list_add_rcu(&new_dev->list, &devices); @@ -1263,7 +1284,7 @@ static void disconnect_devices(void) ifdown(entry->netdev); BT_DBG("Unregistering netdev %s %p", entry->netdev->name, entry->netdev); - lowpan_unregister_netdev(entry->netdev); + unregister_dev(entry); kfree(entry); } } diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index ee6457d1a5ee..d250e94e90eb 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -9,12 +9,8 @@ menuconfig BT depends on RFKILL || !RFKILL select CRC16 select CRYPTO - select CRYPTO_SKCIPHER select CRYPTO_LIB_AES - imply CRYPTO_AES - select CRYPTO_CMAC - select CRYPTO_ECB - select CRYPTO_SHA256 + select CRYPTO_LIB_AES_CBC_MACS select CRYPTO_ECDH help Bluetooth is low-cost, low-power, short-range wireless technology. diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 1a6aa3f8d4d6..bcbc11c9cb15 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 5c5f53ff30e8..add9a8f7535d 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BNEP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2001-2002 Inventel Systemes @@ -7,10 +8,6 @@ Copyright (C) 2002 Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index cc1cff63194f..ee1e39a3daff 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BNEP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2001-2002 Inventel Systemes @@ -7,10 +8,6 @@ Copyright (C) 2002 Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 00d47bcf4d7d..f3fe818d0e4c 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BNEP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2001-2002 Inventel Systemes @@ -6,10 +7,6 @@ Copyright (C) 2002 Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c index 0efc93fdae8a..1938e5559d88 100644 --- a/net/bluetooth/ecdh_helper.c +++ b/net/bluetooth/ecdh_helper.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ECDH helper functions - KPP wrappings * * Copyright (C) 2017 Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/ecdh_helper.h b/net/bluetooth/ecdh_helper.h index 830723971cf8..9d787d3c8797 100644 --- a/net/bluetooth/ecdh_helper.h +++ b/net/bluetooth/ecdh_helper.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * ECDH helper functions - KPP wrappings * * Copyright (C) 2017 Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c index 3f72111ba651..1de5f9df6eec 100644 --- a/net/bluetooth/eir.c +++ b/net/bluetooth/eir.c @@ -283,10 +283,12 @@ u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size) if (!flags) flags |= mgmt_get_adv_discov_flags(hdev); - /* If flags would still be empty, then there is no need to - * include the "Flags" AD field". + /* Only add the "Flags" if it fits together with the instance + * advertising data; drop it rather than overflow the buffer. */ - if (flags && (ad_len + eir_precalc_len(1) <= size)) { + if (flags && + (ad_len + eir_precalc_len(1) + + (adv ? adv->adv_data_len : 0) <= size)) { ptr[0] = 0x02; ptr[1] = EIR_FLAGS; ptr[2] = flags; diff --git a/net/bluetooth/hci_codec.c b/net/bluetooth/hci_codec.c index 3cc135bb1d30..5bc5003c387c 100644 --- a/net/bluetooth/hci_codec.c +++ b/net/bluetooth/hci_codec.c @@ -100,7 +100,7 @@ static void hci_read_codec_capabilities(struct hci_dev *hdev, __u8 transport, caps = (void *)skb->data; if (skb->len < sizeof(*caps)) goto error; - if (skb->len < caps->len) + if (skb->len < sizeof(caps->len) + caps->len) goto error; len += sizeof(caps->len) + caps->len; skb_pull(skb, sizeof(caps->len) + caps->len); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 54eabaa46960..c335372e4062 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. @@ -5,10 +6,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 28d7929dc593..5ba9fe8261ec 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -5,10 +6,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -2671,6 +2668,8 @@ void hci_unregister_dev(struct hci_dev *hdev) disable_work_sync(&hdev->tx_work); disable_work_sync(&hdev->power_on); disable_work_sync(&hdev->error_reset); + disable_delayed_work_sync(&hdev->cmd_timer); + disable_delayed_work_sync(&hdev->ncmd_timer); hci_cmd_sync_clear(hdev); diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 99e2e9fc70e8..0635e4641db4 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2014 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/hci_debugfs.h b/net/bluetooth/hci_debugfs.h index 9a8a7c93bb12..92365cbc922a 100644 --- a/net/bluetooth/hci_debugfs.h +++ b/net/bluetooth/hci_debugfs.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2014 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eea2f810aafa..b6d963ce26d0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. @@ -5,10 +6,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -1769,6 +1766,13 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable) hci_dev_clear_flag(hdev, HCI_LE_SCAN); + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && + hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY) && + !test_bit(HCI_INQUIRY, &hdev->flags) && + hdev->discovery.state == DISCOVERY_FINDING) { + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + } + /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we * interrupted scanning due to a connect request. Mark * therefore discovery as stopped. diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 0290dea081f6..070ca388f9ac 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -27,6 +24,7 @@ #include #include #include +#include #include #include @@ -2063,7 +2061,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, } static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *sopt) { struct hci_ufilter uf; struct sock *sk = sock->sk; @@ -2071,8 +2069,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname, BT_DBG("sk %p, opt %d", sk, optname); - if (get_user(len, optlen)) - return -EFAULT; + len = sopt->optlen; lock_sock(sk); @@ -2088,7 +2085,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname, else opt = 0; - if (put_user(opt, optval)) + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -2098,7 +2096,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname, else opt = 0; - if (put_user(opt, optval)) + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -2114,7 +2113,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname, } len = min_t(unsigned int, len, sizeof(uf)); - if (copy_to_user(optval, &uf, len)) + if (copy_to_iter(&uf, len, &sopt->iter_out) != len) err = -EFAULT; break; @@ -2129,16 +2128,16 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname, } static int hci_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *sopt) { struct sock *sk = sock->sk; int err = 0; + u16 mtu; BT_DBG("sk %p, opt %d", sk, optname); if (level == SOL_HCI) - return hci_sock_getsockopt_old(sock, level, optname, optval, - optlen); + return hci_sock_getsockopt_old(sock, level, optname, sopt); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; @@ -2148,7 +2147,9 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, switch (optname) { case BT_SNDMTU: case BT_RCVMTU: - if (put_user(hci_pi(sk)->mtu, (u16 __user *)optval)) + mtu = hci_pi(sk)->mtu; + if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) != + sizeof(mtu)) err = -EFAULT; break; @@ -2185,7 +2186,7 @@ static const struct proto_ops hci_sock_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = hci_sock_setsockopt, - .getsockopt = hci_sock_getsockopt, + .getsockopt_iter = hci_sock_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index df23245d6ccd..3be8c3581c6c 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4588,11 +4588,33 @@ static int hci_set_le_support_sync(struct hci_dev *hdev) sizeof(cp), &cp, HCI_CMD_TIMEOUT); } +/* LE Set Host Feature V2 */ +static int hci_le_set_host_feature_v2_sync(struct hci_dev *hdev, u16 bit, + u8 value) +{ + struct hci_cp_le_set_host_feature_v2 cp; + + memset(&cp, 0, sizeof(cp)); + + /* Connected Isochronous Channels (Host Support) */ + cp.bit_number = cpu_to_le16(bit); + cp.bit_value = value; + + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HOST_FEATURE_V2, + sizeof(cp), &cp, HCI_CMD_TIMEOUT); +} + /* LE Set Host Feature */ -static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u8 bit, u8 value) +static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u16 bit, u8 value) { struct hci_cp_le_set_host_feature cp; + if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(4)) + return hci_le_set_host_feature_v2_sync(hdev, bit, value); + + if (bit > 255) + return 0; + memset(&cp, 0, sizeof(cp)); /* Connected Isochronous Channels (Host Support) */ diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 70344bd3248a..0e24c5e2955e 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann Copyright (C) 2013 David Herrmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 6ef88d0a1919..959b745bb770 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index c93aaeb3a3fa..b75bea3cf422 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* HIDP implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2003-2004 Marcel Holtmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 3abd8111dda8..793a481d7066 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -1882,18 +1883,17 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, } static int iso_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; - int len, err = 0; struct bt_iso_qos *qos; + int len, val, err = 0; u8 base_len; u8 *base; BT_DBG("sk %p", sk); - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; lock_sock(sk); @@ -1904,15 +1904,17 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), - (u32 __user *)optval)) + val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + if (copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val)) err = -EFAULT; break; case BT_PKT_STATUS: - if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags), - (int __user *)optval)) + val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags); + if (copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val)) err = -EFAULT; break; @@ -1920,7 +1922,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, qos = iso_sock_get_qos(sk); len = min_t(unsigned int, len, sizeof(*qos)); - if (copy_to_user(optval, qos, len)) + if (copy_to_iter(qos, len, &opt->iter_out) != len) err = -EFAULT; break; @@ -1936,9 +1938,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, } len = min_t(unsigned int, len, base_len); - if (copy_to_user(optval, base, len)) - err = -EFAULT; - if (put_user(len, optlen)) + opt->optlen = len; + if (copy_to_iter(base, len, &opt->iter_out) != len) err = -EFAULT; break; @@ -2705,7 +2706,7 @@ static const struct proto_ops iso_sock_ops = { .socketpair = sock_no_socketpair, .shutdown = iso_sock_shutdown, .setsockopt = iso_sock_setsockopt, - .getsockopt = iso_sock_getsockopt + .getsockopt_iter = iso_sock_getsockopt }; static const struct net_proto_family iso_sock_family_ops = { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c4ccfbda9d78..62133eef9d2f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -8,10 +9,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -411,7 +408,7 @@ static void l2cap_chan_timeout(struct work_struct *work) BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - if (!conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_put(chan); return; } @@ -422,6 +419,9 @@ static void l2cap_chan_timeout(struct work_struct *work) */ l2cap_chan_lock(chan); + if (test_bit(FLAG_DEL, &chan->flags)) + goto unlock; + if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; else if (chan->state == BT_CONNECT && @@ -434,10 +434,10 @@ static void l2cap_chan_timeout(struct work_struct *work) chan->ops->close(chan); +unlock: l2cap_chan_unlock(chan); - l2cap_chan_put(chan); - mutex_unlock(&conn->lock); + l2cap_chan_put(chan); } struct l2cap_chan *l2cap_chan_create(void) @@ -490,6 +490,9 @@ static void l2cap_chan_destroy(struct kref *kref) list_del(&chan->global_l); write_unlock(&chan_list_lock); + if (chan->conn) + l2cap_conn_put(chan->conn); + kfree(chan); } @@ -593,7 +596,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - chan->conn = conn; + chan->conn = l2cap_conn_get(conn); switch (chan->chan_type) { case L2CAP_CHAN_CONN_ORIENTED: @@ -648,30 +651,26 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) void l2cap_chan_del(struct l2cap_chan *chan, int err) { - struct l2cap_conn *conn = chan->conn; - __clear_chan_timer(chan); - BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err, + BT_DBG("chan %p, err %d, state %s", chan, err, state_to_string(chan->state)); chan->ops->teardown(chan, err); - if (conn) { + if (!test_and_set_bit(FLAG_DEL, &chan->flags)) { /* Delete from channel list */ list_del(&chan->list); l2cap_chan_put(chan); - chan->conn = NULL; - /* Reference was only held for non-fixed channels or * fixed channels that explicitly requested it using the * FLAG_HOLD_HCI_CONN flag. */ if (chan->chan_type != L2CAP_CHAN_FIXED || test_bit(FLAG_HOLD_HCI_CONN, &chan->flags)) - hci_conn_drop(conn->hcon); + hci_conn_drop(chan->conn->hcon); } if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) @@ -1903,7 +1902,7 @@ static void l2cap_monitor_timeout(struct work_struct *work) l2cap_chan_lock(chan); - if (!chan->conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; @@ -1924,7 +1923,7 @@ static void l2cap_retrans_timeout(struct work_struct *work) l2cap_chan_lock(chan); - if (!chan->conn) { + if (test_bit(FLAG_DEL, &chan->flags)) { l2cap_chan_unlock(chan); l2cap_chan_put(chan); return; @@ -2565,7 +2564,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, int err; struct sk_buff_head seg_queue; - if (!chan->conn) + if (test_bit(FLAG_DEL, &chan->flags)) return -ENOTCONN; /* Connectionless channel */ @@ -3160,12 +3159,16 @@ static void l2cap_ack_timeout(struct work_struct *work) l2cap_chan_lock(chan); + if (test_bit(FLAG_DEL, &chan->flags)) + goto unlock; + frames_to_ack = __seq_offset(chan, chan->buffer_seq, chan->last_acked_seq); if (frames_to_ack) l2cap_send_rr_or_rnr(chan, 0); +unlock: l2cap_chan_unlock(chan); l2cap_chan_put(chan); } @@ -7026,6 +7029,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) break; case L2CAP_CID_CONN_LESS: + if (skb->len < L2CAP_PSMLEN_SIZE) { + kfree_skb(skb); + break; + } + psm = get_unaligned((__le16 *) skb->data); skb_pull(skb, L2CAP_PSMLEN_SIZE); l2cap_conless_channel(conn, psm, skb); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index c138aa4ae266..4853f1b33449 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -7,10 +8,6 @@ Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -31,6 +28,7 @@ #include #include #include +#include #include #include @@ -438,7 +436,7 @@ static int l2cap_get_mode(struct l2cap_chan *chan) } static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, - char __user *optval, int __user *optlen) + sockopt_t *sopt) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -450,8 +448,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, BT_DBG("sk %p", sk); - if (get_user(len, optlen)) - return -EFAULT; + len = sopt->optlen; lock_sock(sk); @@ -493,7 +490,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, BT_DBG("mode 0x%2.2x", chan->mode); len = min(len, sizeof(opts)); - if (copy_to_user(optval, (char *) &opts, len)) + if (copy_to_iter(&opts, len, &sopt->iter_out) != len) err = -EFAULT; break; @@ -525,7 +522,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) opt |= L2CAP_LM_RELIABLE; - if (put_user(opt, (u32 __user *) optval)) + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -543,7 +541,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3); len = min(len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) + if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len) err = -EFAULT; break; @@ -558,25 +556,26 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, } static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *sopt) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; struct bt_power pwr; - u32 phys; int len, mode, err = 0; + u32 opt; + u16 mtu; + u8 mval; BT_DBG("sk %p", sk); if (level == SOL_L2CAP) - return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); + return l2cap_sock_getsockopt_old(sock, optname, sopt); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; + len = sopt->optlen; lock_sock(sk); @@ -600,7 +599,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, } len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) + if (copy_to_iter(&sec, len, &sopt->iter_out) != len) err = -EFAULT; break; @@ -611,15 +610,17 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), - (u32 __user *) optval)) + opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; case BT_FLUSHABLE: - if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), - (u32 __user *) optval)) + opt = test_bit(FLAG_FLUSHABLE, &chan->flags); + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -634,13 +635,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); len = min_t(unsigned int, len, sizeof(pwr)); - if (copy_to_user(optval, (char *) &pwr, len)) + if (copy_to_iter(&pwr, len, &sopt->iter_out) != len) err = -EFAULT; break; case BT_CHANNEL_POLICY: - if (put_user(chan->chan_policy, (u32 __user *) optval)) + opt = chan->chan_policy; + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -655,7 +658,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(chan->omtu, (u16 __user *) optval)) + mtu = chan->omtu; + if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) != + sizeof(mtu)) err = -EFAULT; break; @@ -665,7 +670,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(chan->imtu, (u16 __user *) optval)) + mtu = chan->imtu; + if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) != + sizeof(mtu)) err = -EFAULT; break; @@ -675,9 +682,10 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; } - phys = hci_conn_get_phy(chan->conn->hcon); + opt = hci_conn_get_phy(chan->conn->hcon); - if (put_user(phys, (u32 __user *) optval)) + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -698,7 +706,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(mode, (u8 __user *) optval)) + mval = mode; + if (copy_to_iter(&mval, sizeof(mval), &sopt->iter_out) != + sizeof(mval)) err = -EFAULT; break; @@ -2039,7 +2049,7 @@ static const struct proto_ops l2cap_sock_ops = { .socketpair = sock_no_socketpair, .shutdown = l2cap_sock_shutdown, .setsockopt = l2cap_sock_setsockopt, - .getsockopt = l2cap_sock_getsockopt + .getsockopt_iter = l2cap_sock_getsockopt }; static const struct net_proto_family l2cap_sock_family_ops = { diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 305044a84478..cea21a4c49a0 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f4aa814a0397..d23ca1dd0893 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2010 Nokia Corporation Copyright (C) 2011-2012 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c index 4f19654d41a9..6ea107c0e054 100644 --- a/net/bluetooth/mgmt_util.c +++ b/net/bluetooth/mgmt_util.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2015 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h index bcba8c9d8952..20810cf06e81 100644 --- a/net/bluetooth/mgmt_util.h +++ b/net/bluetooth/mgmt_util.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2015 Intel Coropration - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 364b9381c2dc..ebeae17b71d1 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 805ed5d28ed6..feb302a491fa 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -28,6 +25,7 @@ #include #include #include +#include #include #include @@ -740,7 +738,8 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, return err; } -static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) +static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, + sockopt_t *sopt) { struct sock *sk = sock->sk; struct sock *l2cap_sk; @@ -752,8 +751,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u BT_DBG("sk %p", sk); - if (get_user(len, optlen)) - return -EFAULT; + len = sopt->optlen; lock_sock(sk); @@ -782,7 +780,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u if (rfcomm_pi(sk)->role_switch) opt |= RFCOMM_LM_MASTER; - if (put_user(opt, (u32 __user *) optval)) + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -802,7 +801,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); len = min(len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) + if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len) err = -EFAULT; break; @@ -816,23 +815,24 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u return err; } -static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, + sockopt_t *sopt) { struct sock *sk = sock->sk; struct bt_security sec; int err = 0; size_t len; + u32 opt; BT_DBG("sk %p", sk); if (level == SOL_RFCOMM) - return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen); + return rfcomm_sock_getsockopt_old(sock, optname, sopt); if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; + len = sopt->optlen; lock_sock(sk); @@ -847,7 +847,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c sec.key_size = 0; len = min(len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) + if (copy_to_iter(&sec, len, &sopt->iter_out) != len) err = -EFAULT; break; @@ -858,8 +858,9 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c break; } - if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), - (u32 __user *) optval)) + opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != + sizeof(opt)) err = -EFAULT; break; @@ -1039,7 +1040,7 @@ static const struct proto_ops rfcomm_sock_ops = { .recvmsg = rfcomm_sock_recvmsg, .shutdown = rfcomm_sock_shutdown, .setsockopt = rfcomm_sock_setsockopt, - .getsockopt = rfcomm_sock_getsockopt, + .getsockopt_iter = rfcomm_sock_getsockopt, .ioctl = rfcomm_sock_ioctl, .gettstamp = sock_gettstamp, .poll = bt_sock_poll, diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 91bf5274262e..4b9a699ec59b 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky Copyright (C) 2002 Marcel Holtmann - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 140869e5b2df..fcc597be5bbd 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -28,6 +25,7 @@ #include #include #include +#include #include #include @@ -1089,7 +1087,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, } static int sco_sock_getsockopt_old(struct socket *sock, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct sco_options opts; @@ -1099,8 +1097,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, BT_DBG("sk %p", sk); - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; lock_sock(sk); @@ -1118,7 +1115,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, BT_DBG("mtu %u", opts.mtu); len = min(len, sizeof(opts)); - if (copy_to_user(optval, (char *)&opts, len)) + if (copy_to_iter(&opts, len, &opt->iter_out) != len) err = -EFAULT; break; @@ -1136,7 +1133,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); len = min(len, sizeof(cinfo)); - if (copy_to_user(optval, (char *)&cinfo, len)) + if (copy_to_iter(&cinfo, len, &opt->iter_out) != len) err = -EFAULT; break; @@ -1151,15 +1148,15 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, } static int sco_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; - int len, err = 0; + int len, val, err = 0; struct bt_voice voice; u32 phys; int buf_len; struct codec_list *c; - u8 num_codecs, i, __user *ptr; + u8 num_codecs, i; struct hci_dev *hdev; struct hci_codec_caps *caps; struct bt_codec codec; @@ -1167,10 +1164,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, BT_DBG("sk %p", sk); if (level == SOL_SCO) - return sco_sock_getsockopt_old(sock, optname, optval, optlen); + return sco_sock_getsockopt_old(sock, optname, opt); - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; lock_sock(sk); @@ -1182,8 +1178,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), - (u32 __user *)optval)) + val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + if (copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val)) err = -EFAULT; break; @@ -1192,7 +1189,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, voice.setting = sco_pi(sk)->setting; len = min_t(unsigned int, len, sizeof(voice)); - if (copy_to_user(optval, (char *)&voice, len)) + if (copy_to_iter(&voice, len, &opt->iter_out) != len) err = -EFAULT; break; @@ -1205,13 +1202,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, phys = hci_conn_get_phy(sco_pi(sk)->conn->hcon); - if (put_user(phys, (u32 __user *) optval)) + if (copy_to_iter(&phys, sizeof(phys), &opt->iter_out) != + sizeof(phys)) err = -EFAULT; break; case BT_PKT_STATUS: - if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags), - (int __user *)optval)) + val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags); + if (copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val)) err = -EFAULT; break; @@ -1222,7 +1221,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, break; } - if (put_user(sco_pi(sk)->conn->mtu, (u32 __user *)optval)) + val = sco_pi(sk)->conn->mtu; + if (copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val)) err = -EFAULT; break; @@ -1269,13 +1270,12 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, hci_dev_put(hdev); return -ENOBUFS; } - ptr = optval; - if (put_user(num_codecs, ptr)) { + if (copy_to_iter(&num_codecs, sizeof(num_codecs), + &opt->iter_out) != sizeof(num_codecs)) { hci_dev_put(hdev); return -EFAULT; } - ptr += sizeof(num_codecs); /* Iterate all the codecs supported over SCO and populate * codec data @@ -1292,11 +1292,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, if (err < 0) break; codec.num_caps = c->num_caps; - if (copy_to_user(ptr, &codec, sizeof(codec))) { + if (copy_to_iter(&codec, sizeof(codec), &opt->iter_out) + != sizeof(codec)) { err = -EFAULT; break; } - ptr += sizeof(codec); /* find codec capabilities data length */ len = 0; @@ -1306,11 +1306,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, } /* copy codec capabilities data */ - if (len && copy_to_user(ptr, c->caps, len)) { + if (len && + copy_to_iter(c->caps, len, &opt->iter_out) != len) { err = -EFAULT; break; } - ptr += len; } hci_dev_unlock(hdev); @@ -1318,8 +1318,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, lock_sock(sk); - if (!err && put_user(buf_len, optlen)) - err = -EFAULT; + if (!err) + opt->optlen = buf_len; break; @@ -1594,7 +1594,7 @@ static const struct proto_ops sco_sock_ops = { .socketpair = sock_no_socketpair, .shutdown = sco_sock_shutdown, .setsockopt = sco_sock_setsockopt, - .getsockopt = sco_sock_getsockopt + .getsockopt_iter = sco_sock_getsockopt }; static const struct net_proto_family sco_sock_family_ops = { diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index f49604d44b87..ae5b44bb9d3d 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2014 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/selftest.h b/net/bluetooth/selftest.h index 2aa0a346a913..34d684ee84fd 100644 --- a/net/bluetooth/selftest.h +++ b/net/bluetooth/selftest.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2014 Intel Corporation - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 98f1da4f5f55..031d3022cb1e 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. @@ -22,8 +19,8 @@ #include #include +#include #include -#include #include #include @@ -63,7 +60,7 @@ 0x3f : 0x07) #define KEY_DIST_MASK 0x07 -/* Maximum message length that can be passed to aes_cmac */ +/* Maximum message length that can be passed to smp_aes_cmac */ #define CMAC_MSG_MAX 80 enum { @@ -89,7 +86,6 @@ struct smp_dev { u8 local_rand[16]; bool debug_key; - struct crypto_shash *tfm_cmac; struct crypto_kpp *tfm_ecdh; }; @@ -127,7 +123,6 @@ struct smp_chan { u8 dhkey[32]; u8 mackey[16]; - struct crypto_shash *tfm_cmac; struct crypto_kpp *tfm_ecdh; }; @@ -166,20 +161,15 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len) * AES-CMAC, f4, f5, f6, g2 and h6. */ -static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m, - size_t len, u8 mac[16]) +static int smp_aes_cmac(const u8 k[16], const u8 *m, size_t len, u8 mac[16]) { uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX]; + struct aes_cmac_key key; int err; if (len > CMAC_MSG_MAX) return -EFBIG; - if (!tfm) { - BT_ERR("tfm %p", tfm); - return -EINVAL; - } - /* Swap key and message from LSB to MSB */ swap_buf(k, tmp, 16); swap_buf(m, msg_msb, len); @@ -187,17 +177,10 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m, SMP_DBG("msg (len %zu) %*phN", len, (int) len, m); SMP_DBG("key %16phN", k); - err = crypto_shash_setkey(tfm, tmp, 16); - if (err) { - BT_ERR("cipher setkey failed: %d", err); + err = aes_cmac_preparekey(&key, tmp, 16); + if (WARN_ON_ONCE(err)) /* Should never happen, as 16 is valid keylen */ return err; - } - - err = crypto_shash_tfm_digest(tfm, msg_msb, len, mac_msb); - if (err) { - BT_ERR("Hash computation error %d", err); - return err; - } + aes_cmac(&key, msg_msb, len, mac_msb); swap_buf(mac_msb, mac, 16); @@ -206,8 +189,8 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m, return 0; } -static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32], - const u8 v[32], const u8 x[16], u8 z, u8 res[16]) +static int smp_f4(const u8 u[32], const u8 v[32], const u8 x[16], u8 z, + u8 res[16]) { u8 m[65]; int err; @@ -220,7 +203,7 @@ static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32], memcpy(m + 1, v, 32); memcpy(m + 33, u, 32); - err = aes_cmac(tfm_cmac, x, m, sizeof(m), res); + err = smp_aes_cmac(x, m, sizeof(m), res); if (err) return err; @@ -229,9 +212,8 @@ static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32], return err; } -static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32], - const u8 n1[16], const u8 n2[16], const u8 a1[7], - const u8 a2[7], u8 mackey[16], u8 ltk[16]) +static int smp_f5(const u8 w[32], const u8 n1[16], const u8 n2[16], + const u8 a1[7], const u8 a2[7], u8 mackey[16], u8 ltk[16]) { /* The btle, salt and length "magic" values are as defined in * the SMP section of the Bluetooth core specification. In ASCII @@ -250,7 +232,7 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32], SMP_DBG("n1 %16phN n2 %16phN", n1, n2); SMP_DBG("a1 %7phN a2 %7phN", a1, a2); - err = aes_cmac(tfm_cmac, salt, w, 32, t); + err = smp_aes_cmac(salt, w, 32, t); if (err) return err; @@ -265,7 +247,7 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32], m[52] = 0; /* Counter */ - err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey); + err = smp_aes_cmac(t, m, sizeof(m), mackey); if (err) return err; @@ -273,7 +255,7 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32], m[52] = 1; /* Counter */ - err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk); + err = smp_aes_cmac(t, m, sizeof(m), ltk); if (err) return err; @@ -282,10 +264,9 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32], return 0; } -static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16], - const u8 n1[16], const u8 n2[16], const u8 r[16], - const u8 io_cap[3], const u8 a1[7], const u8 a2[7], - u8 res[16]) +static int smp_f6(const u8 w[16], const u8 n1[16], const u8 n2[16], + const u8 r[16], const u8 io_cap[3], const u8 a1[7], + const u8 a2[7], u8 res[16]) { u8 m[65]; int err; @@ -301,7 +282,7 @@ static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16], memcpy(m + 33, n2, 16); memcpy(m + 49, n1, 16); - err = aes_cmac(tfm_cmac, w, m, sizeof(m), res); + err = smp_aes_cmac(w, m, sizeof(m), res); if (err) return err; @@ -310,8 +291,8 @@ static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16], return err; } -static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32], - const u8 x[16], const u8 y[16], u32 *val) +static int smp_g2(const u8 u[32], const u8 v[32], const u8 x[16], + const u8 y[16], u32 *val) { u8 m[80], tmp[16]; int err; @@ -324,7 +305,7 @@ static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32], memcpy(m + 16, v, 32); memcpy(m + 48, u, 32); - err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp); + err = smp_aes_cmac(x, m, sizeof(m), tmp); if (err) return err; @@ -336,14 +317,13 @@ static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32], return 0; } -static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16], - const u8 key_id[4], u8 res[16]) +static int smp_h6(const u8 w[16], const u8 key_id[4], u8 res[16]) { int err; SMP_DBG("w %16phN key_id %4phN", w, key_id); - err = aes_cmac(tfm_cmac, w, key_id, 4, res); + err = smp_aes_cmac(w, key_id, 4, res); if (err) return err; @@ -352,14 +332,13 @@ static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16], return err; } -static int smp_h7(struct crypto_shash *tfm_cmac, const u8 w[16], - const u8 salt[16], u8 res[16]) +static int smp_h7(const u8 w[16], const u8 salt[16], u8 res[16]) { int err; SMP_DBG("w %16phN salt %16phN", w, salt); - err = aes_cmac(tfm_cmac, salt, w, 16, res); + err = smp_aes_cmac(salt, w, 16, res); if (err) return err; @@ -574,8 +553,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) get_random_bytes(smp->local_rand, 16); - err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->local_pk, - smp->local_rand, 0, hash); + err = smp_f4(smp->local_pk, smp->local_pk, smp->local_rand, 0, hash); if (err < 0) return err; @@ -757,7 +735,6 @@ static void smp_chan_destroy(struct l2cap_conn *conn) kfree_sensitive(smp->responder_csrk); kfree_sensitive(smp->link_key); - crypto_free_shash(smp->tfm_cmac); crypto_free_kpp(smp->tfm_ecdh); /* Ensure that we don't leave any debug key around if debug key @@ -1160,7 +1137,7 @@ static void sc_generate_link_key(struct smp_chan *smp) /* SALT = 0x000000000000000000000000746D7031 */ const u8 salt[16] = { 0x31, 0x70, 0x6d, 0x74 }; - if (smp_h7(smp->tfm_cmac, smp->tk, salt, smp->link_key)) { + if (smp_h7(smp->tk, salt, smp->link_key)) { kfree_sensitive(smp->link_key); smp->link_key = NULL; return; @@ -1169,14 +1146,14 @@ static void sc_generate_link_key(struct smp_chan *smp) /* From core spec. Spells out in ASCII as 'tmp1'. */ const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 }; - if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { + if (smp_h6(smp->tk, tmp1, smp->link_key)) { kfree_sensitive(smp->link_key); smp->link_key = NULL; return; } } - if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) { + if (smp_h6(smp->link_key, lebr, smp->link_key)) { kfree_sensitive(smp->link_key); smp->link_key = NULL; return; @@ -1218,17 +1195,17 @@ static void sc_generate_ltk(struct smp_chan *smp) /* SALT = 0x000000000000000000000000746D7032 */ const u8 salt[16] = { 0x32, 0x70, 0x6d, 0x74 }; - if (smp_h7(smp->tfm_cmac, key->val, salt, smp->tk)) + if (smp_h7(key->val, salt, smp->tk)) return; } else { /* From core spec. Spells out in ASCII as 'tmp2'. */ const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 }; - if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk)) + if (smp_h6(key->val, tmp2, smp->tk)) return; } - if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk)) + if (smp_h6(smp->tk, brle, smp->tk)) return; sc_add_ltk(smp); @@ -1389,16 +1366,10 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) if (!smp) return NULL; - smp->tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0); - if (IS_ERR(smp->tfm_cmac)) { - bt_dev_err(hcon->hdev, "Unable to create CMAC crypto context"); - goto zfree_smp; - } - smp->tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0); if (IS_ERR(smp->tfm_ecdh)) { bt_dev_err(hcon->hdev, "Unable to create ECDH crypto context"); - goto free_shash; + goto zfree_smp; } smp->conn = conn; @@ -1412,8 +1383,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) return smp; -free_shash: - crypto_free_shash(smp->tfm_cmac); zfree_smp: kfree_sensitive(smp); return NULL; @@ -1437,7 +1406,7 @@ static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16]) a[6] = hcon->init_addr_type; b[6] = hcon->resp_addr_type; - return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk); + return smp_f5(smp->dhkey, na, nb, a, b, mackey, ltk); } static void sc_dhkey_check(struct smp_chan *smp) @@ -1470,8 +1439,8 @@ static void sc_dhkey_check(struct smp_chan *smp) if (smp->method == REQ_OOB) memcpy(r, smp->rr, 16); - smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap, - local_addr, remote_addr, check.e); + smp_f6(smp->mackey, smp->prnd, smp->rrnd, r, io_cap, local_addr, + remote_addr, check.e); smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check); } @@ -1488,7 +1457,7 @@ static u8 sc_passkey_send_confirm(struct smp_chan *smp) get_random_bytes(smp->prnd, sizeof(smp->prnd)); - if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r, + if (smp_f4(smp->local_pk, smp->remote_pk, smp->prnd, r, cfm.confirm_val)) return SMP_UNSPECIFIED; @@ -1513,8 +1482,7 @@ static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op) r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01); r |= 0x80; - if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk, - smp->rrnd, r, cfm)) + if (smp_f4(smp->remote_pk, smp->local_pk, smp->rrnd, r, cfm)) return SMP_UNSPECIFIED; if (crypto_memneq(smp->pcnf, cfm, 16)) @@ -2178,8 +2146,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) if (test_bit(SMP_FLAG_INITIATOR, &smp->flags)) { u8 cfm[16]; - err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk, - smp->rrnd, 0, cfm); + err = smp_f4(smp->remote_pk, smp->local_pk, smp->rrnd, 0, cfm); if (err) return SMP_UNSPECIFIED; @@ -2205,7 +2172,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } - err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey); + err = smp_g2(pkax, pkbx, na, nb, &passkey); if (err) return SMP_UNSPECIFIED; @@ -2750,8 +2717,8 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(smp->remote_pk, key, 64); if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) { - err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk, - smp->rr, 0, cfm.confirm_val); + err = smp_f4(smp->remote_pk, smp->remote_pk, smp->rr, 0, + cfm.confirm_val); if (err) return SMP_UNSPECIFIED; @@ -2851,8 +2818,8 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) if (test_bit(SMP_FLAG_INITIATOR, &smp->flags)) return 0; - err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, - 0, cfm.confirm_val); + err = smp_f4(smp->local_pk, smp->remote_pk, smp->prnd, 0, + cfm.confirm_val); if (err) return SMP_UNSPECIFIED; @@ -2899,8 +2866,8 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb) else if (smp->method == REQ_OOB) memcpy(r, smp->lr, 16); - err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r, - io_cap, remote_addr, local_addr, e); + err = smp_f6(smp->mackey, smp->rrnd, smp->prnd, r, io_cap, remote_addr, + local_addr, e); if (err) return SMP_UNSPECIFIED; @@ -3286,7 +3253,6 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) { struct l2cap_chan *chan; struct smp_dev *smp; - struct crypto_shash *tfm_cmac; struct crypto_kpp *tfm_ecdh; if (cid == L2CAP_CID_SMP_BREDR) { @@ -3298,30 +3264,20 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) if (!smp) return ERR_PTR(-ENOMEM); - tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0); - if (IS_ERR(tfm_cmac)) { - bt_dev_err(hdev, "Unable to create CMAC crypto context"); - kfree_sensitive(smp); - return ERR_CAST(tfm_cmac); - } - tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0); if (IS_ERR(tfm_ecdh)) { bt_dev_err(hdev, "Unable to create ECDH crypto context"); - crypto_free_shash(tfm_cmac); kfree_sensitive(smp); return ERR_CAST(tfm_ecdh); } smp->local_oob = false; - smp->tfm_cmac = tfm_cmac; smp->tfm_ecdh = tfm_ecdh; create_chan: chan = l2cap_chan_create(); if (!chan) { if (smp) { - crypto_free_shash(smp->tfm_cmac); crypto_free_kpp(smp->tfm_ecdh); kfree_sensitive(smp); } @@ -3368,7 +3324,6 @@ static void smp_del_chan(struct l2cap_chan *chan) smp = chan->data; if (smp) { chan->data = NULL; - crypto_free_shash(smp->tfm_cmac); crypto_free_kpp(smp->tfm_ecdh); kfree_sensitive(smp); } @@ -3565,7 +3520,7 @@ static int __init test_s1(void) return 0; } -static int __init test_f4(struct crypto_shash *tfm_cmac) +static int __init test_f4(void) { const u8 u[32] = { 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, @@ -3587,7 +3542,7 @@ static int __init test_f4(struct crypto_shash *tfm_cmac) u8 res[16]; int err; - err = smp_f4(tfm_cmac, u, v, x, z, res); + err = smp_f4(u, v, x, z, res); if (err) return err; @@ -3597,7 +3552,7 @@ static int __init test_f4(struct crypto_shash *tfm_cmac) return 0; } -static int __init test_f5(struct crypto_shash *tfm_cmac) +static int __init test_f5(void) { const u8 w[32] = { 0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86, @@ -3621,7 +3576,7 @@ static int __init test_f5(struct crypto_shash *tfm_cmac) u8 mackey[16], ltk[16]; int err; - err = smp_f5(tfm_cmac, w, n1, n2, a1, a2, mackey, ltk); + err = smp_f5(w, n1, n2, a1, a2, mackey, ltk); if (err) return err; @@ -3634,7 +3589,7 @@ static int __init test_f5(struct crypto_shash *tfm_cmac) return 0; } -static int __init test_f6(struct crypto_shash *tfm_cmac) +static int __init test_f6(void) { const u8 w[16] = { 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd, @@ -3657,7 +3612,7 @@ static int __init test_f6(struct crypto_shash *tfm_cmac) u8 res[16]; int err; - err = smp_f6(tfm_cmac, w, n1, n2, r, io_cap, a1, a2, res); + err = smp_f6(w, n1, n2, r, io_cap, a1, a2, res); if (err) return err; @@ -3667,7 +3622,7 @@ static int __init test_f6(struct crypto_shash *tfm_cmac) return 0; } -static int __init test_g2(struct crypto_shash *tfm_cmac) +static int __init test_g2(void) { const u8 u[32] = { 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, @@ -3689,7 +3644,7 @@ static int __init test_g2(struct crypto_shash *tfm_cmac) u32 val; int err; - err = smp_g2(tfm_cmac, u, v, x, y, &val); + err = smp_g2(u, v, x, y, &val); if (err) return err; @@ -3699,7 +3654,7 @@ static int __init test_g2(struct crypto_shash *tfm_cmac) return 0; } -static int __init test_h6(struct crypto_shash *tfm_cmac) +static int __init test_h6(void) { const u8 w[16] = { 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, @@ -3711,7 +3666,7 @@ static int __init test_h6(struct crypto_shash *tfm_cmac) u8 res[16]; int err; - err = smp_h6(tfm_cmac, w, key_id, res); + err = smp_h6(w, key_id, res); if (err) return err; @@ -3736,8 +3691,7 @@ static const struct file_operations test_smp_fops = { .llseek = default_llseek, }; -static int __init run_selftests(struct crypto_shash *tfm_cmac, - struct crypto_kpp *tfm_ecdh) +static int __init run_selftests(struct crypto_kpp *tfm_ecdh) { ktime_t calltime, delta, rettime; unsigned long long duration; @@ -3769,31 +3723,31 @@ static int __init run_selftests(struct crypto_shash *tfm_cmac, goto done; } - err = test_f4(tfm_cmac); + err = test_f4(); if (err) { BT_ERR("smp_f4 test failed"); goto done; } - err = test_f5(tfm_cmac); + err = test_f5(); if (err) { BT_ERR("smp_f5 test failed"); goto done; } - err = test_f6(tfm_cmac); + err = test_f6(); if (err) { BT_ERR("smp_f6 test failed"); goto done; } - err = test_g2(tfm_cmac); + err = test_g2(); if (err) { BT_ERR("smp_g2 test failed"); goto done; } - err = test_h6(tfm_cmac); + err = test_h6(); if (err) { BT_ERR("smp_h6 test failed"); goto done; @@ -3820,26 +3774,17 @@ static int __init run_selftests(struct crypto_shash *tfm_cmac, int __init bt_selftest_smp(void) { - struct crypto_shash *tfm_cmac; struct crypto_kpp *tfm_ecdh; int err; - tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0); - if (IS_ERR(tfm_cmac)) { - BT_ERR("Unable to create CMAC crypto context"); - return PTR_ERR(tfm_cmac); - } - tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0); if (IS_ERR(tfm_ecdh)) { BT_ERR("Unable to create ECDH crypto context"); - crypto_free_shash(tfm_cmac); return PTR_ERR(tfm_ecdh); } - err = run_selftests(tfm_cmac, tfm_ecdh); + err = run_selftests(tfm_ecdh); - crypto_free_shash(tfm_cmac); crypto_free_kpp(tfm_ecdh); return err; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index c5da53dfab04..eac27bd541bb 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. diff --git a/net/bridge/Makefile b/net/bridge/Makefile index 24bd1c0a9a5a..1203dc19e15c 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -29,3 +29,9 @@ obj-$(CONFIG_NETFILTER) += netfilter/ bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o br_cfm_netlink.o + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE_br_nf_core.o := y +GCOV_PROFILE_br_netfilter_hooks.o := y +GCOV_PROFILE_br_netfilter_ipv6.o := y +endif diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index deb1ab1f24b0..23eb6931a2b4 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -29,7 +29,7 @@ void br_recalculate_neigh_suppress_enabled(struct net_bridge *br) bool neigh_suppress = false; list_for_each_entry(p, &br->port_list, list) { - if (p->flags & (BR_NEIGH_SUPPRESS | BR_NEIGH_VLAN_SUPPRESS)) { + if (READ_ONCE(p->flags) & (BR_NEIGH_SUPPRESS | BR_NEIGH_VLAN_SUPPRESS)) { neigh_suppress = true; break; } @@ -132,6 +132,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, __be32 sip, tip; BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0; + BR_INPUT_SKB_CB(skb)->grat_arp = 0; if ((dev->flags & IFF_NOARP) || !pskb_may_pull(skb, arp_hdr_len(dev))) @@ -164,9 +165,10 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, return; if (parp->ar_op != htons(ARPOP_RREQUEST) && parp->ar_op != htons(ARPOP_RREPLY) && - (ipv4_is_zeronet(sip) || sip == tip)) { + sip == tip) { /* prevent flooding to neigh suppress ports */ BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; + BR_INPUT_SKB_CB(skb)->grat_arp = 1; return; } } @@ -204,8 +206,8 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, const struct net_bridge_port *dst = READ_ONCE(f->dst); bool replied = false; - if ((p && (p->flags & BR_PROXYARP)) || - (dst && (dst->flags & BR_PROXYARP_WIFI)) || + if ((p && test_bit(BR_PROXYARP_BIT, &p->flags)) || + (dst && test_bit(BR_PROXYARP_WIFI_BIT, &dst->flags)) || br_is_neigh_suppress_enabled(dst, vid)) { if (!vid) br_arp_send(br, p, skb->dev, sip, tip, @@ -262,6 +264,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, int ns_olen; int i, len; u8 *daddr; + bool dad; u16 pvid; if (!dev || skb_linearize(request)) @@ -300,8 +303,13 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, } } + dad = ipv6_addr_any(&ipv6_hdr(request)->saddr); + /* Ethernet header */ - ether_addr_copy(eth_hdr(reply)->h_dest, daddr); + if (dad) + ipv6_eth_mc_map(&in6addr_linklocal_allnodes, eth_hdr(reply)->h_dest); + else + ether_addr_copy(eth_hdr(reply)->h_dest, daddr); ether_addr_copy(eth_hdr(reply)->h_source, n->ha); eth_hdr(reply)->h_proto = htons(ETH_P_IPV6); reply->protocol = htons(ETH_P_IPV6); @@ -317,7 +325,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, pip6->priority = ipv6_hdr(request)->priority; pip6->nexthdr = IPPROTO_ICMPV6; pip6->hop_limit = 255; - pip6->daddr = ipv6_hdr(request)->saddr; + pip6->daddr = dad ? in6addr_linklocal_allnodes : ipv6_hdr(request)->saddr; pip6->saddr = *(struct in6_addr *)n->primary_key; skb_pull(reply, sizeof(struct ipv6hdr)); @@ -330,7 +338,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; na->icmph.icmp6_router = (n->flags & NTF_ROUTER) ? 1 : 0; na->icmph.icmp6_override = 1; - na->icmph.icmp6_solicited = 1; + na->icmph.icmp6_solicited = dad ? 0 : 1; na->target = ns->target; ether_addr_copy(&na->opt[2], n->ha); na->opt[0] = ND_OPT_TARGET_LL_ADDR; @@ -413,6 +421,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, struct neighbour *n; BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0; + BR_INPUT_SKB_CB(skb)->grat_arp = 0; if (br_is_neigh_suppress_enabled(p, vid)) return; @@ -425,6 +434,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, !msg->icmph.icmp6_solicited) { /* prevent flooding to neigh suppress ports */ BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; + BR_INPUT_SKB_CB(skb)->grat_arp = 1; return; } @@ -435,7 +445,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, saddr = &iphdr->saddr; daddr = &iphdr->daddr; - if (ipv6_addr_any(saddr) || !ipv6_addr_cmp(saddr, daddr)) { + if (!ipv6_addr_cmp(saddr, daddr)) { /* prevent flooding to neigh suppress ports */ BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; return; @@ -501,10 +511,7 @@ bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid) if (!p) return false; - if (!vid) - return !!(p->flags & BR_NEIGH_SUPPRESS); - - if (p->flags & BR_NEIGH_VLAN_SUPPRESS) { + if (vid && test_bit(BR_NEIGH_VLAN_SUPPRESS_BIT, &p->flags)) { struct net_bridge_vlan_group *vg = nbp_vlan_group_rcu(p); struct net_bridge_vlan *v; @@ -512,7 +519,20 @@ bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid) if (!v) return false; return !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED); - } else { - return !!(p->flags & BR_NEIGH_SUPPRESS); } + return test_bit(BR_NEIGH_SUPPRESS_BIT, &p->flags); +} + +bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid) +{ + if (vid && test_bit(BR_NEIGH_VLAN_SUPPRESS_BIT, &p->flags)) { + struct net_bridge_vlan_group *vg = nbp_vlan_group_rcu(p); + struct net_bridge_vlan *v; + + v = br_vlan_find(vg, vid); + if (!v) + return false; + return !!(v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED); + } + return test_bit(BR_NEIGH_FORWARD_GRAT_BIT, &p->flags); } diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c index 118c7ea48c35..dea56fffa1c1 100644 --- a/net/bridge/br_cfm.c +++ b/net/bridge/br_cfm.c @@ -805,6 +805,12 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance, goto save; } + if (!interval_to_us(mep->cc_config.exp_interval)) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid CCM interval"); + return -EINVAL; + } + /* Start delayed work to transmit CCM frames. It is done with zero delay * to send first frame immediately */ diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c index 2faab44652e7..91b9922dc3f2 100644 --- a/net/bridge/br_cfm_netlink.c +++ b/net/bridge/br_cfm_netlink.c @@ -34,7 +34,9 @@ br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = { [IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 }, [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 }, - [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = + NLA_POLICY_RANGE(NLA_U32, BR_CFM_CCM_INTERVAL_3_3_MS, + BR_CFM_CCM_INTERVAL_10_MIN), [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = { .type = NLA_BINARY, .len = CFM_MAID_LENGTH }, }; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index a35ceae0a6f2..e7f343ab22d3 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -107,7 +107,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) else br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid); } else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) { - br_forward(dst->dst, skb, false, true); + br_forward(READ_ONCE(dst->dst), skb, false, true); } else { br_flood(br, skb, BR_PKT_UNICAST, false, true, vid); } diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index ac81e58d5f70..e4570bbed854 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -470,7 +470,8 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) spin_lock_bh(&br->hash_lock); vg = nbp_vlan_group(p); hlist_for_each_entry(f, &br->fdb_list, fdb_node) { - if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) && + if (READ_ONCE(f->dst) == p && + test_bit(BR_FDB_LOCAL, &f->flags) && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) { /* delete old one */ fdb_delete_local(br, p, f); @@ -878,7 +879,7 @@ void br_fdb_delete_by_port(struct net_bridge *br, spin_lock_bh(&br->hash_lock); hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) { - if (f->dst != p) + if (READ_ONCE(f->dst) != p) continue; if (!do_all) @@ -1513,7 +1514,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, trace_br_fdb_external_learn_add(br, p, addr, vid); - if (locked && (!p || !(p->flags & BR_PORT_MAB))) + if (locked && (!p || !test_bit(BR_PORT_MAB_BIT, &p->flags))) return -EINVAL; spin_lock_bh(&br->hash_lock); @@ -1631,7 +1632,7 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid) spin_lock_bh(&p->br->hash_lock); hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) { - if (f->dst == p && f->key.vlan_id == vid) + if (READ_ONCE(f->dst) == p && f->key.vlan_id == vid) clear_bit(BR_FDB_OFFLOADED, &f->flags); } spin_unlock_bh(&p->br->hash_lock); diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index dea09096ad0f..46c762ca5177 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -24,7 +24,7 @@ static inline int should_deliver(const struct net_bridge_port *p, struct net_bridge_vlan_group *vg; vg = nbp_vlan_group_rcu(p); - return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && + return (test_bit(BR_HAIRPIN_MODE_BIT, &p->flags) || skb->dev != p->dev) && (br_mst_is_enabled(p) || p->state == BR_STATE_FORWARDING) && br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p, skb) && !br_skb_isolated(p, skb); @@ -214,26 +214,33 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb, */ switch (pkt_type) { case BR_PKT_UNICAST: - if (!(p->flags & BR_FLOOD)) + if (!test_bit(BR_FLOOD_BIT, &p->flags)) continue; break; case BR_PKT_MULTICAST: - if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) + if (!test_bit(BR_MCAST_FLOOD_BIT, &p->flags) && skb->dev != br->dev) continue; break; case BR_PKT_BROADCAST: - if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) + if (!test_bit(BR_BCAST_FLOOD_BIT, &p->flags) && skb->dev != br->dev) continue; break; } /* Do not flood to ports that enable proxy ARP */ - if (p->flags & BR_PROXYARP) - continue; - if (BR_INPUT_SKB_CB(skb)->proxyarp_replied && - ((p->flags & BR_PROXYARP_WIFI) || - br_is_neigh_suppress_enabled(p, vid))) + if (test_bit(BR_PROXYARP_BIT, &p->flags)) continue; + if (BR_INPUT_SKB_CB(skb)->proxyarp_replied) { + if (test_bit(BR_PROXYARP_WIFI_BIT, &p->flags)) + continue; + /* For gratuitous ARPs/NAs, check neigh_forward_grat. + * For regular ARPs/NDs, check only neigh_suppress. + */ + if (br_is_neigh_suppress_enabled(p, vid) && + (!BR_INPUT_SKB_CB(skb)->grat_arp || + !br_is_neigh_forward_grat_enabled(p, vid))) + continue; + } prev = maybe_deliver(prev, p, skb, local_orig); if (IS_ERR(prev)) { @@ -321,7 +328,8 @@ void br_multicast_flood(struct net_bridge_mdb_entry *mdst, if ((unsigned long)lport > (unsigned long)rport) { port = lport; - if (port->flags & BR_MULTICAST_TO_UNICAST) { + if (test_bit(BR_MULTICAST_TO_UNICAST_BIT, + &port->flags)) { maybe_deliver_addr(lport, skb, p->eth_addr, local_orig); goto delivered; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index d39571e13744..7ed19aa8ae59 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -30,13 +31,13 @@ * Determine initial path cost based on speed. * using recommendations from 802.1d standard * - * Since driver might sleep need to not be holding any locks. + * Since driver might sleep, we need to not be holding any bridge spinlocks. */ static int port_cost(struct net_device *dev) { struct ethtool_link_ksettings ecmd; - if (!__ethtool_get_link_ksettings(dev, &ecmd)) { + if (!netif_get_link_ksettings(dev, &ecmd)) { switch (ecmd.base.speed) { case SPEED_10000: return 2; @@ -75,9 +76,9 @@ void br_port_carrier_check(struct net_bridge_port *p, bool *notified) struct net_device *dev = p->dev; struct net_bridge *br = p->br; - if (!(p->flags & BR_ADMIN_COST) && + if (!test_bit(BR_ADMIN_COST_BIT, &p->flags) && netif_running(dev) && netif_oper_up(dev)) - p->path_cost = port_cost(dev); + WRITE_ONCE(p->path_cost, port_cost(dev)); *notified = false; if (!netif_running(br->dev)) @@ -110,7 +111,7 @@ static void br_port_set_promisc(struct net_bridge_port *p) return; br_fdb_unsync_static(p->br, p); - p->flags |= BR_PROMISC; + set_bit(BR_PROMISC_BIT, &p->flags); } static void br_port_clear_promisc(struct net_bridge_port *p) @@ -133,7 +134,7 @@ static void br_port_clear_promisc(struct net_bridge_port *p) return; dev_set_promiscuity(p->dev, -1); - p->flags &= ~BR_PROMISC; + clear_bit(BR_PROMISC_BIT, &p->flags); } /* When a port is added or removed or when certain port flags @@ -436,7 +437,9 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->br = br; netdev_hold(dev, &p->dev_tracker, GFP_KERNEL); p->dev = dev; + netdev_lock_ops(dev); p->path_cost = port_cost(dev); + netdev_unlock_ops(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; @@ -758,6 +761,6 @@ bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag) if (!p) return false; - return p->flags & flag; + return READ_ONCE(p->flags) & flag; } EXPORT_SYMBOL_GPL(br_port_flag_is_set); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 2cbae0f9ae1f..ddb8f002a40e 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -111,7 +111,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb &state, &vlan)) goto out; - if (p->flags & BR_PORT_LOCKED) { + if (test_bit(BR_PORT_LOCKED_BIT, &p->flags)) { struct net_bridge_fdb_entry *fdb_src = br_fdb_find_rcu(br, eth_hdr(skb)->h_source, vid); @@ -119,7 +119,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb /* FDB miss. Create locked FDB entry if MAB is enabled * and drop the packet. */ - if (p->flags & BR_PORT_MAB) + if (test_bit(BR_PORT_MAB_BIT, &p->flags)) br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, BIT(BR_FDB_LOCKED)); goto drop; @@ -140,7 +140,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb nbp_switchdev_frame_mark(p, skb); /* insert into forwarding database after filtering to avoid spoofing */ - if (p->flags & BR_LEARNING) + if (test_bit(BR_LEARNING_BIT, &p->flags)) br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0); promisc = !!(br->dev->flags & IFF_PROMISC); @@ -164,7 +164,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb } BR_INPUT_SKB_CB(skb)->brdev = br->dev; - BR_INPUT_SKB_CB(skb)->src_port_isolated = !!(p->flags & BR_ISOLATED); + BR_INPUT_SKB_CB(skb)->src_port_isolated = test_bit(BR_ISOLATED_BIT, &p->flags); if (IS_ENABLED(CONFIG_INET) && (skb->protocol == htons(ETH_P_ARP) || @@ -223,7 +223,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb if (now != READ_ONCE(dst->used)) WRITE_ONCE(dst->used, now); - br_forward(dst->dst, skb, local_rcv, false); + br_forward(READ_ONCE(dst->dst), skb, local_rcv, false); } else { if (!mcast_hit) br_flood(br, skb, pkt_type, local_rcv, false, vid); @@ -248,7 +248,7 @@ static void __br_handle_local_finish(struct sk_buff *skb) u16 vid = 0; /* check if vlan is allowed, to avoid spoofing */ - if ((p->flags & BR_LEARNING) && + if (test_bit(BR_LEARNING_BIT, &p->flags) && nbp_state_should_learn(p) && !br_opt_get(p->br, BROPT_NO_LL_LEARN) && br_should_learn(p, skb, &vid)) @@ -359,7 +359,7 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) br_tc_skb_miss_set(skb, false); p = br_port_get_rcu(skb->dev); - if (p->flags & BR_VLAN_TUNNEL) + if (test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p)); if (unlikely(is_link_local_ether_addr(dest))) { diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 766c43b327af..39f3ffcfa2d3 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -257,13 +257,13 @@ int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq, memset(&p, 0, sizeof(struct __port_info)); memcpy(&p.designated_root, &pt->designated_root, 8); memcpy(&p.designated_bridge, &pt->designated_bridge, 8); - p.port_id = pt->port_id; - p.designated_port = pt->designated_port; - p.path_cost = pt->path_cost; - p.designated_cost = pt->designated_cost; + p.port_id = READ_ONCE(pt->port_id); + p.designated_port = READ_ONCE(pt->designated_port); + p.path_cost = READ_ONCE(pt->path_cost); + p.designated_cost = READ_ONCE(pt->designated_cost); p.state = pt->state; p.top_change_ack = pt->topology_change_ack; - p.config_pending = pt->config_pending; + p.config_pending = READ_ONCE(pt->config_pending); p.message_age_timer_value = br_timer_value(&pt->message_age_timer); p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer); p.hold_timer_value = br_timer_value(&pt->hold_timer); diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index f1aa67f7a051..3f7126a7d720 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -454,7 +454,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp) state = netif_running(br->dev) ? BR_STATE_FORWARDING : BR_STATE_DISABLED; p->state = state; - p->flags &= ~BR_MRP_AWARE; + clear_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); br_mrp_port_switchdev_set_state(p, state); rcu_assign_pointer(mrp->p_port, NULL); @@ -466,7 +466,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp) state = netif_running(br->dev) ? BR_STATE_FORWARDING : BR_STATE_DISABLED; p->state = state; - p->flags &= ~BR_MRP_AWARE; + clear_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); br_mrp_port_switchdev_set_state(p, state); rcu_assign_pointer(mrp->s_port, NULL); @@ -478,7 +478,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp) state = netif_running(br->dev) ? BR_STATE_FORWARDING : BR_STATE_DISABLED; p->state = state; - p->flags &= ~BR_MRP_AWARE; + clear_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); br_mrp_port_switchdev_set_state(p, state); rcu_assign_pointer(mrp->i_port, NULL); @@ -526,14 +526,14 @@ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance) p = br_mrp_get_port(br, instance->p_ifindex); spin_lock_bh(&br->lock); p->state = BR_STATE_FORWARDING; - p->flags |= BR_MRP_AWARE; + set_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); rcu_assign_pointer(mrp->p_port, p); p = br_mrp_get_port(br, instance->s_ifindex); spin_lock_bh(&br->lock); p->state = BR_STATE_FORWARDING; - p->flags |= BR_MRP_AWARE; + set_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); rcu_assign_pointer(mrp->s_port, p); @@ -593,7 +593,7 @@ int br_mrp_set_port_state(struct net_bridge_port *p, { u32 port_state; - if (!p || !(p->flags & BR_MRP_AWARE)) + if (!p || !test_bit(BR_MRP_AWARE_BIT, &p->flags)) return -EINVAL; spin_lock_bh(&p->br->lock); @@ -619,7 +619,7 @@ int br_mrp_set_port_role(struct net_bridge_port *p, { struct br_mrp *mrp; - if (!p || !(p->flags & BR_MRP_AWARE)) + if (!p || !test_bit(BR_MRP_AWARE_BIT, &p->flags)) return -EINVAL; mrp = br_mrp_find_port(p->br, p); @@ -784,7 +784,7 @@ int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role) state = netif_running(br->dev) ? BR_STATE_FORWARDING : BR_STATE_DISABLED; p->state = state; - p->flags &= ~BR_MRP_AWARE; + clear_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); br_mrp_port_switchdev_set_state(p, state); rcu_assign_pointer(mrp->i_port, NULL); @@ -809,7 +809,7 @@ int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role) p = br_mrp_get_port(br, role->i_ifindex); spin_lock_bh(&br->lock); p->state = BR_STATE_FORWARDING; - p->flags |= BR_MRP_AWARE; + set_bit(BR_MRP_AWARE_BIT, &p->flags); spin_unlock_bh(&br->lock); rcu_assign_pointer(mrp->i_port, p); @@ -1246,7 +1246,7 @@ static int br_mrp_rcv(struct net_bridge_port *p, static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) { /* If there is no MRP instance do normal forwarding */ - if (likely(!(p->flags & BR_MRP_AWARE))) + if (likely(!test_bit(BR_MRP_AWARE_BIT, &p->flags))) goto out; return br_mrp_rcv(p, skb, p->dev); diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c index 86f0e75d6e34..39f07e13fba5 100644 --- a/net/bridge/br_mrp_netlink.c +++ b/net/bridge/br_mrp_netlink.c @@ -538,9 +538,9 @@ int br_mrp_ring_port_open(struct net_device *dev, u8 loc) } if (loc) - p->flags |= BR_MRP_LOST_CONT; + set_bit(BR_MRP_LOST_CONT_BIT, &p->flags); else - p->flags &= ~BR_MRP_LOST_CONT; + clear_bit(BR_MRP_LOST_CONT_BIT, &p->flags); br_ifinfo_notify(RTM_NEWLINK, NULL, p); @@ -560,9 +560,9 @@ int br_mrp_in_port_open(struct net_device *dev, u8 loc) } if (loc) - p->flags |= BR_MRP_LOST_IN_CONT; + set_bit(BR_MRP_LOST_IN_CONT_BIT, &p->flags); else - p->flags &= ~BR_MRP_LOST_IN_CONT; + clear_bit(BR_MRP_LOST_IN_CONT_BIT, &p->flags); br_ifinfo_notify(RTM_NEWLINK, NULL, p); diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 2eef4f3345cd..6b3ac473fd22 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -281,7 +281,7 @@ static bool br_port_group_equal(struct net_bridge_port_group *p, if (p->key.port != port) return false; - if (!(port->flags & BR_MULTICAST_TO_UNICAST)) + if (!test_bit(BR_MULTICAST_TO_UNICAST_BIT, &port->flags)) return true; return ether_addr_equal(src, p->eth_addr); @@ -934,12 +934,12 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm size_t pkt_size, igmp_hdr_size; unsigned long now = jiffies; struct igmpv3_query *ihv3; + unsigned long lmqt, mrt; void *csum_start = NULL; __sum16 *csum = NULL; struct sk_buff *skb; struct igmphdr *ih; struct ethhdr *eth; - unsigned long lmqt; struct iphdr *iph; u16 lmqt_srcs = 0; @@ -1004,15 +1004,15 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm skb_put(skb, 24); skb_set_transport_header(skb, skb->len); + mrt = group ? brmctx->multicast_last_member_interval : + brmctx->multicast_query_response_interval; *igmp_type = IGMP_HOST_MEMBERSHIP_QUERY; switch (brmctx->multicast_igmp_version) { case 2: ih = igmp_hdr(skb); ih->type = IGMP_HOST_MEMBERSHIP_QUERY; - ih->code = (group ? brmctx->multicast_last_member_interval : - brmctx->multicast_query_response_interval) / - (HZ / IGMP_TIMER_SCALE); + ih->code = mrt / (HZ / IGMP_TIMER_SCALE); ih->group = group; ih->csum = 0; csum = &ih->csum; @@ -1021,11 +1021,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm case 3: ihv3 = igmpv3_query_hdr(skb); ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY; - ihv3->code = (group ? brmctx->multicast_last_member_interval : - brmctx->multicast_query_response_interval) / - (HZ / IGMP_TIMER_SCALE); + ihv3->code = igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE)); ihv3->group = group; - ihv3->qqic = brmctx->multicast_query_interval / HZ; + ihv3->qqic = igmpv3_qqic(brmctx->multicast_query_interval / HZ); ihv3->nsrcs = htons(lmqt_srcs); ihv3->resv = 0; ihv3->suppress = sflag; @@ -1183,7 +1181,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm break; case 2: mld2q = (struct mld2_query *)icmp6_hdr(skb); - mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval)); + mld2q->mld2q_mrc = htons(mldv2_mrc(jiffies_to_msecs(interval))); mld2q->mld2q_type = ICMPV6_MGM_QUERY; mld2q->mld2q_code = 0; mld2q->mld2q_cksum = 0; @@ -1192,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm mld2q->mld2q_suppress = sflag; mld2q->mld2q_qrv = 2; mld2q->mld2q_nsrcs = htons(llqt_srcs); - mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ; + mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ); mld2q->mld2q_mca = *group; csum = &mld2q->mld2q_cksum; csum_start = (void *)mld2q; @@ -2029,9 +2027,11 @@ void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx) bool del = false; #if IS_ENABLED(CONFIG_IPV6) - timer_delete_sync(&pmctx->ip6_mc_router_timer); + timer_shutdown_sync(&pmctx->ip6_own_query.timer); + timer_shutdown_sync(&pmctx->ip6_mc_router_timer); #endif - timer_delete_sync(&pmctx->ip4_mc_router_timer); + timer_shutdown_sync(&pmctx->ip4_own_query.timer); + timer_shutdown_sync(&pmctx->ip4_mc_router_timer); spin_lock_bh(&br->multicast_lock); del |= br_ip6_multicast_rport_del(pmctx); @@ -3518,7 +3518,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx, goto out; max_delay = ih3->code ? - IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; + igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE) : 1; } else { goto out; } @@ -3606,7 +3606,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx, mld2q->mld2q_suppress) goto out; - max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL); + max_delay = max(msecs_to_jiffies(mldv2_mrd(mld2q)), 1UL); } is_general_query = group && ipv6_addr_any(group); @@ -3672,7 +3672,8 @@ br_multicast_leave_group(struct net_bridge_mcast *brmctx, if (!mp) goto out; - if (pmctx && (pmctx->port->flags & BR_MULTICAST_FAST_LEAVE)) { + if (pmctx && + test_bit(BR_MULTICAST_FAST_LEAVE_BIT, &pmctx->port->flags)) { struct net_bridge_port_group __rcu **pp; for (pp = &mp->ports; @@ -4455,8 +4456,6 @@ void br_multicast_dev_del(struct net_bridge *br) br_multicast_ctx_deinit(&br->multicast_ctx); br_multicast_gc(&deleted_head); cancel_work_sync(&br->mcast_gc_work); - - rcu_barrier(); } int br_multicast_set_router(struct net_bridge_mcast *brmctx, unsigned long val) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index b9591dd755f9..b2cd4e39326d 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -113,7 +113,7 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev, num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask); rcu_read_unlock(); - if (p && (p->flags & BR_VLAN_TUNNEL)) + if (p && test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) vinfo_sz += br_get_vlan_tunnel_info_size(vg); /* Each VLAN is returned in bridge_vlan_info along with flags */ @@ -190,6 +190,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_LOCKED */ + nla_total_size(1) /* IFLA_BRPORT_MAB */ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_VLAN_SUPPRESS */ + + nla_total_size(1) /* IFLA_BRPORT_NEIGH_FORWARD_GRAT */ + 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 */ @@ -233,56 +234,61 @@ static inline size_t br_nlmsg_size(struct net_device *dev, u32 filter_mask) static int br_port_fill_attrs(struct sk_buff *skb, const struct net_bridge_port *p) { - u8 mode = !!(p->flags & BR_HAIRPIN_MODE); + unsigned long flags = READ_ONCE(p->flags); + u8 mode = !!(flags & BR_HAIRPIN_MODE); struct net_bridge_port *backup_p; u64 timerval; if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) || - nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) || - nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) || + nla_put_u16(skb, IFLA_BRPORT_PRIORITY, READ_ONCE(p->priority)) || + nla_put_u32(skb, IFLA_BRPORT_COST, READ_ONCE(p->path_cost)) || nla_put_u8(skb, IFLA_BRPORT_MODE, mode) || - nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) || + nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(flags & BR_BPDU_GUARD)) || nla_put_u8(skb, IFLA_BRPORT_PROTECT, - !!(p->flags & BR_ROOT_BLOCK)) || + !!(flags & BR_ROOT_BLOCK)) || nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, - !!(p->flags & BR_MULTICAST_FAST_LEAVE)) || + !!(flags & BR_MULTICAST_FAST_LEAVE)) || nla_put_u8(skb, IFLA_BRPORT_MCAST_TO_UCAST, - !!(p->flags & BR_MULTICAST_TO_UNICAST)) || - nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) || + !!(flags & BR_MULTICAST_TO_UNICAST)) || + nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(flags & BR_LEARNING)) || nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, - !!(p->flags & BR_FLOOD)) || + !!(flags & BR_FLOOD)) || nla_put_u8(skb, IFLA_BRPORT_MCAST_FLOOD, - !!(p->flags & BR_MCAST_FLOOD)) || + !!(flags & BR_MCAST_FLOOD)) || nla_put_u8(skb, IFLA_BRPORT_BCAST_FLOOD, - !!(p->flags & BR_BCAST_FLOOD)) || - nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) || + !!(flags & BR_BCAST_FLOOD)) || + nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(flags & BR_PROXYARP)) || nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI, - !!(p->flags & BR_PROXYARP_WIFI)) || + !!(flags & BR_PROXYARP_WIFI)) || nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id), &p->designated_root) || nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id), &p->designated_bridge) || - nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) || - nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) || - nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) || + nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, + READ_ONCE(p->designated_port)) || + nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, + READ_ONCE(p->designated_cost)) || + nla_put_u16(skb, IFLA_BRPORT_ID, READ_ONCE(p->port_id)) || nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) || nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, p->topology_change_ack) || - nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) || - nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags & + nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, READ_ONCE(p->config_pending)) || + nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(flags & BR_VLAN_TUNNEL)) || nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask) || nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS, - !!(p->flags & BR_NEIGH_SUPPRESS)) || - nla_put_u8(skb, IFLA_BRPORT_MRP_RING_OPEN, !!(p->flags & + !!(flags & BR_NEIGH_SUPPRESS)) || + nla_put_u8(skb, IFLA_BRPORT_MRP_RING_OPEN, !!(flags & 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_LOCKED, !!(p->flags & BR_PORT_LOCKED)) || - nla_put_u8(skb, IFLA_BRPORT_MAB, !!(p->flags & BR_PORT_MAB)) || + !!(flags & BR_MRP_LOST_IN_CONT)) || + nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(flags & BR_ISOLATED)) || + nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(flags & BR_PORT_LOCKED)) || + nla_put_u8(skb, IFLA_BRPORT_MAB, !!(flags & BR_PORT_MAB)) || nla_put_u8(skb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, - !!(p->flags & BR_NEIGH_VLAN_SUPPRESS))) + !!(flags & BR_NEIGH_VLAN_SUPPRESS)) || + nla_put_u8(skb, IFLA_BRPORT_NEIGH_FORWARD_GRAT, + !!(flags & BR_NEIGH_FORWARD_GRAT))) return -EMSGSIZE; timerval = br_timer_value(&p->message_age_timer); @@ -534,7 +540,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, else err = br_fill_ifvlaninfo(skb, vg); - if (port && (port->flags & BR_VLAN_TUNNEL)) + if (port && test_bit(BR_VLAN_TUNNEL_BIT, &port->flags)) err = br_fill_vlan_tunnel_info(skb, vg); rcu_read_unlock(); if (err) @@ -817,7 +823,7 @@ static int br_afspec(struct net_bridge *br, err = 0; switch (nla_type(attr)) { case IFLA_BRIDGE_VLAN_TUNNEL_INFO: - if (!p || !(p->flags & BR_VLAN_TUNNEL)) + if (!p || !test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) return -EINVAL; err = br_parse_vlan_tunnel_info(attr, &tinfo_curr); if (err) @@ -902,6 +908,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_MCAST_MAX_GROUPS] = { .type = NLA_U32 }, [IFLA_BRPORT_NEIGH_VLAN_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1), [IFLA_BRPORT_BACKUP_NHID] = { .type = NLA_U32 }, + [IFLA_BRPORT_NEIGH_FORWARD_GRAT] = NLA_POLICY_MAX(NLA_U8, 1), }; /* Change the state of the port and notify spanning tree */ @@ -927,56 +934,67 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state) } /* Set/clear or port flags based on attribute */ -static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], +static void br_set_port_flag(unsigned long *set_flags, + unsigned long *clear_flags, + struct nlattr *tb[], int attrtype, unsigned long mask) { - if (!tb[attrtype]) - return; - - if (nla_get_u8(tb[attrtype])) - p->flags |= mask; - else - p->flags &= ~mask; + if (tb[attrtype]) { + if (nla_get_u8(tb[attrtype])) + *set_flags |= mask; + else + *clear_flags |= mask; + } } /* Process bridge protocol info on port */ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], struct netlink_ext_ack *extack) { - unsigned long old_flags, changed_mask; + unsigned long old_flags, flags, changed_mask; + unsigned long set = 0, clear = 0; bool br_vlan_tunnel_old; int err; - old_flags = p->flags; + old_flags = READ_ONCE(p->flags); br_vlan_tunnel_old = (old_flags & BR_VLAN_TUNNEL) ? true : false; - br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); - br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); - br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE); - br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); - br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); - br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MCAST_FLOOD, + BR_MCAST_FLOOD); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST); - br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); - br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); - 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); - br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB); - br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_BCAST_FLOOD, + BR_BCAST_FLOOD); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_PROXYARP_WIFI, + BR_PROXYARP_WIFI); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_VLAN_TUNNEL, + BR_VLAN_TUNNEL); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_NEIGH_SUPPRESS, + BR_NEIGH_SUPPRESS); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_MAB, BR_PORT_MAB); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, BR_NEIGH_VLAN_SUPPRESS); + br_set_port_flag(&set, &clear, tb, IFLA_BRPORT_NEIGH_FORWARD_GRAT, + BR_NEIGH_FORWARD_GRAT); - if ((p->flags & BR_PORT_MAB) && - (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) { + flags = (old_flags | set) & ~clear; + + if ((flags & BR_PORT_MAB) && + (!(flags & BR_PORT_LOCKED) || !(flags & BR_LEARNING))) { NL_SET_ERR_MSG(extack, "Bridge port must be locked and have learning enabled when MAB is enabled"); - p->flags = old_flags; return -EINVAL; - } else if (!(p->flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) { + } + if (!(flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) { struct net_bridge_fdb_flush_desc desc = { .flags = BIT(BR_FDB_LOCKED), .flags_mask = BIT(BR_FDB_LOCKED), @@ -986,15 +1004,17 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], br_fdb_flush(p->br, &desc); } - changed_mask = old_flags ^ p->flags; + changed_mask = old_flags ^ flags; - err = br_switchdev_set_port_flag(p, p->flags, changed_mask, extack); - if (err) { - p->flags = old_flags; + err = br_switchdev_set_port_flag(p, flags, changed_mask, extack); + if (err) return err; - } - if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL)) + do { + flags = (old_flags | set) & ~clear; + } while (!try_cmpxchg(&p->flags, &old_flags, flags)); + + if (br_vlan_tunnel_old && !(flags & BR_VLAN_TUNNEL)) nbp_vlan_tunnel_info_flush(p); br_port_flags_change(p, changed_mask); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index bed1b1d9b282..d55ea9516e3e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -183,6 +183,7 @@ enum { BR_VLFLAG_GLOBAL_MCAST_ENABLED = BIT(3), BR_VLFLAG_NEIGH_SUPPRESS_ENABLED = BIT(4), BR_VLFLAG_TAGGING_BY_SWITCHDEV = BIT(5), + BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED = BIT(6), }; /** @@ -451,7 +452,7 @@ struct net_bridge_port { #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj) #define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) -#define br_promisc_port(p) ((p)->flags & BR_PROMISC) +#define br_promisc_port(p) test_bit(BR_PROMISC_BIT, &(p)->flags) static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev) { @@ -600,6 +601,7 @@ struct br_input_skb_cb { u8 proxyarp_replied:1; u8 src_port_isolated:1; u8 promisc:1; + u8 grat_arp:1; #ifdef CONFIG_BRIDGE_VLAN_FILTERING u8 vlan_filtered:1; #endif @@ -2361,4 +2363,5 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p, struct nd_msg *msg); struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *m); bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid); +bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid); #endif diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 024210f95468..46919d73d42f 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -39,7 +39,7 @@ void br_set_state(struct net_bridge_port *p, unsigned int state) /* Don't change the state of the ports if they are driven by a different * protocol. */ - if (p->flags & BR_MRP_AWARE) + if (test_bit(BR_MRP_AWARE_BIT, &p->flags)) return; p->state = state; @@ -102,8 +102,9 @@ struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no) static int br_should_become_root_port(const struct net_bridge_port *p, u16 root_port) { - struct net_bridge *br; + u32 p_path_cost, rp_path_cost, p_designated_cost, rp_designated_cost; struct net_bridge_port *rp; + struct net_bridge *br; int t; br = p->br; @@ -125,11 +126,16 @@ static int br_should_become_root_port(const struct net_bridge_port *p, else if (t > 0) return 0; - if (p->designated_cost + p->path_cost < - rp->designated_cost + rp->path_cost) + p_path_cost = READ_ONCE(p->path_cost); + rp_path_cost = READ_ONCE(rp->path_cost); + p_designated_cost = READ_ONCE(p->designated_cost); + rp_designated_cost = READ_ONCE(rp->designated_cost); + + if (p_designated_cost + p_path_cost < + rp_designated_cost + rp_path_cost) return 1; - else if (p->designated_cost + p->path_cost > - rp->designated_cost + rp->path_cost) + else if (p_designated_cost + p_path_cost > + rp_designated_cost + rp_path_cost) return 0; t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); @@ -173,7 +179,7 @@ static void br_root_selection(struct net_bridge *br) if (!br_should_become_root_port(p, root_port)) continue; - if (p->flags & BR_ROOT_BLOCK) + if (test_bit(BR_ROOT_BLOCK_BIT, &p->flags)) br_root_port_block(br, p); else root_port = p->port_no; @@ -187,7 +193,8 @@ static void br_root_selection(struct net_bridge *br) } else { p = br_get_port(br, root_port); br->designated_root = p->designated_root; - br->root_path_cost = p->designated_cost + p->path_cost; + br->root_path_cost = READ_ONCE(p->designated_cost) + + READ_ONCE(p->path_cost); } } @@ -213,7 +220,7 @@ void br_transmit_config(struct net_bridge_port *p) struct net_bridge *br; if (timer_pending(&p->hold_timer)) { - p->config_pending = 1; + WRITE_ONCE(p->config_pending, 1); return; } @@ -240,7 +247,7 @@ void br_transmit_config(struct net_bridge_port *p) if (bpdu.message_age < br->max_age) { br_send_config_bpdu(p, &bpdu); p->topology_change_ack = 0; - p->config_pending = 0; + WRITE_ONCE(p->config_pending, 0); if (p->br->stp_enabled == BR_KERNEL_STP) mod_timer(&p->hold_timer, round_jiffies(jiffies + BR_HOLD_TIME)); @@ -252,9 +259,9 @@ static void br_record_config_information(struct net_bridge_port *p, const struct br_config_bpdu *bpdu) { p->designated_root = bpdu->root; - p->designated_cost = bpdu->root_path_cost; + WRITE_ONCE(p->designated_cost, bpdu->root_path_cost); p->designated_bridge = bpdu->bridge_id; - p->designated_port = bpdu->port_id; + WRITE_ONCE(p->designated_port, bpdu->port_id); p->designated_age = jiffies - bpdu->message_age; mod_timer(&p->message_age_timer, jiffies @@ -288,6 +295,7 @@ void br_transmit_tcn(struct net_bridge *br) static int br_should_become_designated_port(const struct net_bridge_port *p) { struct net_bridge *br; + u32 p_designated_cost; int t; br = p->br; @@ -297,9 +305,10 @@ static int br_should_become_designated_port(const struct net_bridge_port *p) if (memcmp(&p->designated_root, &br->designated_root, 8)) return 1; - if (br->root_path_cost < p->designated_cost) + p_designated_cost = READ_ONCE(p->designated_cost); + if (br->root_path_cost < p_designated_cost) return 1; - else if (br->root_path_cost > p->designated_cost) + else if (br->root_path_cost > p_designated_cost) return 0; t = memcmp(&br->bridge_id, &p->designated_bridge, 8); @@ -331,6 +340,7 @@ static void br_designated_port_selection(struct net_bridge *br) static int br_supersedes_port_info(const struct net_bridge_port *p, const struct br_config_bpdu *bpdu) { + u32 p_designated_cost; int t; t = memcmp(&bpdu->root, &p->designated_root, 8); @@ -339,9 +349,10 @@ static int br_supersedes_port_info(const struct net_bridge_port *p, else if (t > 0) return 0; - if (bpdu->root_path_cost < p->designated_cost) + p_designated_cost = READ_ONCE(p->designated_cost); + if (bpdu->root_path_cost < p_designated_cost) return 1; - else if (bpdu->root_path_cost > p->designated_cost) + else if (bpdu->root_path_cost > p_designated_cost) return 0; t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8); @@ -421,9 +432,9 @@ void br_become_designated_port(struct net_bridge_port *p) br = p->br; p->designated_root = br->designated_root; - p->designated_cost = br->root_path_cost; + WRITE_ONCE(p->designated_cost, br->root_path_cost); p->designated_bridge = br->bridge_id; - p->designated_port = p->port_id; + WRITE_ONCE(p->designated_port, p->port_id); } @@ -479,14 +490,14 @@ void br_port_state_selection(struct net_bridge *br) /* Don't change port states if userspace is handling STP */ if (br->stp_enabled != BR_USER_STP) { if (p->port_no == br->root_port) { - p->config_pending = 0; + WRITE_ONCE(p->config_pending, 0); p->topology_change_ack = 0; br_make_forwarding(p); } else if (br_is_designated_port(p)) { timer_delete(&p->message_age_timer); br_make_forwarding(p); } else { - p->config_pending = 0; + WRITE_ONCE(p->config_pending, 0); p->topology_change_ack = 0; br_make_blocking(p); } diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 7895489ac6fe..74ec42ba1e7d 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -178,7 +178,7 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, if (!ether_addr_equal(eth_hdr(skb)->h_dest, br->group_addr)) goto out; - if (p->flags & BR_BPDU_GUARD) { + if (test_bit(BR_BPDU_GUARD_BIT, &p->flags)) { br_notice(br, "BPDU received on blocked port %u(%s)\n", (unsigned int) p->port_no, p->dev->name); br_stp_disable_port(p); diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 28c1d3f7e22f..a7e5422eb5d1 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -34,11 +34,11 @@ void br_init_port(struct net_bridge_port *p) { int err; - p->port_id = br_make_port_id(p->priority, p->port_no); + WRITE_ONCE(p->port_id, br_make_port_id(p->priority, p->port_no)); br_become_designated_port(p); br_set_state(p, BR_STATE_BLOCKING); p->topology_change_ack = 0; - p->config_pending = 0; + WRITE_ONCE(p->config_pending, 0); err = __set_ageing_time(p->dev, p->br->ageing_time); if (err) @@ -105,7 +105,7 @@ void br_stp_disable_port(struct net_bridge_port *p) br_become_designated_port(p); br_set_state(p, BR_STATE_DISABLED); p->topology_change_ack = 0; - p->config_pending = 0; + WRITE_ONCE(p->config_pending, 0); br_ifinfo_notify(RTM_NEWLINK, NULL, p); @@ -320,10 +320,10 @@ int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio) new_port_id = br_make_port_id(newprio, p->port_no); if (br_is_designated_port(p)) - p->designated_port = new_port_id; + WRITE_ONCE(p->designated_port, new_port_id); - p->port_id = new_port_id; - p->priority = newprio; + WRITE_ONCE(p->port_id, new_port_id); + WRITE_ONCE(p->priority, newprio); if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) && p->port_id < p->designated_port) { br_become_designated_port(p); @@ -340,8 +340,8 @@ int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost) path_cost > BR_MAX_PATH_COST) return -ERANGE; - p->flags |= BR_ADMIN_COST; - p->path_cost = path_cost; + set_bit(BR_ADMIN_COST_BIT, &p->flags); + WRITE_ONCE(p->path_cost, path_cost); br_configuration_update(p->br); br_port_state_selection(p->br); return 0; diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index e5d453305381..83cc9c6a3943 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -160,5 +160,5 @@ void br_stp_port_timer_init(struct net_bridge_port *p) unsigned long br_timer_value(const struct timer_list *timer) { return timer_pending(timer) - ? jiffies_delta_to_clock_t(timer->expires - jiffies) : 0; + ? jiffies_delta_to_clock_t(READ_ONCE(timer->expires) - jiffies) : 0; } diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index ee3ad9dfbab9..990c6b38fd39 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -20,7 +20,7 @@ static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p, if (br_multicast_igmp_type(skb)) return false; - return (p->flags & BR_TX_FWD_OFFLOAD) && + return test_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags) && (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom); } @@ -287,7 +287,7 @@ static int nbp_switchdev_add(struct net_bridge_port *p, return err; if (tx_fwd_offload) { - p->flags |= BR_TX_FWD_OFFLOAD; + set_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags); static_branch_inc(&br_switchdev_tx_fwd_offload); } @@ -307,8 +307,8 @@ static void nbp_switchdev_del(struct net_bridge_port *p) if (p->hwdom) nbp_switchdev_hwdom_put(p); - if (p->flags & BR_TX_FWD_OFFLOAD) { - p->flags &= ~BR_TX_FWD_OFFLOAD; + if (test_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags)) { + clear_bit(BR_TX_FWD_OFFLOAD_BIT, &p->flags); static_branch_dec(&br_switchdev_tx_fwd_offload); } } diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index d6df81fa0d13..bde731c01e10 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -44,46 +44,50 @@ const struct brport_attribute brport_attr_##_name = { \ .store = _store, \ }; -#define BRPORT_ATTR_FLAG(_name, _mask) \ +#define BRPORT_ATTR_FLAG(_name, _bitnr) \ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \ { \ - return sysfs_emit(buf, "%d\n", !!(p->flags & _mask)); \ + return sysfs_emit(buf, "%d\n", test_bit(_bitnr, &p->flags)); \ } \ static int store_##_name(struct net_bridge_port *p, unsigned long v) \ { \ - return store_flag(p, v, _mask); \ + return store_flag(p, v, _bitnr); \ } \ static BRPORT_ATTR(_name, 0644, \ show_##_name, store_##_name) static int store_flag(struct net_bridge_port *p, unsigned long v, - unsigned long mask) + unsigned long bitnr) { + unsigned long oflags, flags = READ_ONCE(p->flags); struct netlink_ext_ack extack = {0}; - unsigned long flags = p->flags; int err; + oflags = flags; if (v) - flags |= mask; + __set_bit(bitnr, &flags); else - flags &= ~mask; + __clear_bit(bitnr, &flags); - if (flags != p->flags) { - err = br_switchdev_set_port_flag(p, flags, mask, &extack); - if (err) { - netdev_err(p->dev, "%s\n", extack._msg); - return err; - } + if (flags == oflags) + return 0; - p->flags = flags; - br_port_flags_change(p, mask); + err = br_switchdev_set_port_flag(p, flags, BIT(bitnr), &extack); + if (err) { + netdev_err(p->dev, "%s\n", extack._msg); + return err; } + if (v) + set_bit(bitnr, &p->flags); + else + clear_bit(bitnr, &p->flags); + br_port_flags_change(p, BIT(bitnr)); return 0; } static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) { - return sysfs_emit(buf, "%d\n", p->path_cost); + return sysfs_emit(buf, "%d\n", READ_ONCE(p->path_cost)); } static int store_path_cost(struct net_bridge_port *p, unsigned long v) @@ -100,7 +104,7 @@ static BRPORT_ATTR(path_cost, 0644, show_path_cost, store_path_cost); static ssize_t show_priority(struct net_bridge_port *p, char *buf) { - return sysfs_emit(buf, "%d\n", p->priority); + return sysfs_emit(buf, "%d\n", READ_ONCE(p->priority)); } static int store_priority(struct net_bridge_port *p, unsigned long v) @@ -129,19 +133,19 @@ static BRPORT_ATTR(designated_bridge, 0444, show_designated_bridge, NULL); static ssize_t show_designated_port(struct net_bridge_port *p, char *buf) { - return sysfs_emit(buf, "%d\n", p->designated_port); + return sysfs_emit(buf, "%d\n", READ_ONCE(p->designated_port)); } static BRPORT_ATTR(designated_port, 0444, show_designated_port, NULL); static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf) { - return sysfs_emit(buf, "%d\n", p->designated_cost); + return sysfs_emit(buf, "%d\n", READ_ONCE(p->designated_cost)); } static BRPORT_ATTR(designated_cost, 0444, show_designated_cost, NULL); static ssize_t show_port_id(struct net_bridge_port *p, char *buf) { - return sysfs_emit(buf, "0x%x\n", p->port_id); + return sysfs_emit(buf, "0x%x\n", READ_ONCE(p->port_id)); } static BRPORT_ATTR(port_id, 0444, show_port_id, NULL); @@ -160,7 +164,7 @@ static BRPORT_ATTR(change_ack, 0444, show_change_ack, NULL); static ssize_t show_config_pending(struct net_bridge_port *p, char *buf) { - return sysfs_emit(buf, "%d\n", p->config_pending); + return sysfs_emit(buf, "%d\n", READ_ONCE(p->config_pending)); } static BRPORT_ATTR(config_pending, 0444, show_config_pending, NULL); @@ -247,17 +251,17 @@ static int store_backup_port(struct net_bridge_port *p, char *buf) } static BRPORT_ATTR_RAW(backup_port, 0644, show_backup_port, store_backup_port); -BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); -BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); -BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); -BRPORT_ATTR_FLAG(learning, BR_LEARNING); -BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD); -BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP); -BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI); -BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD); -BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); -BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); -BRPORT_ATTR_FLAG(isolated, BR_ISOLATED); +BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE_BIT); +BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD_BIT); +BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK_BIT); +BRPORT_ATTR_FLAG(learning, BR_LEARNING_BIT); +BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD_BIT); +BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP_BIT); +BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI_BIT); +BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD_BIT); +BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD_BIT); +BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS_BIT); +BRPORT_ATTR_FLAG(isolated, BR_ISOLATED_BIT); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -273,8 +277,8 @@ static int store_multicast_router(struct net_bridge_port *p, static BRPORT_ATTR(multicast_router, 0644, show_multicast_router, store_multicast_router); -BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); -BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST); +BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE_BIT); +BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST_BIT); #endif static const struct brport_attribute *brport_attrs[] = { diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 84a180927eb7..5560afcaaca3 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -2176,6 +2176,7 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] = [BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS] = { .type = NLA_REJECT }, [BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS] = { .type = NLA_U32 }, [BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1), + [BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT] = NLA_POLICY_MAX(NLA_U8, 1), }; static int br_vlan_rtm_process_one(struct net_device *dev, diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index 5514e1fc8d1f..fcc200c3e3da 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -54,7 +54,8 @@ bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr, /* Check user-visible priv_flags that affect output */ if ((v_curr->priv_flags ^ range_end->priv_flags) & - (BR_VLFLAG_NEIGH_SUPPRESS_ENABLED | BR_VLFLAG_MCAST_ENABLED)) + (BR_VLFLAG_NEIGH_SUPPRESS_ENABLED | BR_VLFLAG_MCAST_ENABLED | + BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED)) return false; #ifdef CONFIG_BRIDGE_IGMP_SNOOPING @@ -74,7 +75,9 @@ bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v, if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, br_vlan_get_state(v)) || !__vlan_tun_put(skb, v) || nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS, - !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED))) + !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED)) || + nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT, + !!(v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED))) return false; #ifdef CONFIG_BRIDGE_IGMP_SNOOPING @@ -103,6 +106,7 @@ size_t br_vlan_opts_nl_size(void) + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS */ #endif + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS */ + + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT */ + 0; } @@ -168,7 +172,7 @@ static int br_vlan_modify_tunnel(const struct net_bridge_port *p, NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans"); return -EINVAL; } - if (!(p->flags & BR_VLAN_TUNNEL)) { + if (!test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) { NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set"); return -EINVAL; } @@ -277,6 +281,22 @@ static int br_vlan_process_one_opts(const struct net_bridge *br, } } + if (tb[BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT]) { + bool enabled = v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED; + bool val = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_NEIGH_FORWARD_GRAT]); + + if (!p) { + NL_SET_ERR_MSG_MOD(extack, + "Can't set neigh_forward_grat for non-port vlans"); + return -EINVAL; + } + + if (val != enabled) { + v->priv_flags ^= BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED; + *changed = true; + } + } + return 0; } diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index b9a1303da977..af0c903aa4ac 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -38,3 +38,7 @@ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o # watchers obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o obj-$(CONFIG_BRIDGE_EBT_NFLOG) += ebt_nflog.o + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE := y +endif diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 8a6a069329d2..f20c039e44c8 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2485,6 +2485,8 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; #ifdef CONFIG_NETFILTER_XTABLES_COMPAT /* try real handler in case userland supplied needed padding */ @@ -2550,6 +2552,8 @@ static int do_ebt_set_ctl(struct sock *sk, int cmd, sockptr_t arg, if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case EBT_SO_SET_ENTRIES: diff --git a/net/core/dev.c b/net/core/dev.c index 0c6c270d9f7d..5c01dfaa6c44 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1593,7 +1593,7 @@ EXPORT_SYMBOL(netdev_features_change); void netif_state_change(struct net_device *dev) { - netdev_ops_assert_locked_or_invisible(dev); + netdev_assert_locked_ops_compat_or_invisible(dev); if (dev->flags & IFF_UP) { struct netdev_notifier_change_info change_info = { @@ -1693,7 +1693,7 @@ static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack) set_bit(__LINK_STATE_START, &dev->state); - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (ops->ndo_validate_addr) ret = ops->ndo_validate_addr(dev); @@ -1770,11 +1770,12 @@ static void __dev_close_many(struct list_head *head) * event. */ - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (ops->ndo_stop) ops->ndo_stop(dev); + netif_rx_mode_cancel_retry(dev); netif_set_up(dev, false); netpoll_poll_enable(dev); } @@ -3198,7 +3199,7 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) if (dev->reg_state == NETREG_REGISTERED || dev->reg_state == NETREG_UNREGISTERING) { - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); rc = netdev_queue_update_kobjects(dev, dev->real_num_tx_queues, txq); @@ -3247,7 +3248,7 @@ int netif_set_real_num_rx_queues(struct net_device *dev, unsigned int rxq) return -EINVAL; if (dev->reg_state == NETREG_REGISTERED) { - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); rc = net_rx_queue_update_kobjects(dev, dev->real_num_rx_queues, rxq); @@ -3993,10 +3994,11 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb, struct skb_shared_info *shinfo; struct net_iov *niov; - if (likely(skb_frags_readable(skb))) + if (likely(skb_frags_readable(skb) || + dev->netmem_tx == NETMEM_TX_NO_DMA)) goto out; - if (!dev->netmem_tx) + if (dev->netmem_tx == NETMEM_TX_NONE) goto out_free; shinfo = skb_shinfo(skb); @@ -5381,8 +5383,6 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, u32 tail; reason = SKB_DROP_REASON_DEV_READY; - if (unlikely(!netif_running(skb->dev))) - goto bad_dev; sd = &per_cpu(softnet_data, cpu); @@ -5394,6 +5394,10 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, backlog_lock_irq_save(sd, &flags); qlen = skb_queue_len(&sd->input_pkt_queue); if (likely(qlen <= max_backlog)) { + if (unlikely(!netif_running(skb->dev))) { + backlog_unlock_irq_restore(sd, flags); + goto bad_dev; + } if (!qlen) { /* Schedule NAPI for backlog device. We can use * non atomic operation as we own the queue lock. @@ -6862,22 +6866,6 @@ static void skb_defer_free_flush(void) #if defined(CONFIG_NET_RX_BUSY_POLL) -static void __busy_poll_stop(struct napi_struct *napi, unsigned long timeout) -{ - if (!timeout) { - gro_normal_list(&napi->gro); - __napi_schedule(napi); - return; - } - - /* Flush too old packets. If HZ < 1000, flush all packets */ - gro_flush_normal(&napi->gro, HZ >= 1000); - - clear_bit(NAPI_STATE_SCHED, &napi->state); - hrtimer_start(&napi->timer, ns_to_ktime(timeout), - HRTIMER_MODE_REL_PINNED); -} - enum { NAPI_F_PREFER_BUSY_POLL = 1, NAPI_F_END_ON_RESCHED = 2, @@ -6893,8 +6881,8 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, /* Busy polling means there is a high chance device driver hard irq * could not grab NAPI_STATE_SCHED, and that NAPI_STATE_MISSED was * set in napi_schedule_prep(). - * Since we are about to call napi->poll() once more, we can safely - * clear NAPI_STATE_MISSED. + * Since we either call napi->poll() once more or start the timer, + * we can safely clear NAPI_STATE_MISSED. * * Note: x86 could use a single "lock and ..." instruction * to perform these two clear_bit() @@ -6907,27 +6895,35 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, if (flags & NAPI_F_PREFER_BUSY_POLL) { napi->defer_hard_irqs_count = napi_get_defer_hard_irqs(napi); - if (napi->defer_hard_irqs_count) { - /* A short enough gro flush timeout and long enough - * poll can result in timer firing too early. - * Timer will be armed later if necessary. - */ + if (napi->defer_hard_irqs_count) timeout = napi_get_gro_flush_timeout(napi); + } + if (timeout) { + netpoll_poll_unlock(have_poll_lock); + + /* Drain aged GRO packets before clearing SCHED since the NAPI + * won't run again until after the timer fires. When HZ < 1000, + * GRO age comparison is too coarse, so flush everything. + */ + gro_flush_normal(&napi->gro, HZ >= 1000); + + clear_bit(NAPI_STATE_SCHED, &napi->state); + hrtimer_start(&napi->timer, ns_to_ktime(timeout), + HRTIMER_MODE_REL_PINNED); + } else { + /* Use driver poll to re-enable device interrupts. */ + rc = napi->poll(napi, budget); + /* Unless rc == budget we no longer own the NAPI instance, + * IRQ may fire on another CPU, poll this NAPI, and enter GRO. + */ + trace_napi_poll(napi, rc, budget); + netpoll_poll_unlock(have_poll_lock); + if (rc == budget) { + gro_normal_list(&napi->gro); + __napi_schedule(napi); } } - /* All we really want here is to re-enable device interrupts. - * Ideally, a new ndo_busy_poll_stop() could avoid another round. - */ - rc = napi->poll(napi, budget); - /* We can't gro_normal_list() here, because napi->poll() might have - * rearmed the napi (napi_complete_done()) in which case it could - * already be running on another CPU. - */ - trace_napi_poll(napi, rc, budget); - netpoll_poll_unlock(have_poll_lock); - if (rc == budget) - __busy_poll_stop(napi, timeout); bpf_net_ctx_clear(bpf_net_ctx); local_bh_enable(); } @@ -7301,7 +7297,7 @@ void netif_queue_set_napi(struct net_device *dev, unsigned int queue_index, if (WARN_ON_ONCE(napi && !napi->dev)) return; - netdev_ops_assert_locked_or_invisible(dev); + netdev_assert_locked_ops_compat_or_invisible(dev); switch (type) { case NETDEV_QUEUE_TYPE_RX: @@ -9596,7 +9592,7 @@ int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) kuid_t uid; kgid_t gid; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); promiscuity = dev->promiscuity + inc; if (promiscuity == 0) { @@ -9655,7 +9651,7 @@ int netif_set_allmulti(struct net_device *dev, int inc, bool notify) unsigned int old_flags = dev->flags, old_gflags = dev->gflags; unsigned int allmulti, flags; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); allmulti = dev->allmulti + inc; if (allmulti == 0) { @@ -9723,7 +9719,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags, unsigned int old_flags = dev->flags; int ret; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); /* * Set the flags on our device. @@ -9871,7 +9867,7 @@ int netif_set_mtu_ext(struct net_device *dev, int new_mtu, { int err, orig_mtu; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (new_mtu == dev->mtu) return 0; @@ -10144,17 +10140,36 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b) } EXPORT_SYMBOL(netdev_port_same_parent_id); +static struct net_device *dev_get_iflink_dev(struct net_device *dev) +{ + struct net *net; + + ASSERT_RTNL(); + + if (!dev->netdev_ops->ndo_get_iflink || !dev->rtnl_link_ops || + !dev->rtnl_link_ops->get_link_net) + return dev; + + net = dev->rtnl_link_ops->get_link_net(dev); + return __dev_get_by_index(net, dev_get_iflink(dev)); +} + int netif_change_proto_down(struct net_device *dev, bool proto_down) { + struct net_device *iflink_dev; + if (!dev->change_proto_down) return -EOPNOTSUPP; if (!netif_device_present(dev)) return -ENODEV; + iflink_dev = dev_get_iflink_dev(dev); + if (!iflink_dev) + return -ENODEV; + WRITE_ONCE(dev->proto_down, proto_down); if (proto_down) netif_carrier_off(dev); - else + else if (dev == iflink_dev || netif_carrier_ok(iflink_dev)) netif_carrier_on(dev); - WRITE_ONCE(dev->proto_down, proto_down); return 0; } @@ -10305,7 +10320,7 @@ static int dev_xdp_install(struct net_device *dev, enum bpf_xdp_mode mode, struct netdev_bpf xdp; int err; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (dev->cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED && prog && !prog->aux->xdp_has_frags) { @@ -10757,7 +10772,7 @@ u32 dev_get_min_mp_channel_count(const struct net_device *dev) { int i; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); for (i = dev->real_num_rx_queues - 1; i >= 0; i--) if (dev->_rx[i].mp_params.mp_priv) @@ -10985,7 +11000,7 @@ int __netdev_update_features(struct net_device *dev) int err = -1; ASSERT_RTNL(); - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); features = netdev_get_wanted_features(dev); @@ -11217,7 +11232,8 @@ static int netif_alloc_netdev_queues(struct net_device *dev) netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); spin_lock_init(&dev->tx_global_lock); - + spin_lock_init(&dev->watchdog_lock); + dev->watchdog_ref_held = false; return 0; } @@ -11718,6 +11734,8 @@ void netdev_run_todo(void) WARN_ON(rcu_access_pointer(dev->ip_ptr)); WARN_ON(rcu_access_pointer(dev->ip6_ptr)); + netdev_name_node_alt_flush(dev); + netdev_name_node_free(dev->name_node); netdev_do_free_pcpu_stats(dev); if (dev->priv_destructor) dev->priv_destructor(dev); @@ -12080,8 +12098,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, #endif mutex_init(&dev->lock); - INIT_LIST_HEAD(&dev->rx_mode_node); - __hw_addr_init(&dev->rx_mode_addr_cache); + netif_rx_mode_init(dev); dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; setup(dev); @@ -12431,8 +12448,6 @@ void unregister_netdevice_many_notify(struct list_head *head, dev_uc_flush(dev); dev_mc_flush(dev); - netdev_name_node_alt_flush(dev); - netdev_name_node_free(dev->name_node); netdev_rss_contexts_free(dev); diff --git a/net/core/dev.h b/net/core/dev.h index 0cf24b8f5008..4121c50e7c88 100644 --- a/net/core/dev.h +++ b/net/core/dev.h @@ -166,8 +166,10 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier); void __dev_set_rx_mode(struct net_device *dev); int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify); +void netif_rx_mode_init(struct net_device *dev); bool netif_rx_mode_clean(struct net_device *dev); void netif_rx_mode_sync(struct net_device *dev); +void netif_rx_mode_cancel_retry(struct net_device *dev); void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, unsigned int gchanges, u32 portid, @@ -396,4 +398,10 @@ int dev_get_hwtstamp_phylib(struct net_device *dev, struct kernel_hwtstamp_config *cfg); int net_hwtstamp_validate(const struct kernel_hwtstamp_config *cfg); +/* Caller holds RTNL, netdev->lock or RCU */ +static inline bool dev_isalive(const struct net_device *dev) +{ + return READ_ONCE(dev->reg_state) <= NETREG_REGISTERED; +} + #endif diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index d73fcb0c6785..e17f64a65e17 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -1252,6 +1252,35 @@ static int netif_uc_promisc_update(struct net_device *dev) return 0; } +/* Total retry budget (4): 1+2+4+8 = 15 seconds */ +#define NETIF_RX_MODE_RETRY_MAX 4 + +void netif_rx_mode_schedule_retry(struct net_device *dev) +{ + unsigned long delay; + + netdev_assert_locked_ops_compat(dev); + + if (dev->rx_mode_retry_count >= NETIF_RX_MODE_RETRY_MAX) { + netdev_err(dev, "rx_mode retry limit reached, giving up\n"); + return; + } + + delay = HZ << dev->rx_mode_retry_count; + if (mod_timer(&dev->rx_mode_retry_timer, jiffies + delay)) + return; + if (!dev->rx_mode_retry_count) + netdev_info(dev, "rx_mode install failed, retrying with backoff\n"); + dev->rx_mode_retry_count++; +} +EXPORT_SYMBOL_GPL(netif_rx_mode_schedule_retry); + +void netif_rx_mode_cancel_retry(struct net_device *dev) +{ + timer_delete_sync(&dev->rx_mode_retry_timer); + dev->rx_mode_retry_count = 0; +} + static void netif_rx_mode_run(struct net_device *dev) { struct netdev_hw_addr_list uc_snap, mc_snap, uc_ref, mc_ref; @@ -1260,7 +1289,7 @@ static void netif_rx_mode_run(struct net_device *dev) int err; might_sleep(); - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); __hw_addr_init(&uc_snap); __hw_addr_init(&mc_snap); @@ -1275,8 +1304,8 @@ static void netif_rx_mode_run(struct net_device *dev) err = netif_addr_lists_snapshot(dev, &uc_snap, &mc_snap, &uc_ref, &mc_ref); if (err) { - netdev_WARN(dev, "failed to sync uc/mc addresses\n"); netif_addr_unlock_bh(dev); + netif_rx_mode_schedule_retry(dev); return; } @@ -1292,12 +1321,17 @@ static void netif_rx_mode_run(struct net_device *dev) __dev_set_promiscuity(dev, promisc_inc, false); if (ops->ndo_set_rx_mode_async) { - ops->ndo_set_rx_mode_async(dev, &uc_snap, &mc_snap); + err = ops->ndo_set_rx_mode_async(dev, &uc_snap, &mc_snap); netif_addr_lock_bh(dev); netif_addr_lists_reconcile(dev, &uc_snap, &mc_snap, &uc_ref, &mc_ref); netif_addr_unlock_bh(dev); + + if (err) + netif_rx_mode_schedule_retry(dev); + else + dev->rx_mode_retry_count = 0; } else if (ops->ndo_set_rx_mode) { netif_addr_lock_bh(dev); ops->ndo_set_rx_mode(dev); @@ -1350,6 +1384,21 @@ static void netif_rx_mode_queue(struct net_device *dev) schedule_work(&rx_mode_work); } +static void netif_rx_mode_retry(struct timer_list *t) +{ + struct net_device *dev = + timer_container_of(dev, t, rx_mode_retry_timer); + + netif_rx_mode_queue(dev); +} + +void netif_rx_mode_init(struct net_device *dev) +{ + INIT_LIST_HEAD(&dev->rx_mode_node); + __hw_addr_init(&dev->rx_mode_addr_cache); + timer_setup(&dev->rx_mode_retry_timer, netif_rx_mode_retry, 0); +} + /** * __dev_set_rx_mode() - upload unicast and multicast address lists to device * and configure RX filtering. diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index f3979b276090..a320e264eaaf 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -260,7 +260,7 @@ int dev_get_hwtstamp_phylib(struct net_device *dev, { struct hwtstamp_provider *hwprov; - hwprov = rtnl_dereference(dev->hwprov); + hwprov = netdev_ops_lock_dereference(dev->hwprov, dev); if (hwprov) { cfg->qualifier = hwprov->desc.qualifier; if (hwprov->source == HWTSTAMP_SOURCE_PHYLIB && @@ -337,7 +337,7 @@ int dev_set_hwtstamp_phylib(struct net_device *dev, bool phy_ts; int err; - hwprov = rtnl_dereference(dev->hwprov); + hwprov = netdev_ops_lock_dereference(dev->hwprov, dev); if (hwprov) { if (hwprov->source == HWTSTAMP_SOURCE_PHYLIB && hwprov->phydev) { diff --git a/net/core/devmem.c b/net/core/devmem.c index 4f71de44c0fb..957d6b96216b 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -181,7 +181,7 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, } struct net_devmem_dmabuf_binding * -net_devmem_bind_dmabuf(struct net_device *dev, +net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, struct device *dma_dev, enum dma_data_direction direction, unsigned int dmabuf_fd, struct netdev_nl_sock *priv, @@ -212,6 +212,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, } binding->dev = dev; + binding->vdev = vdev; xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC); err = percpu_ref_init(&binding->ref, @@ -407,7 +408,8 @@ struct net_devmem_dmabuf_binding *net_devmem_get_binding(struct sock *sk, */ dst_dev = dst_dev_rcu(dst); if (unlikely(!dst_dev) || - unlikely(dst_dev != READ_ONCE(binding->dev))) { + unlikely(dst_dev != READ_ONCE(binding->dev) && + dst_dev != READ_ONCE(binding->vdev))) { err = -ENODEV; goto out_unlock; } diff --git a/net/core/devmem.h b/net/core/devmem.h index 1c5c18581fcb..3852a56036cb 100644 --- a/net/core/devmem.h +++ b/net/core/devmem.h @@ -19,7 +19,13 @@ struct net_devmem_dmabuf_binding { struct dma_buf *dmabuf; struct dma_buf_attachment *attachment; struct sg_table *sgt; + /* Physical NIC that does the actual DMA for this binding. */ struct net_device *dev; + /* Opaque cookie identifying the virtual device (e.g. netkit) the user + * called bind-tx on. Used only for pointer comparison. Never + * dereferenced. + */ + void *vdev; struct gen_pool *chunk_pool; /* Protect dev */ struct mutex lock; @@ -84,7 +90,7 @@ struct dmabuf_genpool_chunk_owner { void __net_devmem_dmabuf_binding_free(struct work_struct *wq); struct net_devmem_dmabuf_binding * -net_devmem_bind_dmabuf(struct net_device *dev, +net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, struct device *dma_dev, enum dma_data_direction direction, unsigned int dmabuf_fd, struct netdev_nl_sock *priv, @@ -165,7 +171,7 @@ static inline void net_devmem_put_net_iov(struct net_iov *niov) } static inline struct net_devmem_dmabuf_binding * -net_devmem_bind_dmabuf(struct net_device *dev, +net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, struct device *dma_dev, enum dma_data_direction direction, unsigned int dmabuf_fd, diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index f23cea9e1aaf..2bf3cab5e557 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -110,7 +110,7 @@ struct net_dm_alert_ops { void (*kfree_skb_probe)(void *ignore, struct sk_buff *skb, void *location, enum skb_drop_reason reason, - struct sock *rx_sk); + const struct sock *rx_sk); void (*napi_poll_probe)(void *ignore, struct napi_struct *napi, int work, int budget); void (*work_item_func)(struct work_struct *work); @@ -266,7 +266,7 @@ static void trace_drop_common(struct sk_buff *skb, void *location) static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location, enum skb_drop_reason reason, - struct sock *rx_sk) + const struct sock *rx_sk) { trace_drop_common(skb, location); } @@ -493,7 +493,7 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location, enum skb_drop_reason reason, - struct sock *rx_sk) + const struct sock *rx_sk) { ktime_t tstamp = ktime_get_real(); struct per_cpu_dm_data *data; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 8ca634964e36..cf374c208732 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -349,7 +349,7 @@ int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl, if (err != -EAGAIN) { if ((arg->flags & FIB_LOOKUP_NOREF) || - likely(refcount_inc_not_zero(&rule->refcnt))) { + likely(fib_rule_get_safe(rule))) { arg->rule = rule; goto out; } @@ -410,8 +410,12 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family, if (!ops) return -EAFNOSUPPORT; list_for_each_entry_rcu(rule, &ops->rules_list, list) { + if (!fib_rule_get_safe(rule)) + continue; + err = call_fib_rule_notifier(nb, FIB_EVENT_RULE_ADD, rule, family, extack); + fib_rule_put(rule); if (err) break; } diff --git a/net/core/filter.c b/net/core/filter.c index 80439767e0ee..40037413dd4e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2733,11 +2733,13 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, poffset += len; sge->length = 0; put_page(sg_page(sge)); + __clear_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); } while (i != last_sge); sg_set_page(&msg->sg.data[first_sge], page, copy, 0); + __clear_bit(first_sge, msg->sg.copy); /* To repair sg ring we need to shift entries. If we only * had a single entry though we can just replace it and @@ -2763,9 +2765,11 @@ BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start, break; msg->sg.data[i] = msg->sg.data[move_from]; + sk_msg_sg_copy_assign(msg, i, msg, move_from); msg->sg.data[move_from].length = 0; msg->sg.data[move_from].page_link = 0; msg->sg.data[move_from].offset = 0; + __clear_bit(move_from, msg->sg.copy); sk_msg_iter_var_next(i); } while (1); @@ -2794,6 +2798,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, { struct scatterlist sge, nsge, nnsge, rsge = {0}, *psge; u32 new, i = 0, l = 0, space, copy = 0, offset = 0; + bool sge_copy, nsge_copy, nnsge_copy, rsge_copy = false; u8 *raw, *to, *from; struct page *page; @@ -2866,6 +2871,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, sk_msg_iter_var_prev(i); psge = sk_msg_elem(msg, i); rsge = sk_msg_elem_cpy(msg, i); + rsge_copy = test_bit(i, msg->sg.copy); psge->length = start - offset; rsge.length -= psge->length; @@ -2890,24 +2896,32 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, /* Shift one or two slots as needed */ sge = sk_msg_elem_cpy(msg, new); + sge_copy = test_bit(new, msg->sg.copy); sg_unmark_end(&sge); nsge = sk_msg_elem_cpy(msg, i); + nsge_copy = test_bit(i, msg->sg.copy); if (rsge.length) { sk_msg_iter_var_next(i); nnsge = sk_msg_elem_cpy(msg, i); + nnsge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_next(msg, end); } while (i != msg->sg.end) { msg->sg.data[i] = sge; + __assign_bit(i, msg->sg.copy, sge_copy); sge = nsge; + sge_copy = nsge_copy; sk_msg_iter_var_next(i); if (rsge.length) { nsge = nnsge; + nsge_copy = nnsge_copy; nnsge = sk_msg_elem_cpy(msg, i); + nnsge_copy = test_bit(i, msg->sg.copy); } else { nsge = sk_msg_elem_cpy(msg, i); + nsge_copy = test_bit(i, msg->sg.copy); } } @@ -2921,6 +2935,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, get_page(sg_page(&rsge)); sk_msg_iter_var_next(new); msg->sg.data[new] = rsge; + __assign_bit(new, msg->sg.copy, rsge_copy); } sk_msg_reset_curr(msg); @@ -2948,25 +2963,33 @@ static void sk_msg_shift_left(struct sk_msg *msg, int i) prev = i; sk_msg_iter_var_next(i); msg->sg.data[prev] = msg->sg.data[i]; + sk_msg_sg_copy_assign(msg, prev, msg, i); } while (i != msg->sg.end); sk_msg_iter_prev(msg, end); + __clear_bit(msg->sg.end, msg->sg.copy); } static void sk_msg_shift_right(struct sk_msg *msg, int i) { struct scatterlist tmp, sge; + bool tmp_copy, sge_copy; sk_msg_iter_next(msg, end); sge = sk_msg_elem_cpy(msg, i); + sge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); tmp = sk_msg_elem_cpy(msg, i); + tmp_copy = test_bit(i, msg->sg.copy); while (i != msg->sg.end) { msg->sg.data[i] = sge; + __assign_bit(i, msg->sg.copy, sge_copy); sk_msg_iter_var_next(i); sge = tmp; + sge_copy = tmp_copy; tmp = sk_msg_elem_cpy(msg, i); + tmp_copy = test_bit(i, msg->sg.copy); } } @@ -3026,6 +3049,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, struct scatterlist *nsge, *sge = sk_msg_elem(msg, i); int a = start - offset; int b = sge->length - pop - a; + u32 sge_i = i; + bool sge_copy = test_bit(i, msg->sg.copy); sk_msg_iter_var_next(i); @@ -3038,6 +3063,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, sg_set_page(nsge, sg_page(sge), b, sge->offset + pop + a); + __assign_bit(i, msg->sg.copy, sge_copy); } else { struct page *page, *orig; u8 *to, *from; @@ -3054,6 +3080,7 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start, memcpy(to, from, a); memcpy(to + a, from + a + pop, b); sg_set_page(sge, page, a + b, 0); + __clear_bit(sge_i, msg->sg.copy); put_page(orig); } pop = 0; diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index b71ccaec0991..9468bc5ca3f4 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -124,13 +124,13 @@ void gnet_stats_basic_sync_init(struct gnet_stats_basic_sync *b) EXPORT_SYMBOL(gnet_stats_basic_sync_init); static void gnet_stats_add_basic_cpu(struct gnet_stats_basic_sync *bstats, - struct gnet_stats_basic_sync __percpu *cpu) + const struct gnet_stats_basic_sync __percpu *cpu) { u64 t_bytes = 0, t_packets = 0; int i; for_each_possible_cpu(i) { - struct gnet_stats_basic_sync *bcpu = per_cpu_ptr(cpu, i); + const struct gnet_stats_basic_sync *bcpu = per_cpu_ptr(cpu, i); unsigned int start; u64 bytes, packets; @@ -147,8 +147,8 @@ static void gnet_stats_add_basic_cpu(struct gnet_stats_basic_sync *bstats, } void gnet_stats_add_basic(struct gnet_stats_basic_sync *bstats, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, bool running) + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running) { unsigned int start; u64 bytes = 0; @@ -172,8 +172,8 @@ void gnet_stats_add_basic(struct gnet_stats_basic_sync *bstats, EXPORT_SYMBOL(gnet_stats_add_basic); static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, bool running) + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running) { unsigned int start; @@ -182,8 +182,7 @@ static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, int i; for_each_possible_cpu(i) { - struct gnet_stats_basic_sync *bcpu = per_cpu_ptr(cpu, i); - unsigned int start; + const struct gnet_stats_basic_sync *bcpu = per_cpu_ptr(cpu, i); u64 bytes, packets; do { @@ -209,8 +208,8 @@ static void gnet_stats_read_basic(u64 *ret_bytes, u64 *ret_packets, static int ___gnet_stats_copy_basic(struct gnet_dump *d, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, int type, bool running) { u64 bstats_bytes, bstats_packets; @@ -258,8 +257,8 @@ ___gnet_stats_copy_basic(struct gnet_dump *d, */ int gnet_stats_copy_basic(struct gnet_dump *d, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running) { return ___gnet_stats_copy_basic(d, cpu, b, TCA_STATS_BASIC, running); @@ -285,8 +284,8 @@ EXPORT_SYMBOL(gnet_stats_copy_basic); */ int gnet_stats_copy_basic_hw(struct gnet_dump *d, - struct gnet_stats_basic_sync __percpu *cpu, - struct gnet_stats_basic_sync *b, + const struct gnet_stats_basic_sync __percpu *cpu, + const struct gnet_stats_basic_sync *b, bool running) { return ___gnet_stats_copy_basic(d, cpu, b, TCA_STATS_BASIC_HW, running); @@ -345,11 +344,11 @@ static void gnet_stats_add_queue_cpu(struct gnet_stats_queue *qstats, for_each_possible_cpu(i) { const struct gnet_stats_queue *qcpu = per_cpu_ptr(q, i); - qstats->qlen += qcpu->qlen; - qstats->backlog += qcpu->backlog; - qstats->drops += qcpu->drops; - qstats->requeues += qcpu->requeues; - qstats->overlimits += qcpu->overlimits; + qstats->qlen += READ_ONCE(qcpu->qlen); + qstats->backlog += READ_ONCE(qcpu->backlog); + qstats->drops += READ_ONCE(qcpu->drops); + qstats->requeues += READ_ONCE(qcpu->requeues); + qstats->overlimits += READ_ONCE(qcpu->overlimits); } } @@ -360,11 +359,11 @@ void gnet_stats_add_queue(struct gnet_stats_queue *qstats, if (cpu) { gnet_stats_add_queue_cpu(qstats, cpu); } else { - qstats->qlen += q->qlen; - qstats->backlog += q->backlog; - qstats->drops += q->drops; - qstats->requeues += q->requeues; - qstats->overlimits += q->overlimits; + qstats->qlen += READ_ONCE(q->qlen); + qstats->backlog += READ_ONCE(q->backlog); + qstats->drops += READ_ONCE(q->drops); + qstats->requeues += READ_ONCE(q->requeues); + qstats->overlimits += READ_ONCE(q->overlimits); } } EXPORT_SYMBOL(gnet_stats_add_queue); @@ -385,8 +384,8 @@ EXPORT_SYMBOL(gnet_stats_add_queue); */ int gnet_stats_copy_queue(struct gnet_dump *d, - struct gnet_stats_queue __percpu *cpu_q, - struct gnet_stats_queue *q, __u32 qlen) + const struct gnet_stats_queue __percpu *cpu_q, + const struct gnet_stats_queue *q, __u32 qlen) { struct gnet_stats_queue qstats = {0}; diff --git a/net/core/link_watch.c b/net/core/link_watch.c index ff2c1d4538ef..9c35aac8b2e9 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -277,7 +277,7 @@ static bool linkwatch_clean_dev(struct net_device *dev) void __linkwatch_sync_dev(struct net_device *dev) { - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (linkwatch_clean_dev(dev)) { linkwatch_do_dev(dev); diff --git a/net/core/lock_debug.c b/net/core/lock_debug.c index 9e9fb25314b9..8a81c5430705 100644 --- a/net/core/lock_debug.c +++ b/net/core/lock_debug.c @@ -24,7 +24,7 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event, case NETDEV_CHANGE: case NETDEV_REGISTER: case NETDEV_UP: - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); fallthrough; case NETDEV_DOWN: case NETDEV_REBOOT: @@ -65,6 +65,7 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event, break; case NETDEV_CHANGENAME: + netdev_assert_locked_ops(dev); ASSERT_RTNL_NET(net); break; } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 5d9216016507..1349c0eedb64 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -457,7 +457,6 @@ void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) neigh_flush_dev(tbl, dev, false); spin_unlock_bh(&tbl->lock); } -EXPORT_SYMBOL(neigh_changeaddr); static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, bool skip_perm) @@ -484,14 +483,12 @@ int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) __neigh_ifdown(tbl, dev, true); return 0; } -EXPORT_SYMBOL(neigh_carrier_down); int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) { __neigh_ifdown(tbl, dev, false); return 0; } -EXPORT_SYMBOL(neigh_ifdown); static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev, @@ -1652,13 +1649,11 @@ int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) } return err; } -EXPORT_SYMBOL(neigh_connected_output); int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb) { return dev_queue_xmit(skb); } -EXPORT_SYMBOL(neigh_direct_output); static void neigh_managed_work(struct work_struct *work) { @@ -1880,7 +1875,6 @@ void neigh_table_init(int index, struct neigh_table *tbl) rcu_assign_pointer(neigh_tables[index], tbl); } -EXPORT_SYMBOL(neigh_table_init); /* * Only called from ndisc_cleanup(), which means this is dead code @@ -1914,7 +1908,6 @@ int neigh_table_clear(int index, struct neigh_table *tbl) return 0; } -EXPORT_SYMBOL(neigh_table_clear); static struct neigh_table *neigh_find_table(int family) { diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 3318b5666e43..0e71c9ed41e8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -37,12 +37,6 @@ static const char fmt_uint[] = "%u\n"; static const char fmt_ulong[] = "%lu\n"; static const char fmt_u64[] = "%llu\n"; -/* Caller holds RTNL, netdev->lock or RCU */ -static inline int dev_isalive(const struct net_device *dev) -{ - return READ_ONCE(dev->reg_state) <= NETREG_REGISTERED; -} - /* There is a possible ABBA deadlock between rtnl_lock and kernfs_node->active, * when unregistering a net device and accessing associated sysfs files. The * potential deadlock is as follow: diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c index 81aecb5d3bc5..d18c89b5a6c7 100644 --- a/net/core/netdev-genl-gen.c +++ b/net/core/netdev-genl-gen.c @@ -51,14 +51,28 @@ static const struct nla_policy netdev_dev_get_nl_policy[NETDEV_A_DEV_IFINDEX + 1 /* NETDEV_CMD_PAGE_POOL_GET - do */ #ifdef CONFIG_PAGE_POOL -static const struct nla_policy netdev_page_pool_get_nl_policy[NETDEV_A_PAGE_POOL_ID + 1] = { +static const struct nla_policy netdev_page_pool_get_do_nl_policy[NETDEV_A_PAGE_POOL_ID + 1] = { [NETDEV_A_PAGE_POOL_ID] = NLA_POLICY_FULL_RANGE(NLA_UINT, &netdev_a_page_pool_id_range), }; #endif /* CONFIG_PAGE_POOL */ +/* NETDEV_CMD_PAGE_POOL_GET - dump */ +#ifdef CONFIG_PAGE_POOL +static const struct nla_policy netdev_page_pool_get_dump_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1] = { + [NETDEV_A_PAGE_POOL_IFINDEX] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_page_pool_ifindex_range), +}; +#endif /* CONFIG_PAGE_POOL */ + /* NETDEV_CMD_PAGE_POOL_STATS_GET - do */ #ifdef CONFIG_PAGE_POOL_STATS -static const struct nla_policy netdev_page_pool_stats_get_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = { +static const struct nla_policy netdev_page_pool_stats_get_do_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = { + [NETDEV_A_PAGE_POOL_STATS_INFO] = NLA_POLICY_NESTED(netdev_page_pool_info_nl_policy), +}; +#endif /* CONFIG_PAGE_POOL_STATS */ + +/* NETDEV_CMD_PAGE_POOL_STATS_GET - dump */ +#ifdef CONFIG_PAGE_POOL_STATS +static const struct nla_policy netdev_page_pool_stats_get_dump_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = { [NETDEV_A_PAGE_POOL_STATS_INFO] = NLA_POLICY_NESTED(netdev_page_pool_info_nl_policy), }; #endif /* CONFIG_PAGE_POOL_STATS */ @@ -138,28 +152,32 @@ static const struct genl_split_ops netdev_nl_ops[] = { { .cmd = NETDEV_CMD_PAGE_POOL_GET, .doit = netdev_nl_page_pool_get_doit, - .policy = netdev_page_pool_get_nl_policy, + .policy = netdev_page_pool_get_do_nl_policy, .maxattr = NETDEV_A_PAGE_POOL_ID, .flags = GENL_CMD_CAP_DO, }, { - .cmd = NETDEV_CMD_PAGE_POOL_GET, - .dumpit = netdev_nl_page_pool_get_dumpit, - .flags = GENL_CMD_CAP_DUMP, + .cmd = NETDEV_CMD_PAGE_POOL_GET, + .dumpit = netdev_nl_page_pool_get_dumpit, + .policy = netdev_page_pool_get_dump_nl_policy, + .maxattr = NETDEV_A_PAGE_POOL_IFINDEX, + .flags = GENL_CMD_CAP_DUMP, }, #endif /* CONFIG_PAGE_POOL */ #ifdef CONFIG_PAGE_POOL_STATS { .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, .doit = netdev_nl_page_pool_stats_get_doit, - .policy = netdev_page_pool_stats_get_nl_policy, + .policy = netdev_page_pool_stats_get_do_nl_policy, .maxattr = NETDEV_A_PAGE_POOL_STATS_INFO, .flags = GENL_CMD_CAP_DO, }, { - .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, - .dumpit = netdev_nl_page_pool_stats_get_dumpit, - .flags = GENL_CMD_CAP_DUMP, + .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, + .dumpit = netdev_nl_page_pool_stats_get_dumpit, + .policy = netdev_page_pool_stats_get_dump_nl_policy, + .maxattr = NETDEV_A_PAGE_POOL_STATS_INFO, + .flags = GENL_CMD_CAP_DUMP, }, #endif /* CONFIG_PAGE_POOL_STATS */ { @@ -202,7 +220,7 @@ static const struct genl_split_ops netdev_nl_ops[] = { .doit = netdev_nl_bind_rx_doit, .policy = netdev_bind_rx_nl_policy, .maxattr = NETDEV_A_DMABUF_FD, - .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO, }, { .cmd = NETDEV_CMD_NAPI_SET, diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index 119eaa6501d5..11b0b91683d7 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -1077,7 +1077,7 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) goto err_rxq_bitmap; } - binding = net_devmem_bind_dmabuf(netdev, dma_dev, DMA_FROM_DEVICE, + binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE, dmabuf_fd, priv, info->extack); if (IS_ERR(binding)) { err = PTR_ERR(binding); @@ -1117,9 +1117,43 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) return err; } +/* Find the DMA-capable device for a netmem TX binding. + * + * For NETMEM_TX_DMA devices, return the device itself. + * For NETMEM_TX_NO_DMA devices, walk leased RX queues to find the underlying + * physical device and return it. + */ +static struct net_device * +netdev_find_netmem_tx_dev(struct net_device *dev) +{ + struct netdev_rx_queue *lease_rxq; + struct net_device *phys_dev; + int i; + + if (dev->netmem_tx == NETMEM_TX_DMA) + return dev; + + if (dev->netmem_tx != NETMEM_TX_NO_DMA) + return NULL; + + for (i = 0; i < dev->real_num_rx_queues; i++) { + lease_rxq = READ_ONCE(__netif_get_rx_queue(dev, i)->lease); + if (!lease_rxq) + continue; + + phys_dev = lease_rxq->dev; + if (netif_device_present(phys_dev) && + phys_dev->netmem_tx == NETMEM_TX_DMA) + return phys_dev; + } + + return NULL; +} + int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info) { struct net_devmem_dmabuf_binding *binding; + struct net_device *bind_dev; struct netdev_nl_sock *priv; struct net_device *netdev; struct device *dma_dev; @@ -1162,29 +1196,48 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info) goto err_unlock_netdev; } - if (!netdev->netmem_tx) { + if (netdev->netmem_tx == NETMEM_TX_NONE) { err = -EOPNOTSUPP; NL_SET_ERR_MSG(info->extack, "Driver does not support netmem TX"); goto err_unlock_netdev; } - dma_dev = netdev_queue_get_dma_dev(netdev, 0, NETDEV_QUEUE_TYPE_TX); - binding = net_devmem_bind_dmabuf(netdev, dma_dev, DMA_TO_DEVICE, - dmabuf_fd, priv, info->extack); + bind_dev = netdev_find_netmem_tx_dev(netdev); + if (!bind_dev) { + err = -EOPNOTSUPP; + NL_SET_ERR_MSG(info->extack, + "No DMA-capable device found for netmem TX"); + goto err_unlock_netdev; + } + + if (bind_dev != netdev) + netdev_lock(bind_dev); + + dma_dev = netdev_queue_get_dma_dev(bind_dev, 0, NETDEV_QUEUE_TYPE_TX); + + binding = net_devmem_bind_dmabuf(bind_dev, + bind_dev != netdev ? netdev : NULL, + dma_dev, DMA_TO_DEVICE, dmabuf_fd, + priv, info->extack); if (IS_ERR(binding)) { err = PTR_ERR(binding); - goto err_unlock_netdev; + goto err_unlock_bind_dev; } nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id); genlmsg_end(rsp, hdr); + if (bind_dev != netdev) + netdev_unlock(bind_dev); netdev_unlock(netdev); mutex_unlock(&priv->lock); return genlmsg_reply(rsp, info); +err_unlock_bind_dev: + if (bind_dev != netdev) + netdev_unlock(bind_dev); err_unlock_netdev: netdev_unlock(netdev); err_unlock_sock: diff --git a/net/core/netdev_queues.c b/net/core/netdev_queues.c index 73fb28087a93..4d6864bb4f6b 100644 --- a/net/core/netdev_queues.c +++ b/net/core/netdev_queues.c @@ -40,7 +40,7 @@ struct device *netdev_queue_get_dma_dev(struct net_device *dev, struct netdev_rx_queue *hw_rxq; struct device *dma_dev; - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); /* Only RX side supports queue leasing today. */ if (type != NETDEV_QUEUE_TYPE_RX || !netif_rxq_is_leased(dev, idx)) diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c index de4dac4c88b3..00a7011eb4d5 100644 --- a/net/core/netdev_rx_queue.c +++ b/net/core/netdev_rx_queue.c @@ -338,12 +338,12 @@ void __netif_mp_uninstall_rxq(struct netdev_rx_queue *rxq, void netif_rxq_cleanup_unlease(struct netdev_rx_queue *phys_rxq, struct netdev_rx_queue *virt_rxq) { - struct pp_memory_provider_params *p = &phys_rxq->mp_params; unsigned int rxq_idx = get_netdev_rx_queue_index(phys_rxq); + struct pp_memory_provider_params p = phys_rxq->mp_params; - if (!p->mp_ops) + if (!p.mp_ops) return; - __netif_mp_uninstall_rxq(virt_rxq, p); - __netif_mp_close_rxq(phys_rxq->dev, rxq_idx, p); + __netif_mp_close_rxq(phys_rxq->dev, rxq_idx, &p); + __netif_mp_uninstall_rxq(virt_rxq, &p); } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 3f4a17fa5713..229dde818ab3 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -29,12 +29,10 @@ #include #include #include +#include #include -#include #include #include -#include -#include #include #include @@ -43,18 +41,9 @@ * message gets out even in extreme OOM situations. */ -#define MAX_UDP_CHUNK 1460 #define MAX_SKBS 32 #define USEC_PER_POLL 50 -#define MAX_SKB_SIZE \ - (sizeof(struct ethhdr) + \ - sizeof(struct iphdr) + \ - sizeof(struct udphdr) + \ - MAX_UDP_CHUNK) - -static void zap_completion_queue(void); - static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); @@ -201,7 +190,7 @@ void netpoll_poll_dev(struct net_device *dev) up(&ni->dev_lock); - zap_completion_queue(); + netpoll_zap_completion_queue(); } EXPORT_SYMBOL(netpoll_poll_dev); @@ -240,7 +229,7 @@ static void refill_skbs(struct netpoll *np) } } -static void zap_completion_queue(void) +void netpoll_zap_completion_queue(void) { unsigned long flags; struct softnet_data *sd = &get_cpu_var(softnet_data); @@ -267,33 +256,7 @@ static void zap_completion_queue(void) put_cpu_var(softnet_data); } - -static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve) -{ - int count = 0; - struct sk_buff *skb; - - zap_completion_queue(); -repeat: - - skb = alloc_skb(len, GFP_ATOMIC); - if (!skb) { - skb = skb_dequeue(&np->skb_pool); - schedule_work(&np->refill_wq); - } - - if (!skb) { - if (++count < 10) { - netpoll_poll_dev(np->dev); - goto repeat; - } - return NULL; - } - - refcount_set(&skb->users, 1); - skb_reserve(skb, reserve); - return skb; -} +EXPORT_SYMBOL_NS_GPL(netpoll_zap_completion_queue, "NETDEV_INTERNAL"); static int netpoll_owner_active(struct net_device *dev) { @@ -371,31 +334,6 @@ static netdev_tx_t __netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) return ret; } -static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb, - int len) -{ - struct udphdr *udph; - int udp_len; - - udp_len = len + sizeof(struct udphdr); - udph = udp_hdr(skb); - - /* check needs to be set, since it will be consumed in csum_partial */ - udph->check = 0; - if (np->ipv6) - udph->check = csum_ipv6_magic(&np->local_ip.in6, - &np->remote_ip.in6, - udp_len, IPPROTO_UDP, - csum_partial(udph, udp_len, 0)); - else - udph->check = csum_tcpudp_magic(np->local_ip.ip, - np->remote_ip.ip, - udp_len, IPPROTO_UDP, - csum_partial(udph, udp_len, 0)); - if (udph->check == 0) - udph->check = CSUM_MANGLED_0; -} - netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) { unsigned long flags; @@ -413,125 +351,6 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) } EXPORT_SYMBOL(netpoll_send_skb); -static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len) -{ - struct ipv6hdr *ip6h; - - skb_push(skb, sizeof(struct ipv6hdr)); - skb_reset_network_header(skb); - ip6h = ipv6_hdr(skb); - - /* ip6h->version = 6; ip6h->priority = 0; */ - *(unsigned char *)ip6h = 0x60; - ip6h->flow_lbl[0] = 0; - ip6h->flow_lbl[1] = 0; - ip6h->flow_lbl[2] = 0; - - ip6h->payload_len = htons(sizeof(struct udphdr) + len); - ip6h->nexthdr = IPPROTO_UDP; - ip6h->hop_limit = 32; - ip6h->saddr = np->local_ip.in6; - ip6h->daddr = np->remote_ip.in6; - - skb->protocol = htons(ETH_P_IPV6); -} - -static void push_ipv4(struct netpoll *np, struct sk_buff *skb, int len) -{ - static atomic_t ip_ident; - struct iphdr *iph; - int ip_len; - - ip_len = len + sizeof(struct udphdr) + sizeof(struct iphdr); - - skb_push(skb, sizeof(struct iphdr)); - skb_reset_network_header(skb); - iph = ip_hdr(skb); - - /* iph->version = 4; iph->ihl = 5; */ - *(unsigned char *)iph = 0x45; - iph->tos = 0; - put_unaligned(htons(ip_len), &iph->tot_len); - iph->id = htons(atomic_inc_return(&ip_ident)); - iph->frag_off = 0; - iph->ttl = 64; - iph->protocol = IPPROTO_UDP; - iph->check = 0; - put_unaligned(np->local_ip.ip, &iph->saddr); - put_unaligned(np->remote_ip.ip, &iph->daddr); - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); - skb->protocol = htons(ETH_P_IP); -} - -static void push_udp(struct netpoll *np, struct sk_buff *skb, int len) -{ - struct udphdr *udph; - int udp_len; - - udp_len = len + sizeof(struct udphdr); - - skb_push(skb, sizeof(struct udphdr)); - skb_reset_transport_header(skb); - - udph = udp_hdr(skb); - udph->source = htons(np->local_port); - udph->dest = htons(np->remote_port); - udph->len = htons(udp_len); - - netpoll_udp_checksum(np, skb, len); -} - -static void push_eth(struct netpoll *np, struct sk_buff *skb) -{ - struct ethhdr *eth; - - eth = skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - ether_addr_copy(eth->h_source, np->dev->dev_addr); - ether_addr_copy(eth->h_dest, np->remote_mac); - if (np->ipv6) - eth->h_proto = htons(ETH_P_IPV6); - else - eth->h_proto = htons(ETH_P_IP); -} - -int netpoll_send_udp(struct netpoll *np, const char *msg, int len) -{ - int total_len, ip_len, udp_len; - struct sk_buff *skb; - - if (!IS_ENABLED(CONFIG_PREEMPT_RT)) - WARN_ON_ONCE(!irqs_disabled()); - - udp_len = len + sizeof(struct udphdr); - if (np->ipv6) - ip_len = udp_len + sizeof(struct ipv6hdr); - else - ip_len = udp_len + sizeof(struct iphdr); - - total_len = ip_len + LL_RESERVED_SPACE(np->dev); - - skb = find_skb(np, total_len + np->dev->needed_tailroom, - total_len - len); - if (!skb) - return -ENOMEM; - - skb_copy_to_linear_data(skb, msg, len); - skb_put(skb, len); - - push_udp(np, skb, len); - if (np->ipv6) - push_ipv6(np, skb, len); - else - push_ipv4(np, skb, len); - push_eth(np, skb); - skb->dev = np->dev; - - return (int)netpoll_send_skb(np, skb); -} -EXPORT_SYMBOL(netpoll_send_udp); - - static void skb_pool_flush(struct netpoll *np) { struct sk_buff_head *skb_pool; diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 8171d1173221..21dc4a9c8714 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -76,20 +76,20 @@ static const char pp_stats[][ETH_GSTRING_LEN] = { * @pool: pool from which page was allocated * @stats: struct page_pool_stats to fill in * + * Deprecated driver API for querying stats. Page pool stats can be queried + * via netdev Netlink. + * * 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.). */ -bool page_pool_get_stats(const struct page_pool *pool, +void page_pool_get_stats(const struct page_pool *pool, struct page_pool_stats *stats) { int cpu = 0; - if (!stats) - return false; - /* The caller is responsible to initialize stats. */ stats->alloc_stats.fast += pool->alloc_stats.fast; stats->alloc_stats.slow += pool->alloc_stats.slow; @@ -108,8 +108,6 @@ bool page_pool_get_stats(const struct page_pool *pool, stats->recycle_stats.ring_full += pcpu->ring_full; stats->recycle_stats.released_refcnt += pcpu->released_refcnt; } - - return true; } EXPORT_SYMBOL(page_pool_get_stats); diff --git a/net/core/page_pool_user.c b/net/core/page_pool_user.c index ee5060d8eec0..ef4261c0e8ea 100644 --- a/net/core/page_pool_user.c +++ b/net/core/page_pool_user.c @@ -79,7 +79,7 @@ struct page_pool_dump_cb { static int netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, - pp_nl_fill_cb fill) + pp_nl_fill_cb fill, struct nlattr *ifindex_attr) { struct page_pool_dump_cb *state = (void *)cb->ctx; const struct genl_info *info = genl_info_dump(cb); @@ -88,9 +88,17 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, struct page_pool *pool; int err = 0; + if (ifindex_attr) + state->ifindex = nla_get_u32(ifindex_attr); + rtnl_lock(); mutex_lock(&page_pools_lock); for_each_netdev_dump(net, netdev, state->ifindex) { + /* Either the provided ifindex doesn't exist or done dumping */ + if (ifindex_attr && + netdev->ifindex != nla_get_u32(ifindex_attr)) + break; + hlist_for_each_entry(pool, &netdev->page_pools, user.list) { if (state->pp_id && state->pp_id < pool->user.id) continue; @@ -119,14 +127,15 @@ page_pool_nl_stats_fill(struct sk_buff *rsp, const struct page_pool *pool, struct nlattr *nest; void *hdr; - if (!page_pool_get_stats(pool, &stats)) - return 0; + page_pool_get_stats(pool, &stats); hdr = genlmsg_iput(rsp, info); if (!hdr) return -EMSGSIZE; nest = nla_nest_start(rsp, NETDEV_A_PAGE_POOL_STATS_INFO); + if (!nest) + goto err_cancel_msg; if (nla_put_uint(rsp, NETDEV_A_PAGE_POOL_ID, pool->user.id) || (pool->slow.netdev->ifindex != LOOPBACK_IFINDEX && @@ -206,10 +215,40 @@ int netdev_nl_page_pool_stats_get_doit(struct sk_buff *skb, return netdev_nl_page_pool_get_do(info, id, page_pool_nl_stats_fill); } +static const struct netlink_range_validation page_pool_ifindex_range = { + .min = 1ULL, + .max = S32_MAX, +}; + +static const struct nla_policy +page_pool_stat_info_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1] = { + [NETDEV_A_PAGE_POOL_IFINDEX] = + NLA_POLICY_FULL_RANGE(NLA_U32, &page_pool_ifindex_range), +}; + int netdev_nl_page_pool_stats_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { - return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_stats_fill); + struct nlattr *tb[ARRAY_SIZE(page_pool_stat_info_policy)]; + const struct genl_info *info = genl_info_dump(cb); + struct nlattr *ifindex_attr = NULL; + + if (info->attrs[NETDEV_A_PAGE_POOL_STATS_INFO]) { + struct nlattr *nest; + int err; + + nest = info->attrs[NETDEV_A_PAGE_POOL_STATS_INFO]; + err = nla_parse_nested(tb, ARRAY_SIZE(tb) - 1, nest, + page_pool_stat_info_policy, + info->extack); + if (err) + return err; + + ifindex_attr = tb[NETDEV_A_PAGE_POOL_IFINDEX]; + } + + return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_stats_fill, + ifindex_attr); } static int @@ -305,7 +344,10 @@ int netdev_nl_page_pool_get_doit(struct sk_buff *skb, struct genl_info *info) int netdev_nl_page_pool_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { - return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_fill); + const struct genl_info *info = genl_info_dump(cb); + + return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_fill, + info->attrs[NETDEV_A_PAGE_POOL_IFINDEX]); } int page_pool_list(struct page_pool *pool) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 511c25bf6f2a..61d095ce1b3b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -63,7 +63,7 @@ #include "dev.h" #define RTNL_MAX_TYPE 50 -#define RTNL_SLAVE_MAX_TYPE 44 +#define RTNL_SLAVE_MAX_TYPE 45 struct rtnl_link { rtnl_doit_func doit; @@ -750,13 +750,12 @@ static size_t rtnl_link_get_size(const struct net_device *dev) } static LIST_HEAD(rtnl_af_ops); +static DEFINE_SPINLOCK(rtnl_af_ops_lock); static struct rtnl_af_ops *rtnl_af_lookup(const int family, int *srcu_index) { struct rtnl_af_ops *ops; - ASSERT_RTNL(); - rcu_read_lock(); list_for_each_entry_rcu(ops, &rtnl_af_ops, list) { @@ -791,9 +790,9 @@ int rtnl_af_register(struct rtnl_af_ops *ops) if (err) return err; - rtnl_lock(); + spin_lock(&rtnl_af_ops_lock); list_add_tail_rcu(&ops->list, &rtnl_af_ops); - rtnl_unlock(); + spin_unlock(&rtnl_af_ops_lock); return 0; } @@ -805,9 +804,9 @@ EXPORT_SYMBOL_GPL(rtnl_af_register); */ void rtnl_af_unregister(struct rtnl_af_ops *ops) { - rtnl_lock(); + spin_lock(&rtnl_af_ops_lock); list_del_rcu(&ops->list); - rtnl_unlock(); + spin_unlock(&rtnl_af_ops_lock); synchronize_rcu(); synchronize_srcu(&ops->srcu); @@ -1264,11 +1263,11 @@ static size_t rtnl_devlink_port_size(const struct net_device *dev) return size; } -static size_t rtnl_dpll_pin_size(const struct net_device *dev) +static size_t rtnl_dpll_pin_size(void) { size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */ - size += dpll_netdev_pin_handle_size(dev); + size += dpll_netdev_pin_handle_size(); return size; } @@ -1295,7 +1294,12 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, size = NLMSG_ALIGN(sizeof(struct ifinfomsg)) + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ - + nla_total_size(IFALIASZ) /* IFLA_IFALIAS */ + + rtnl_prop_list_size(dev); + + if (ext_filter_mask & RTEXT_FILTER_NAME_ONLY) + return size; + + size += nla_total_size(IFALIASZ) /* IFLA_IFALIAS */ + nla_total_size(IFNAMSIZ) /* IFLA_QDISC */ + nla_total_size_64bit(sizeof(struct rtnl_link_ifmap)) + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ @@ -1342,10 +1346,9 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */ + nla_total_size(4) /* IFLA_MIN_MTU */ + nla_total_size(4) /* IFLA_MAX_MTU */ - + rtnl_prop_list_size(dev) + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */ + rtnl_devlink_port_size(dev) - + rtnl_dpll_pin_size(dev) + + rtnl_dpll_pin_size() + nla_total_size(8) /* IFLA_MAX_PACING_OFFLOAD_HORIZON */ + nla_total_size(2) /* IFLA_HEADROOM */ + nla_total_size(2) /* IFLA_TAILROOM */ @@ -1941,15 +1944,18 @@ static int rtnl_fill_alt_ifnames(struct sk_buff *skb, struct netdev_name_node *name_node; int count = 0; + rcu_read_lock(); list_for_each_entry_rcu(name_node, &dev->name_node->list, list) { - if (nla_put_string(skb, IFLA_ALT_IFNAME, name_node->name)) + if (nla_put_string(skb, IFLA_ALT_IFNAME, name_node->name)) { + rcu_read_unlock(); return -EMSGSIZE; + } count++; } + rcu_read_unlock(); return count; } -/* RCU protected. */ static int rtnl_fill_prop_list(struct sk_buff *skb, const struct net_device *dev) { @@ -1964,7 +1970,10 @@ static int rtnl_fill_prop_list(struct sk_buff *skb, if (ret <= 0) goto nest_cancel; - nla_nest_end(skb, prop_list); + ret = -EMSGSIZE; + if (nla_nest_end_safe(skb, prop_list) < 0) + goto nest_cancel; + return 0; nest_cancel: @@ -2059,7 +2068,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct nlmsghdr *nlh; struct Qdisc *qdisc; - ASSERT_RTNL(); nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); if (nlh == NULL) return -EMSGSIZE; @@ -2072,13 +2080,21 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, ifm->ifi_flags = netif_get_flags(dev); ifm->ifi_change = change; - if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid)) - goto nla_put_failure; - netdev_copy_name(dev, devname); if (nla_put_string(skb, IFLA_IFNAME, devname)) goto nla_put_failure; + if (rtnl_fill_prop_list(skb, dev)) + goto nla_put_failure; + + if (ext_filter_mask & RTEXT_FILTER_NAME_ONLY) + goto end; + + ASSERT_RTNL(); + if (tgt_netnsid >= 0 && + nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid)) + goto nla_put_failure; + if (nla_put_u32(skb, IFLA_TXQLEN, READ_ONCE(dev->tx_queue_len)) || nla_put_u8(skb, IFLA_OPERSTATE, netif_running(dev) ? READ_ONCE(dev->operstate) : @@ -2191,8 +2207,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, goto nla_put_failure_rcu; if (rtnl_fill_link_ifmap(skb, dev)) goto nla_put_failure_rcu; - if (rtnl_fill_prop_list(skb, dev)) - goto nla_put_failure_rcu; rcu_read_unlock(); if (dev->dev.parent && @@ -2211,6 +2225,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, if (rtnl_fill_dpll_pin(skb, dev)) goto nla_put_failure; +end: nlmsg_end(skb, nlh); return 0; @@ -2356,22 +2371,24 @@ static struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla, static bool link_master_filtered(struct net_device *dev, int master_idx) { struct net_device *master; + bool res = false; if (!master_idx) return false; - master = netdev_master_upper_dev_get(dev); + rcu_read_lock(); + master = netdev_master_upper_dev_get_rcu(dev); /* 0 is already used to denote IFLA_MASTER wasn't passed, therefore need * another invalid value for ifindex to denote "no master". */ if (master_idx == -1) - return !!master; + res = !!master; + else if (!master || master->ifindex != master_idx) + res = true; + rcu_read_unlock(); - if (!master || master->ifindex != master_idx) - return true; - - return false; + return res; } static bool link_kind_filtered(const struct net_device *dev, @@ -2482,6 +2499,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) int ops_srcu_index; int master_idx = 0; int netnsid = -1; + bool need_rtnl; int err, i; err = rtnl_valid_dump_ifinfo_req(nlh, cb->strict_check, tb, extack); @@ -2531,6 +2549,12 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) walk_entries: err = 0; + need_rtnl = !(ext_filter_mask & RTEXT_FILTER_NAME_ONLY); + if (need_rtnl) + rtnl_lock(); + else + rcu_read_lock(); + for_each_netdev_dump(tgt_net, dev, ctx->ifindex) { if (link_dump_filtered(dev, master_idx, kind_ops)) continue; @@ -2542,11 +2566,13 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) if (err < 0) break; } - - - cb->seq = tgt_net->dev_base_seq; + cb->seq = READ_ONCE(tgt_net->dev_base_seq); nl_dump_check_consistent(cb, nlmsg_hdr(skb)); + if (need_rtnl) + rtnl_unlock(); + else + rcu_read_unlock(); out: if (kind_ops) @@ -3453,6 +3479,21 @@ static struct net_device *rtnl_dev_get(struct net *net, return __dev_get_by_name(net, ifname); } +static struct net_device *rtnl_dev_get_rcu(struct net *net, + struct nlattr *tb[]) +{ + char ifname[ALTIFNAMSIZ]; + + if (tb[IFLA_IFNAME]) + nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); + else if (tb[IFLA_ALT_IFNAME]) + nla_strscpy(ifname, tb[IFLA_ALT_IFNAME], ALTIFNAMSIZ); + else + return NULL; + + return dev_get_by_name_rcu(net, ifname); +} + static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -4172,14 +4213,16 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); - struct net *tgt_net = net; - struct ifinfomsg *ifm; - struct nlattr *tb[IFLA_MAX+1]; + struct nlattr *tb[IFLA_MAX + 1]; + netdevice_tracker dev_tracker; struct net_device *dev = NULL; + struct net *tgt_net = net; + u32 ext_filter_mask = 0; + struct ifinfomsg *ifm; struct sk_buff *nskb; int netnsid = -1; + bool need_rtnl; int err; - u32 ext_filter_mask = 0; err = rtnl_valid_getlink_req(skb, nlh, tb, extack); if (err < 0) @@ -4199,43 +4242,71 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, if (tb[IFLA_EXT_MASK]) ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); - err = -EINVAL; ifm = nlmsg_data(nlh); - if (ifm->ifi_index > 0) - dev = __dev_get_by_index(tgt_net, ifm->ifi_index); - else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME]) - dev = rtnl_dev_get(tgt_net, tb); - else + rcu_read_lock(); + if (ifm->ifi_index > 0) { + dev = dev_get_by_index_rcu(tgt_net, ifm->ifi_index); + } else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME]) { + dev = rtnl_dev_get_rcu(tgt_net, tb); + } else { + rcu_read_unlock(); + err = -EINVAL; goto out; + } + netdev_hold(dev, &dev_tracker, GFP_ATOMIC); + rcu_read_unlock(); err = -ENODEV; if (dev == NULL) goto out; + need_rtnl = !(ext_filter_mask & RTEXT_FILTER_NAME_ONLY); + +retry: + if (need_rtnl) { + rtnl_lock(); + if (!dev_isalive(dev)) { + err = -ENODEV; + nskb = NULL; + goto unlock; + } + /* Synchronize the carrier state so we don't report a state + * that we're not actually going to honour immediately; if + * the driver just did a carrier off->on transition, we can + * only TX if link watch work has run, but without this we'd + * already report carrier on, even if it doesn't work yet. + */ + linkwatch_sync_dev(dev); + } + err = -ENOBUFS; nskb = nlmsg_new_large(if_nlmsg_size(dev, ext_filter_mask)); - if (nskb == NULL) - goto out; + if (nskb) + err = rtnl_fill_ifinfo(nskb, dev, net, + RTM_NEWLINK, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, 0, 0, ext_filter_mask, + 0, NULL, 0, netnsid, GFP_KERNEL); - /* Synchronize the carrier state so we don't report a state - * that we're not actually going to honour immediately; if - * the driver just did a carrier off->on transition, we can - * only TX if link watch work has run, but without this we'd - * already report carrier on, even if it doesn't work yet. - */ - linkwatch_sync_dev(dev); +unlock: + if (need_rtnl) + rtnl_unlock(); - err = rtnl_fill_ifinfo(nskb, dev, net, - RTM_NEWLINK, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, 0, 0, ext_filter_mask, - 0, NULL, 0, netnsid, GFP_KERNEL); if (err < 0) { - /* -EMSGSIZE implies BUG in if_nlmsg_size */ - WARN_ON(err == -EMSGSIZE); kfree_skb(nskb); - } else + if (err == -EMSGSIZE) { + if (!need_rtnl) { + /* Some altnames were added, retry with RTNL. */ + need_rtnl = true; + goto retry; + } + /* -EMSGSIZE implies BUG in if_nlmsg_size */ + WARN_ON_ONCE(1); + } + } else { err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid); + } out: + netdev_put(dev, &dev_tracker); if (netnsid >= 0) put_net(tgt_net); @@ -7102,7 +7173,10 @@ static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst = {.msgtype = RTM_DELLINK, .doit = rtnl_dellink, .flags = RTNL_FLAG_DOIT_PERNET_WIP}, {.msgtype = RTM_GETLINK, .doit = rtnl_getlink, - .dumpit = rtnl_dump_ifinfo, .flags = RTNL_FLAG_DUMP_SPLIT_NLM_DONE}, + .dumpit = rtnl_dump_ifinfo, + .flags = RTNL_FLAG_DUMP_SPLIT_NLM_DONE | + RTNL_FLAG_DOIT_UNLOCKED | + RTNL_FLAG_DUMP_UNLOCKED}, {.msgtype = RTM_SETLINK, .doit = rtnl_setlink, .flags = RTNL_FLAG_DOIT_PERNET_WIP}, {.msgtype = RTM_GETADDR, .dumpit = rtnl_dump_all}, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 77261a4b8ea0..18dabb4e9cfa 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -78,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -96,7 +97,6 @@ #include "devmem.h" #include "net-sysfs.h" #include "netmem_priv.h" -#include "sock_destructor.h" #ifdef CONFIG_SKB_EXTENSIONS static struct kmem_cache *skbuff_ext_cache __ro_after_init; @@ -1208,7 +1208,7 @@ void __kfree_skb(struct sk_buff *skb) EXPORT_SYMBOL(__kfree_skb); static __always_inline -bool __sk_skb_reason_drop(struct sock *sk, struct sk_buff *skb, +bool __sk_skb_reason_drop(const struct sock *sk, struct sk_buff *skb, enum skb_drop_reason reason) { if (unlikely(!skb_unref(skb))) @@ -1237,7 +1237,8 @@ bool __sk_skb_reason_drop(struct sock *sk, struct sk_buff *skb, * 'kfree_skb' tracepoint. */ void __fix_address -sk_skb_reason_drop(struct sock *sk, struct sk_buff *skb, enum skb_drop_reason reason) +sk_skb_reason_drop(const struct sock *sk, struct sk_buff *skb, + enum skb_drop_reason reason) { if (__sk_skb_reason_drop(sk, skb, reason)) __kfree_skb(skb); diff --git a/net/core/skmsg.c b/net/core/skmsg.c index e1850caf1a71..2521b643fa05 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -7,7 +7,6 @@ #include #include -#include #include static bool sk_msg_try_coalesce_ok(struct sk_msg *msg, int elem_first_coalesce) @@ -66,6 +65,7 @@ int sk_msg_alloc(struct sock *sk, struct sk_msg *msg, int len, sge = &msg->sg.data[msg->sg.end]; sg_unmark_end(sge); sg_set_page(sge, pfrag->page, use, orig_offset); + __clear_bit(msg->sg.end, msg->sg.copy); get_page(pfrag->page); sk_msg_iter_next(msg, end); } @@ -186,6 +186,7 @@ static int sk_msg_free_elem(struct sock *sk, struct sk_msg *msg, u32 i, sk_mem_uncharge(sk, len); put_page(sg_page(sge)); } + __clear_bit(i, msg->sg.copy); memset(sge, 0, sizeof(*sge)); return len; } @@ -992,41 +993,6 @@ static int sk_psock_skb_redirect(struct sk_psock *from, struct sk_buff *skb) return 0; } -static void sk_psock_tls_verdict_apply(struct sk_buff *skb, - struct sk_psock *from, int verdict) -{ - switch (verdict) { - case __SK_REDIRECT: - sk_psock_skb_redirect(from, skb); - break; - case __SK_PASS: - case __SK_DROP: - default: - break; - } -} - -int sk_psock_tls_strp_read(struct sk_psock *psock, struct sk_buff *skb) -{ - struct bpf_prog *prog; - int ret = __SK_PASS; - - rcu_read_lock(); - prog = READ_ONCE(psock->progs.stream_verdict); - if (likely(prog)) { - skb->sk = psock->sk; - skb_dst_drop(skb); - skb_bpf_redirect_clear(skb); - ret = bpf_prog_run_pin_on_cpu(prog, skb); - ret = sk_psock_map_verd(ret, skb_bpf_redirect_fetch(skb)); - skb->sk = NULL; - } - sk_psock_tls_verdict_apply(skb, psock, ret); - rcu_read_unlock(); - return ret; -} -EXPORT_SYMBOL_GPL(sk_psock_tls_strp_read); - static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb, int verdict) { @@ -1167,13 +1133,9 @@ static void sk_psock_strp_data_ready(struct sock *sk) rcu_read_lock(); psock = sk_psock(sk); if (likely(psock)) { - if (tls_sw_has_ctx_rx(sk)) { - psock->saved_data_ready(sk); - } else { - read_lock_bh(&sk->sk_callback_lock); - strp_data_ready(&psock->strp); - read_unlock_bh(&sk->sk_callback_lock); - } + read_lock_bh(&sk->sk_callback_lock); + strp_data_ready(&psock->strp); + read_unlock_bh(&sk->sk_callback_lock); } rcu_read_unlock(); } @@ -1275,12 +1237,6 @@ static void sk_psock_verdict_data_ready(struct sock *sk) trace_sk_data_ready(sk); rcu_read_lock(); - psock = sk_psock(sk); - if (psock && tls_sw_has_ctx_rx(sk)) { - psock->saved_data_ready(sk); - rcu_read_unlock(); - return; - } sock = READ_ONCE(sk->sk_socket); if (likely(sock)) ops = READ_ONCE(sock->ops); diff --git a/net/core/sock.c b/net/core/sock.c index cab041b57d28..8a59bfaa8096 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2717,6 +2717,7 @@ EXPORT_SYMBOL(sock_wfree); /* This variant of sock_wfree() is used by TCP, * since it sets SOCK_USE_WRITE_QUEUE. */ +#ifdef CONFIG_INET void __sock_wfree(struct sk_buff *skb) { struct sock *sk = skb->sk; @@ -2724,6 +2725,8 @@ void __sock_wfree(struct sk_buff *skb) if (refcount_sub_and_test(skb->truesize, &sk->sk_wmem_alloc)) __sk_free(sk); } +EXPORT_SYMBOL_GPL(__sock_wfree); +#endif void skb_set_owner_w(struct sk_buff *skb, struct sock *sk) { @@ -3047,12 +3050,42 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg, sockc->tsflags |= tsflags; break; case SCM_TXTIME: + { + ktime_t tmin; + u64 txtime; + if (!sock_flag(sk, SOCK_TXTIME)) return -EINVAL; if (cmsg->cmsg_len != CMSG_LEN(sizeof(u64))) return -EINVAL; - sockc->transmit_time = get_unaligned((u64 *)CMSG_DATA(cmsg)); + + txtime = get_unaligned((u64 *)CMSG_DATA(cmsg)); + + /* Allow sending without a delivery time: zero special case */ + if (!txtime) { + sockc->transmit_time = 0; + break; + } + + switch (sk->sk_clockid) { + case CLOCK_MONOTONIC: + tmin = 1; + break; + case CLOCK_REALTIME: + tmin = max(ktime_mono_to_real(0), 1); + break; + case CLOCK_TAI: + tmin = max(ktime_mono_to_any(0, TK_OFFS_TAI), 1); + break; + default: + tmin = 1; + WARN_ON_ONCE(1); + break; + } + + sockc->transmit_time = max_t(ktime_t, txtime, tmin); break; + } case SCM_TS_OPT_ID: if (sk_is_tcp(sk)) return -EINVAL; diff --git a/net/core/sock_destructor.h b/net/core/sock_destructor.h deleted file mode 100644 index 2f396e6bfba5..000000000000 --- a/net/core/sock_destructor.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef _NET_CORE_SOCK_DESTRUCTOR_H -#define _NET_CORE_SOCK_DESTRUCTOR_H -#include - -static inline bool is_skb_wmem(const struct sk_buff *skb) -{ - return skb->destructor == sock_wfree || - skb->destructor == __sock_wfree || - (IS_ENABLED(CONFIG_INET) && skb->destructor == tcp_wfree); -} -#endif diff --git a/net/core/stream.c b/net/core/stream.c index 7a37e7dd2c43..2d748581862d 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -112,7 +112,6 @@ void sk_stream_wait_close(struct sock *sk, long timeout) remove_wait_queue(sk_sleep(sk), &wait); } } -EXPORT_SYMBOL(sk_stream_wait_close); /** * sk_stream_wait_memory - Wait for more memory for a socket diff --git a/net/devlink/netlink_gen.c b/net/devlink/netlink_gen.c index 81899786fd98..f52b0c2b19ed 100644 --- a/net/devlink/netlink_gen.c +++ b/net/devlink/netlink_gen.c @@ -37,6 +37,8 @@ devlink_attr_param_type_validate(const struct nlattr *attr, case DEVLINK_VAR_ATTR_TYPE_NUL_STRING: fallthrough; case DEVLINK_VAR_ATTR_TYPE_BINARY: + fallthrough; + case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY: return 0; } NL_SET_ERR_MSG_ATTR(extack, attr, "invalid enum value"); diff --git a/net/devlink/param.c b/net/devlink/param.c index cf95268da5b0..3e9d2e5750c2 100644 --- a/net/devlink/param.c +++ b/net/devlink/param.c @@ -216,28 +216,28 @@ static int devlink_param_reset_default(struct devlink *devlink, static int devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type, - int nla_type, union devlink_param_value val, + int nla_type, union devlink_param_value *val, bool flag_as_u8) { switch (type) { case DEVLINK_PARAM_TYPE_U8: - if (nla_put_u8(msg, nla_type, val.vu8)) + if (nla_put_u8(msg, nla_type, val->vu8)) return -EMSGSIZE; break; case DEVLINK_PARAM_TYPE_U16: - if (nla_put_u16(msg, nla_type, val.vu16)) + if (nla_put_u16(msg, nla_type, val->vu16)) return -EMSGSIZE; break; case DEVLINK_PARAM_TYPE_U32: - if (nla_put_u32(msg, nla_type, val.vu32)) + if (nla_put_u32(msg, nla_type, val->vu32)) return -EMSGSIZE; break; case DEVLINK_PARAM_TYPE_U64: - if (devlink_nl_put_u64(msg, nla_type, val.vu64)) + if (devlink_nl_put_u64(msg, nla_type, val->vu64)) return -EMSGSIZE; break; case DEVLINK_PARAM_TYPE_STRING: - if (nla_put_string(msg, nla_type, val.vstr)) + if (nla_put_string(msg, nla_type, val->vstr)) return -EMSGSIZE; break; case DEVLINK_PARAM_TYPE_BOOL: @@ -245,10 +245,19 @@ devlink_nl_param_value_put(struct sk_buff *msg, enum devlink_param_type type, * false can be distinguished from not present */ if (flag_as_u8) { - if (nla_put_u8(msg, nla_type, val.vbool)) + if (nla_put_u8(msg, nla_type, val->vbool)) return -EMSGSIZE; } else { - if (val.vbool && nla_put_flag(msg, nla_type)) + if (val->vbool && nla_put_flag(msg, nla_type)) + return -EMSGSIZE; + } + break; + case DEVLINK_PARAM_TYPE_U64_ARRAY: + if (val->u64arr.size > __DEVLINK_PARAM_MAX_ARRAY_SIZE) + return -EMSGSIZE; + + for (int i = 0; i < val->u64arr.size; i++) { + if (nla_put_uint(msg, nla_type, val->u64arr.val[i])) return -EMSGSIZE; } break; @@ -260,8 +269,8 @@ static int devlink_nl_param_value_fill_one(struct sk_buff *msg, enum devlink_param_type type, enum devlink_param_cmode cmode, - union devlink_param_value val, - union devlink_param_value default_val, + union devlink_param_value *val, + union devlink_param_value *default_val, bool has_default) { struct nlattr *param_value_attr; @@ -304,56 +313,79 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, u32 portid, u32 seq, int flags, struct netlink_ext_ack *extack) { - union devlink_param_value default_value[DEVLINK_PARAM_CMODE_MAX + 1]; - union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1]; bool default_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {}; bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {}; const struct devlink_param *param = param_item->param; - struct devlink_param_gset_ctx ctx; + union devlink_param_value *default_value; + union devlink_param_value *param_value; + struct devlink_param_gset_ctx *ctx; struct nlattr *param_values_list; struct nlattr *param_attr; void *hdr; int err; int i; + default_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1, + sizeof(*default_value), GFP_KERNEL); + if (!default_value) + return -ENOMEM; + + param_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1, + sizeof(*param_value), GFP_KERNEL); + if (!param_value) { + kfree(default_value); + return -ENOMEM; + } + + ctx = kzalloc_obj(*ctx); + if (!ctx) { + kfree(param_value); + kfree(default_value); + return -ENOMEM; + } + /* Get value from driver part to driverinit configuration mode */ for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) { if (!devlink_param_cmode_is_supported(param, i)) continue; if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) { - if (param_item->driverinit_value_new_valid) + if (param_item->driverinit_value_new_valid) { param_value[i] = param_item->driverinit_value_new; - else if (param_item->driverinit_value_valid) + } else if (param_item->driverinit_value_valid) { param_value[i] = param_item->driverinit_value; - else - return -EOPNOTSUPP; + } else { + err = -EOPNOTSUPP; + goto get_put_fail; + } if (param_item->driverinit_value_valid) { default_value[i] = param_item->driverinit_default; default_value_set[i] = true; } } else { - ctx.cmode = i; - err = devlink_param_get(devlink, param, &ctx, extack); + ctx->cmode = i; + err = devlink_param_get(devlink, param, ctx, extack); if (err) - return err; - param_value[i] = ctx.val; + goto get_put_fail; - err = devlink_param_get_default(devlink, param, &ctx, + param_value[i] = ctx->val; + + err = devlink_param_get_default(devlink, param, ctx, extack); if (!err) { - default_value[i] = ctx.val; + default_value[i] = ctx->val; default_value_set[i] = true; } else if (err != -EOPNOTSUPP) { - return err; + goto get_put_fail; } } param_value_set[i] = true; } + err = -EMSGSIZE; hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); if (!hdr) - return -EMSGSIZE; + goto get_put_fail; if (devlink_nl_put_handle(msg, devlink)) goto genlmsg_cancel; @@ -383,8 +415,8 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, if (!param_value_set[i]) continue; err = devlink_nl_param_value_fill_one(msg, param->type, - i, param_value[i], - default_value[i], + i, ¶m_value[i], + &default_value[i], default_value_set[i]); if (err) goto values_list_nest_cancel; @@ -393,6 +425,9 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, nla_nest_end(msg, param_values_list); nla_nest_end(msg, param_attr); genlmsg_end(msg, hdr); + kfree(default_value); + kfree(param_value); + kfree(ctx); return 0; values_list_nest_cancel: @@ -401,7 +436,11 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, nla_nest_cancel(msg, param_attr); genlmsg_cancel: genlmsg_cancel(msg, hdr); - return -EMSGSIZE; +get_put_fail: + kfree(default_value); + kfree(param_value); + kfree(ctx); + return err; } static void devlink_param_notify(struct devlink *devlink, @@ -507,7 +546,7 @@ devlink_param_value_get_from_info(const struct devlink_param *param, union devlink_param_value *value) { struct nlattr *param_data; - int len; + int len, cnt, rem; param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]; @@ -547,6 +586,28 @@ devlink_param_value_get_from_info(const struct devlink_param *param, return -EINVAL; value->vbool = nla_get_flag(param_data); break; + + case DEVLINK_PARAM_TYPE_U64_ARRAY: + cnt = 0; + nla_for_each_attr_type(param_data, + DEVLINK_ATTR_PARAM_VALUE_DATA, + genlmsg_data(info->genlhdr), + genlmsg_len(info->genlhdr), rem) { + if (cnt >= __DEVLINK_PARAM_MAX_ARRAY_SIZE) + return -EMSGSIZE; + + if ((nla_len(param_data) != sizeof(u64)) && + (nla_len(param_data) != sizeof(u32))) { + NL_SET_BAD_ATTR(info->extack, param_data); + return -EINVAL; + } + + value->u64arr.val[cnt] = nla_get_uint(param_data); + cnt++; + } + + value->u64arr.size = cnt; + break; } return 0; } @@ -621,7 +682,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink, if (err) return err; if (param->validate) { - err = param->validate(devlink, param->id, value, + err = param->validate(devlink, param->id, &value, info->extack); if (err) return err; @@ -888,7 +949,7 @@ EXPORT_SYMBOL_GPL(devl_param_driverinit_value_get); * configuration mode default value. */ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id, - union devlink_param_value init_val) + union devlink_param_value *init_val) { struct devlink_param_item *param_item; @@ -902,9 +963,9 @@ void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id, DEVLINK_PARAM_CMODE_DRIVERINIT))) return; - param_item->driverinit_value = init_val; + param_item->driverinit_value = *init_val; param_item->driverinit_value_valid = true; - param_item->driverinit_default = init_val; + param_item->driverinit_default = *init_val; devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW); } diff --git a/net/devlink/resource.c b/net/devlink/resource.c index 3d2f42bc2fb5..574108ccfe5d 100644 --- a/net/devlink/resource.c +++ b/net/devlink/resource.c @@ -486,6 +486,8 @@ __devl_resource_register(struct devlink *devlink, struct list_head *resource_list; bool top_hierarchy; + WARN_ON(resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP); + lockdep_assert_held(&devlink->lock); top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP; diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index e1c09d7b8200..14bee83cbe22 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -64,46 +64,27 @@ int dns_query(struct net *net, const char *options, char **_result, time64_t *_expiry, bool invalidate) { - struct key *rkey; struct user_key_payload *upayload; - size_t typelen, desclen; - char *desc, *cp; + struct key *rkey; int ret, len; + char *desc; kenter("%s,%*.*s,%zu,%s", type, (int)namelen, (int)namelen, name, namelen, options); - if (!name || namelen == 0) + if (!name || namelen < 3 || namelen > 255) + return -EINVAL; + if (type && *type == '\0') return -EINVAL; /* construct the query key description as "[:]" */ - typelen = 0; - desclen = 0; - if (type) { - typelen = strlen(type); - if (typelen < 1) - return -EINVAL; - desclen += typelen + 1; - } - - if (namelen < 3 || namelen > 255) - return -EINVAL; - desclen += namelen + 1; - - desc = kmalloc(desclen, GFP_KERNEL); + if (type) + desc = kasprintf(GFP_KERNEL, "%s:%.*s", type, (int)namelen, name); + else + desc = kmemdup_nul(name, namelen, GFP_KERNEL); if (!desc) return -ENOMEM; - cp = desc; - if (type) { - memcpy(cp, type, typelen); - cp += typelen; - *cp++ = ':'; - } - memcpy(cp, name, namelen); - cp += namelen; - *cp = '\0'; - if (!options) options = ""; kdebug("call request_key(,%s,%s)", desc, options); diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 5ed8c704636d..d5e725b90d78 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -125,6 +125,16 @@ config NET_DSA_TAG_KSZ Say Y if you want to enable support for tagging frames for the Microchip 8795/937x/9477/9893 families of switches. +config NET_DSA_TAG_NETC + tristate "Tag driver for NXP NETC switches" + help + Say Y or M if you want to enable support for the NXP Switch Tag (NST), + as implemented by NXP NETC switches having version 4.3 or later. The + switch tag is a proprietary header added to frames after the source + MAC address, it has 3 types and each type has different subtypes, so + its length depends on the type and subtype of the tag, the maximum + length is 14 bytes. + config NET_DSA_TAG_OCELOT tristate "Tag driver for Ocelot family of switches, using NPI port" select PACKING diff --git a/net/dsa/Makefile b/net/dsa/Makefile index bf7247759a64..b8c2667cd14a 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o +obj-$(CONFIG_NET_DSA_TAG_NETC) += tag_netc.o obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o diff --git a/net/dsa/tag_netc.c b/net/dsa/tag_netc.c new file mode 100644 index 000000000000..ccedfe3a80b6 --- /dev/null +++ b/net/dsa/tag_netc.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025-2026 NXP + */ + +#include + +#include "tag.h" + +#define NETC_NAME "nxp_netc" + +/* Forward NXP switch tag */ +#define NETC_TAG_FORWARD 0 + +/* To_Port NXP switch tag */ +#define NETC_TAG_TO_PORT 1 +/* SubType0: No request to perform timestamping */ +#define NETC_TAG_TP_SUBTYPE0 0 + +/* To_Host NXP switch tag */ +#define NETC_TAG_TO_HOST 2 +/* SubType0: frames redirected or copied to CPU port */ +#define NETC_TAG_TH_SUBTYPE0 0 +/* SubType1: frames redirected or copied to CPU port with timestamp */ +#define NETC_TAG_TH_SUBTYPE1 1 +/* SubType2: Transmit timestamp response (two-step timestamping) */ +#define NETC_TAG_TH_SUBTYPE2 2 + +/* NETC switch tag lengths */ +#define NETC_TAG_FORWARD_LEN 6 +#define NETC_TAG_TP_SUBTYPE0_LEN 6 +#define NETC_TAG_TH_SUBTYPE0_LEN 6 +#define NETC_TAG_TH_SUBTYPE1_LEN 14 +#define NETC_TAG_TH_SUBTYPE2_LEN 14 +#define NETC_TAG_CMN_LEN 5 + +#define NETC_TAG_SUBTYPE GENMASK(3, 0) +#define NETC_TAG_TYPE GENMASK(7, 4) +#define NETC_TAG_QV BIT(0) +#define NETC_TAG_IPV GENMASK(4, 2) +#define NETC_TAG_SWITCH GENMASK(2, 0) +#define NETC_TAG_PORT GENMASK(7, 3) + +struct netc_tag_cmn { + __be16 tpid; + u8 type; + u8 qos; + u8 switch_port; +} __packed; + +static void netc_fill_common_tag(struct netc_tag_cmn *tag, u8 type, + u8 subtype, u8 sw_id, u8 port, u8 ipv) +{ + tag->tpid = htons(ETH_P_NXP_NETC); + tag->type = FIELD_PREP(NETC_TAG_TYPE, type) | + FIELD_PREP(NETC_TAG_SUBTYPE, subtype); + tag->qos = NETC_TAG_QV | FIELD_PREP(NETC_TAG_IPV, ipv); + tag->switch_port = FIELD_PREP(NETC_TAG_SWITCH, sw_id) | + FIELD_PREP(NETC_TAG_PORT, port); +} + +static void *netc_fill_common_tp_tag(struct sk_buff *skb, + struct net_device *ndev, + u8 subtype, int tag_len) +{ + struct dsa_port *dp = dsa_user_to_port(ndev); + u16 queue = skb_get_queue_mapping(skb); + s8 ipv = netdev_txq_to_tc(ndev, queue); + void *tag; + + if (unlikely(ipv < 0)) + ipv = 0; + + skb_push(skb, tag_len); + dsa_alloc_etype_header(skb, tag_len); + + tag = dsa_etype_header_pos_tx(skb); + memset(tag + NETC_TAG_CMN_LEN, 0, tag_len - NETC_TAG_CMN_LEN); + /* As 'dsa,member' is a required property for NETC switch, the member + * is used to specify the switch ID (thus the hardware switch ID and + * the software switch ID are consistent), its range is 1 ~ 7. The + * NETC switch driver will check this value, and if it is invalid, + * the switch driver will fail the probe. + * In addition, according to the nxp,netc-switch.yaml doc, the port + * index will not be greater than 0xf. + */ + netc_fill_common_tag(tag, NETC_TAG_TO_PORT, subtype, + dp->ds->index, dp->index, ipv); + + return tag; +} + +static void netc_fill_tp_tag_subtype0(struct sk_buff *skb, + struct net_device *ndev) +{ + netc_fill_common_tp_tag(skb, ndev, NETC_TAG_TP_SUBTYPE0, + NETC_TAG_TP_SUBTYPE0_LEN); +} + +/* Currently only support To_Port tag, subtype 0 */ +static struct sk_buff *netc_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + netc_fill_tp_tag_subtype0(skb, ndev); + + return skb; +} + +static int netc_get_rx_tag_len(int type, int subtype) +{ + /* Only NETC_TAG_TO_HOST and NETC_TAG_FORWARD are expected in RX, + * NETC_TAG_TO_PORT is a TX switch tag that does not exist in RX. + */ + if (type == NETC_TAG_TO_HOST) { + if (subtype == NETC_TAG_TH_SUBTYPE1) + return NETC_TAG_TH_SUBTYPE1_LEN; + else if (subtype == NETC_TAG_TH_SUBTYPE2) + return NETC_TAG_TH_SUBTYPE2_LEN; + else + return NETC_TAG_TH_SUBTYPE0_LEN; + } + + return NETC_TAG_FORWARD_LEN; +} + +static struct sk_buff *netc_rcv(struct sk_buff *skb, + struct net_device *ndev) +{ + struct netc_tag_cmn *tag_cmn; + int tag_len, sw_id, port; + int type, subtype; + + if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN))) + return NULL; + + tag_cmn = dsa_etype_header_pos_rx(skb); + if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) { + dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n", + ntohs(tag_cmn->tpid)); + + return NULL; + } + + if (tag_cmn->qos & NETC_TAG_QV) + skb->priority = FIELD_GET(NETC_TAG_IPV, tag_cmn->qos); + + sw_id = FIELD_GET(NETC_TAG_SWITCH, tag_cmn->switch_port); + /* ENETC VEPA switch ID (0) is not supported yet */ + if (!sw_id) { + dev_warn_ratelimited(&ndev->dev, + "VEPA switch ID is not supported yet\n"); + + return NULL; + } + + port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port); + skb->dev = dsa_conduit_find_user(ndev, sw_id, port); + if (!skb->dev) + return NULL; + + type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type); + subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type); + if (type == NETC_TAG_FORWARD) { + dsa_default_offload_fwd_mark(skb); + } else if (type == NETC_TAG_TO_HOST) { + /* Currently only subtype0 supported */ + if (subtype != NETC_TAG_TH_SUBTYPE0) + return NULL; + } else { + dev_warn_ratelimited(&ndev->dev, + "Unexpected tag type %d\n", type); + return NULL; + } + + /* Remove Switch tag from the frame */ + tag_len = netc_get_rx_tag_len(type, subtype); + skb_pull_rcsum(skb, tag_len); + dsa_strip_etype_header(skb, tag_len); + + return skb; +} + +static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto, + int *offset) +{ + struct netc_tag_cmn *tag_cmn = (struct netc_tag_cmn *)(skb->data - 2); + int subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type); + int type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type); + int tag_len = netc_get_rx_tag_len(type, subtype); + + /* The RX minimum frame length of the NETC switch port is 64 bytes, + * and the frame is received by the ENETC driver. From the hardware + * perspective, the receive buffer of RX BD is at least 128 bytes, + * so the switch tag header is guaranteed to be in the linear region + * of the skb. + */ + *offset = tag_len; + *proto = ((__be16 *)skb->data)[(tag_len / 2) - 1]; +} + +static const struct dsa_device_ops netc_netdev_ops = { + .name = NETC_NAME, + .proto = DSA_TAG_PROTO_NETC, + .xmit = netc_xmit, + .rcv = netc_rcv, + .needed_headroom = NETC_TAG_MAX_LEN, + .flow_dissect = netc_flow_dissect, +}; + +MODULE_DESCRIPTION("DSA tag driver for NXP NETC switch family"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC, NETC_NAME); +module_dsa_tag_driver(netc_netdev_ops); diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c index aefef8c770e3..f3ced99b1c85 100644 --- a/net/dsa/tag_yt921x.c +++ b/net/dsa/tag_yt921x.c @@ -49,6 +49,7 @@ */ enum yt921x_tag_code { YT921X_TAG_CODE_FORWARD = 0, + YT921X_TAG_CODE_ACL = 0x17, YT921X_TAG_CODE_UNK_UCAST = 0x19, YT921X_TAG_CODE_UNK_MCAST = 0x1a, YT921X_TAG_CODE_PORT_COPY = 0x1b, @@ -129,6 +130,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) /* Already forwarded by hardware */ dsa_default_offload_fwd_mark(skb); break; + case YT921X_TAG_CODE_ACL: case YT921X_TAG_CODE_UNK_UCAST: case YT921X_TAG_CODE_UNK_MCAST: /* NOTE: hardware doesn't distinguish between TRAP (copy diff --git a/net/dsa/user.c b/net/dsa/user.c index c4bd6fe90b45..8704c1a3a5b7 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -1499,7 +1499,7 @@ dsa_user_add_cls_matchall_police(struct net_device *dev, policer = &mall_tc_entry->policer; *policer = act->police; - err = ds->ops->port_policer_add(ds, dp->index, policer); + err = ds->ops->port_policer_add(ds, dp->index, policer, extack); if (err) { kfree(mall_tc_entry); return err; diff --git a/net/ethtool/cabletest.c b/net/ethtool/cabletest.c index 8d375dac2a40..9c22d4c767c6 100644 --- a/net/ethtool/cabletest.c +++ b/net/ethtool/cabletest.c @@ -73,8 +73,7 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) dev = req_info.dev; - rtnl_lock(); - netdev_lock_ops(dev); + netdev_lock_ops_compat(dev); phydev = ethnl_req_get_phydev(&req_info, tb, ETHTOOL_A_CABLE_TEST_HEADER, info->extack); @@ -101,8 +100,7 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) ethnl_cable_test_started(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); out_unlock: - netdev_unlock_ops(dev); - rtnl_unlock(); + netdev_unlock_ops_compat(dev); ethnl_parse_header_dev_put(&req_info); return ret; } @@ -342,8 +340,7 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) if (ret) goto out_dev_put; - rtnl_lock(); - netdev_lock_ops(dev); + netdev_lock_ops_compat(dev); phydev = ethnl_req_get_phydev(&req_info, tb, ETHTOOL_A_CABLE_TEST_TDR_HEADER, info->extack); @@ -371,8 +368,7 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) ETHTOOL_MSG_CABLE_TEST_TDR_NTF); out_unlock: - netdev_unlock_ops(dev); - rtnl_unlock(); + netdev_unlock_ops_compat(dev); out_dev_put: ethnl_parse_header_dev_put(&req_info); return ret; diff --git a/net/ethtool/cmis_cdb.c b/net/ethtool/cmis_cdb.c index f3a53a984460..a4e8f4b3fb75 100644 --- a/net/ethtool/cmis_cdb.c +++ b/net/ethtool/cmis_cdb.c @@ -2,6 +2,7 @@ #include #include +#include #include "common.h" #include "module_fw.h" @@ -179,6 +180,7 @@ cmis_cdb_validate_password(struct ethtool_cmis_cdb *cdb, pe_pl = *((struct cmis_password_entry_pl *)page_data.data); pe_pl.password = params->password; + netdev_assert_locked_ops(dev); err = ops->set_module_eeprom_by_page(dev, &page_data, &extack); if (err < 0) { if (extack._msg) @@ -546,6 +548,7 @@ __ethtool_cmis_cdb_execute_cmd(struct net_device *dev, if (!page_data->data) return -ENOMEM; + netdev_assert_locked_ops(dev); err = ops->set_module_eeprom_by_page(dev, page_data, &extack); if (err < 0) { if (extack._msg) diff --git a/net/ethtool/cmis_fw_update.c b/net/ethtool/cmis_fw_update.c index 291d04d2776a..dff83807e975 100644 --- a/net/ethtool/cmis_fw_update.c +++ b/net/ethtool/cmis_fw_update.c @@ -435,13 +435,9 @@ cmis_fw_update_commit_image(struct ethtool_cmis_cdb *cdb, static int cmis_fw_update_reset(struct net_device *dev) { __u32 reset_data = ETH_RESET_PHY; - int ret; - netdev_lock_ops(dev); - ret = dev->ethtool_ops->reset(dev, &reset_data); - netdev_unlock_ops(dev); - - return ret; + netdev_assert_locked_ops(dev); + return dev->ethtool_ops->reset(dev, &reset_data); } void diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 84ec88dee05c..23db40618fed 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -67,7 +67,6 @@ const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = { [NETIF_F_HW_ESP_BIT] = "esp-hw-offload", [NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload", [NETIF_F_RX_UDP_TUNNEL_PORT_BIT] = "rx-udp_tunnel-port-offload", - [NETIF_F_HW_TLS_RECORD_BIT] = "tls-hw-record", [NETIF_F_HW_TLS_TX_BIT] = "tls-hw-tx-offload", [NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload", [NETIF_F_GRO_FRAGLIST_BIT] = "rx-gro-list", @@ -101,6 +100,9 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = { [ETHTOOL_PHY_DOWNSHIFT] = "phy-downshift", [ETHTOOL_PHY_FAST_LINK_DOWN] = "phy-fast-link-down", [ETHTOOL_PHY_EDPD] = "phy-energy-detect-power-down", + [ETHTOOL_PHY_SHORT_CABLE_PRESET] = "phy-short-cable-preset", + [ETHTOOL_PHY_LPF_BW] = "phy-lpf-bandwidth", + [ETHTOOL_PHY_DSP_EQ_INIT_VALUE] = "phy-dsp-eq-init-value", }; #define __LINK_MODE_NAME(speed, type, duplex) \ diff --git a/net/ethtool/common.h b/net/ethtool/common.h index 1609cf4e53eb..2b3847f00801 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -80,6 +80,89 @@ int ethtool_get_module_eeprom_call(struct net_device *dev, bool __ethtool_dev_mm_supported(struct net_device *dev); +/** + * ethtool_nl_msg_needs_rtnl() - does this Netlink cmd need rtnl_lock? + * @dev: target device + * @cmd: ETHTOOL_MSG_* Netlink command value + * + * Return: true if @cmd is a command for which @dev has opted-in to + * keeping rtnl_lock held across the call (via op_needs_rtnl). + */ +static inline bool +ethtool_nl_msg_needs_rtnl(const struct net_device *dev, u8 cmd) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + + switch (cmd) { + case ETHTOOL_MSG_LINKINFO_GET: + case ETHTOOL_MSG_LINKINFO_SET: + case ETHTOOL_MSG_LINKMODES_GET: + case ETHTOOL_MSG_LINKMODES_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS; + case ETHTOOL_MSG_PRIVFLAGS_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPFLAGS; + case ETHTOOL_MSG_RINGS_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM; + case ETHTOOL_MSG_CHANNELS_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCHANNELS; + case ETHTOOL_MSG_COALESCE_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCOALESCE; + case ETHTOOL_MSG_PAUSE_GET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM; + case ETHTOOL_MSG_PAUSE_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM; + case ETHTOOL_MSG_RSS_SET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_RSS; + case ETHTOOL_MSG_TSCONFIG_GET: + case ETHTOOL_MSG_TSCONFIG_SET: + /* tsconfig calls ndos (ndo_hwtstamp_set/get), not ethtool ops. + * Also, there is no corresponding ethtool ioctl, therefore + * these cases are Netlink-only. + */ + return true; + } + return false; +} + +/** + * ethtool_ioctl_needs_rtnl() - does this legacy ioctl cmd need rtnl_lock? + * @dev: target device + * @ethcmd: ETHTOOL_* ioctl command value + * + * Return: true if @ethcmd is a command for which @dev has opted-in to + * keeping rtnl_lock held across the call (via op_needs_rtnl). + */ +static inline bool +ethtool_ioctl_needs_rtnl(const struct net_device *dev, u32 ethcmd) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + + switch (ethcmd) { + case ETHTOOL_GLINKSETTINGS: + case ETHTOOL_GSET: + case ETHTOOL_SLINKSETTINGS: + case ETHTOOL_SSET: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS; + case ETHTOOL_SPFLAGS: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPFLAGS; + case ETHTOOL_SRINGPARAM: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM; + case ETHTOOL_SCHANNELS: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCHANNELS; + case ETHTOOL_SCOALESCE: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCOALESCE; + case ETHTOOL_GPAUSEPARAM: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM; + case ETHTOOL_SPAUSEPARAM: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM; + case ETHTOOL_SRSSH: + case ETHTOOL_SRXFH: + case ETHTOOL_SRXFHINDIR: + return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_RSS; + } + return false; +} + #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) void ethtool_rss_notify(struct net_device *dev, u32 type, u32 rss_context); #else diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index bd97f9b9bf18..4b0bc503f930 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -436,10 +436,10 @@ struct ethtool_link_usettings { }; /* Internal kernel helper to query a device ethtool_link_settings. */ -int __ethtool_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *link_ksettings) +int netif_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *link_ksettings) { - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); if (!dev->ethtool_ops->get_link_ksettings) return -EOPNOTSUPP; @@ -450,6 +450,21 @@ int __ethtool_get_link_ksettings(struct net_device *dev, memset(link_ksettings, 0, sizeof(*link_ksettings)); return dev->ethtool_ops->get_link_ksettings(dev, link_ksettings); } +EXPORT_SYMBOL(netif_get_link_ksettings); + +/* Convenience helper for callers that hold only rtnl_lock(). */ +int __ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *link_ksettings) +{ + int ret; + + ASSERT_RTNL(); + + netdev_lock_ops(dev); + ret = netif_get_link_ksettings(dev, link_ksettings); + netdev_unlock_ops(dev); + return ret; +} EXPORT_SYMBOL(__ethtool_get_link_ksettings); /* convert ethtool_link_usettings in user space to a kernel internal @@ -529,7 +544,7 @@ static int ethtool_get_link_ksettings(struct net_device *dev, int err = 0; struct ethtool_link_ksettings link_ksettings; - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); if (!dev->ethtool_ops->get_link_ksettings) return -EOPNOTSUPP; @@ -586,7 +601,7 @@ static int ethtool_set_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings link_ksettings = {}; int err; - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); if (!dev->ethtool_ops->set_link_ksettings) return -EOPNOTSUPP; @@ -660,7 +675,7 @@ static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) struct ethtool_cmd cmd; int err; - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); if (!dev->ethtool_ops->get_link_ksettings) return -EOPNOTSUPP; @@ -696,7 +711,7 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) struct ethtool_cmd cmd; int ret; - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); if (copy_from_user(&cmd, useraddr, sizeof(cmd))) return -EFAULT; @@ -2437,10 +2452,10 @@ void ethtool_puts(u8 **data, const char *str) } EXPORT_SYMBOL(ethtool_puts); -static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) +static int ethtool_phys_id(struct net_device *dev, void __user *useraddr, + bool has_rtnl_lock) { struct ethtool_value id; - static bool busy; const struct ethtool_ops *ops = dev->ethtool_ops; netdevice_tracker dev_tracker; int rc; @@ -2448,7 +2463,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) if (!ops->set_phys_id) return -EOPNOTSUPP; - if (busy) + if (dev->ethtool->phys_id_busy) return -EBUSY; if (copy_from_user(&id, useraddr, sizeof(id))) @@ -2458,13 +2473,14 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) if (rc < 0) return rc; - /* Drop the RTNL lock while waiting, but prevent reentry or + /* Drop the locks while waiting, but prevent reentry or * removal of the device. */ - busy = true; + dev->ethtool->phys_id_busy = true; netdev_hold(dev, &dev_tracker, GFP_KERNEL); netdev_unlock_ops(dev); - rtnl_unlock(); + if (has_rtnl_lock) + rtnl_unlock(); if (rc == 0) { /* Driver will handle this itself */ @@ -2477,22 +2493,25 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) u64 i = 0; do { - rtnl_lock(); + if (has_rtnl_lock) + rtnl_lock(); netdev_lock_ops(dev); rc = ops->set_phys_id(dev, (i++ & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON); netdev_unlock_ops(dev); - rtnl_unlock(); + if (has_rtnl_lock) + rtnl_unlock(); if (rc) break; schedule_timeout_interruptible(interval); } while (!signal_pending(current) && (!id.data || i < count)); } - rtnl_lock(); + if (has_rtnl_lock) + rtnl_lock(); netdev_lock_ops(dev); netdev_put(dev, &dev_tracker); - busy = false; + dev->ethtool->phys_id_busy = false; (void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); return rc; @@ -3114,6 +3133,7 @@ static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna) switch (tuna->id) { case ETHTOOL_PHY_DOWNSHIFT: case ETHTOOL_PHY_FAST_LINK_DOWN: + case ETHTOOL_PHY_SHORT_CABLE_PRESET: if (tuna->len != sizeof(u8) || tuna->type_id != ETHTOOL_TUNABLE_U8) return -EINVAL; @@ -3123,6 +3143,12 @@ static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna) tuna->type_id != ETHTOOL_TUNABLE_U16) return -EINVAL; break; + case ETHTOOL_PHY_LPF_BW: + case ETHTOOL_PHY_DSP_EQ_INIT_VALUE: + if (tuna->len != sizeof(u32) || + tuna->type_id != ETHTOOL_TUNABLE_U32) + return -EINVAL; + break; default: return -EINVAL; } @@ -3243,18 +3269,15 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr) /* The main entry point in this file. Called from net/core/dev_ioctl.c */ static int -__dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, - u32 ethcmd, struct ethtool_devlink_compat *devlink_state) +dev_ethtool_locked(struct net *net, struct net_device *dev, + void __user *useraddr, + u32 ethcmd, struct ethtool_devlink_compat *devlink_state, + bool has_rtnl_lock) { - struct net_device *dev; u32 sub_cmd; int rc; netdev_features_t old_features; - dev = __dev_get_by_name(net, ifr->ifr_name); - if (!dev) - return -ENODEV; - if (ethcmd == ETHTOOL_PERQUEUE) { if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd))) return -EFAULT; @@ -3305,7 +3328,8 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, return -EPERM; } - netdev_lock_ops(dev); + netdev_assert_locked_ops_compat(dev); + if (dev->dev.parent) pm_runtime_get_sync(dev->dev.parent); @@ -3393,7 +3417,7 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, rc = ethtool_get_strings(dev, useraddr); break; case ETHTOOL_PHYS_ID: - rc = ethtool_phys_id(dev, useraddr); + rc = ethtool_phys_id(dev, useraddr, has_rtnl_lock); break; case ETHTOOL_GSTATS: rc = ethtool_get_stats(dev, useraddr); @@ -3540,12 +3564,81 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, if (dev->ethtool_ops->complete) dev->ethtool_ops->complete(dev); - if (old_features != dev->features) - netdev_features_change(dev); + switch (ethcmd) { + case ETHTOOL_PHYS_ID: + /* Don't check features if operation drops the locks. + * Someone else may have changed features in parallel. + */ + break; + default: + if (old_features != dev->features) { + if (has_rtnl_lock) + netdev_features_change(dev); + else + netdev_WARN(dev, "ethtool cmd %u changed features without rtnl_lock", ethcmd); + } + } out: if (dev->dev.parent) pm_runtime_put(dev->dev.parent); + + return rc; +} + +/* Commands that may toggle dev->features in net/ethtool/ioctl.c and so + * call into __netdev_update_features(), which still requires rtnl_lock. + * Driver-decided SET commands that may chain into rtnl-only helpers are + * covered by ethtool_ioctl_needs_rtnl()/ETHTOOL_OP_NEEDS_RTNL_*. + */ +static bool ethtool_cmd_changes_features(u32 ethcmd) +{ + switch (ethcmd) { + case ETHTOOL_SFEATURES: + case ETHTOOL_SFLAGS: + case ETHTOOL_STXCSUM: + case ETHTOOL_SRXCSUM: + case ETHTOOL_SSG: + case ETHTOOL_STSO: + case ETHTOOL_SGSO: + case ETHTOOL_SGRO: + return true; + } + return false; +} + +static int +__dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, + u32 ethcmd, struct ethtool_devlink_compat *devlink_state) +{ + netdevice_tracker dev_tracker; + struct net_device *dev; + bool need_rtnl; + int rc; + + dev = netdev_get_by_name(net, ifr->ifr_name, &dev_tracker, GFP_KERNEL); + if (!dev) + return -ENODEV; + + need_rtnl = !netdev_need_ops_lock(dev) || + ethtool_cmd_changes_features(ethcmd) || + ethtool_ioctl_needs_rtnl(dev, ethcmd); + if (need_rtnl) + rtnl_lock(); + netdev_lock_ops(dev); + if (dev->reg_state > NETREG_REGISTERED || + dev->moving_ns || !net_eq(dev_net(dev), net)) { + rc = -ENODEV; + goto exit_ops_unlock; + } + + rc = dev_ethtool_locked(net, dev, useraddr, ethcmd, devlink_state, + need_rtnl); + +exit_ops_unlock: netdev_unlock_ops(dev); + if (need_rtnl) + rtnl_unlock(); + netdev_put(dev, &dev_tracker); return rc; } @@ -3573,9 +3666,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr) break; } - rtnl_lock(); rc = __dev_ethtool(net, ifr, useraddr, ethcmd, state); - rtnl_unlock(); if (rc) goto exit_free; diff --git a/net/ethtool/linkinfo.c b/net/ethtool/linkinfo.c index 244ff92e2ff9..d5b3dbc53c5f 100644 --- a/net/ethtool/linkinfo.c +++ b/net/ethtool/linkinfo.c @@ -34,7 +34,7 @@ static int linkinfo_prepare_data(const struct ethnl_req_info *req_base, ret = ethnl_ops_begin(dev); if (ret < 0) return ret; - ret = __ethtool_get_link_ksettings(dev, &data->ksettings); + ret = netif_get_link_ksettings(dev, &data->ksettings); if (ret < 0) GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); ethnl_ops_complete(dev); @@ -104,7 +104,7 @@ ethnl_set_linkinfo(struct ethnl_req_info *req_info, struct genl_info *info) bool mod = false; int ret; - ret = __ethtool_get_link_ksettings(dev, &ksettings); + ret = netif_get_link_ksettings(dev, &ksettings); if (ret < 0) { GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); return ret; diff --git a/net/ethtool/linkmodes.c b/net/ethtool/linkmodes.c index 30d703531652..a6d32f0d9fcc 100644 --- a/net/ethtool/linkmodes.c +++ b/net/ethtool/linkmodes.c @@ -39,7 +39,7 @@ static int linkmodes_prepare_data(const struct ethnl_req_info *req_base, if (ret < 0) return ret; - ret = __ethtool_get_link_ksettings(dev, &data->ksettings); + ret = netif_get_link_ksettings(dev, &data->ksettings); if (ret < 0) { GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); goto out; @@ -324,7 +324,7 @@ ethnl_set_linkmodes(struct ethnl_req_info *req_info, struct genl_info *info) bool mod = false; int ret; - ret = __ethtool_get_link_ksettings(dev, &ksettings); + ret = netif_get_link_ksettings(dev, &ksettings); if (ret < 0) { GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); return ret; diff --git a/net/ethtool/mm.c b/net/ethtool/mm.c index 29bbbc149375..2d10e2a1393f 100644 --- a/net/ethtool/mm.c +++ b/net/ethtool/mm.c @@ -246,8 +246,9 @@ const struct ethnl_request_ops ethnl_mm_request_ops = { }; /* Returns whether a given device supports the MAC merge layer - * (has an eMAC and a pMAC). Must be called under rtnl_lock() and - * ethnl_ops_begin(). + * (has an eMAC and a pMAC). Must be called under whichever lock + * netdev_assert_locked_ops_compat() accepts (rtnl for traditional drivers, + * the netdev instance lock for ops-locked ones) and ethnl_ops_begin(). */ bool __ethtool_dev_mm_supported(struct net_device *dev) { diff --git a/net/ethtool/module.c b/net/ethtool/module.c index ea4fb2a76650..9cf670e089f2 100644 --- a/net/ethtool/module.c +++ b/net/ethtool/module.c @@ -226,7 +226,9 @@ static void module_flash_fw_work(struct work_struct *work) module_fw = container_of(work, struct ethtool_module_fw_flash, work); dev = module_fw->fw_update.dev; + netdev_lock_ops(dev); ethtool_cmis_fw_update(&module_fw->fw_update); + netdev_unlock_ops(dev); module_flash_fw_work_list_del(&module_fw->list); @@ -427,8 +429,7 @@ int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info) return ret; dev = req_info.dev; - rtnl_lock(); - netdev_lock_ops(dev); + netdev_lock_ops_compat(dev); ret = ethnl_ops_begin(dev); if (ret < 0) goto out_unlock; @@ -443,8 +444,7 @@ int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info) ethnl_ops_complete(dev); out_unlock: - netdev_unlock_ops(dev); - rtnl_unlock(); + netdev_unlock_ops_compat(dev); ethnl_parse_header_dev_put(&req_info); return ret; } diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 7d45f9a884e5..1af395b54330 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -7,12 +7,19 @@ #include #include +#include "common.h" #include "module_fw.h" #include "netlink.h" static struct genl_family ethtool_genl_family; static bool ethnl_ok __read_mostly; + +/* Serializes broadcast notification sequence allocation with the multicast + * send, so that userspace observes nlmsg_seq monotonic in receive order + * regardless of which lock the caller holds (rtnl or instance lock). + */ +static DEFINE_MUTEX(ethnl_bcast_lock); static u32 ethnl_bcast_seq; #define ETHTOOL_FLAGS_BASIC (ETHTOOL_FLAG_COMPACT_BITSETS | \ @@ -82,12 +89,6 @@ static void ethnl_sock_priv_destroy(void *priv) } } -u32 ethnl_bcast_seq_next(void) -{ - ASSERT_RTNL(); - return ++ethnl_bcast_seq; -} - int ethnl_ops_begin(struct net_device *dev) { int ret; @@ -98,7 +99,7 @@ int ethnl_ops_begin(struct net_device *dev) if (dev->dev.parent) pm_runtime_get_sync(dev->dev.parent); - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (!netif_device_present(dev) || dev->reg_state >= NETREG_UNREGISTERING) { @@ -226,11 +227,13 @@ struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info, { struct phy_device *phydev; - ASSERT_RTNL(); - if (!req_info->dev) return NULL; + /* If there is no PHY in sight there's no need for assert locking */ + if (!phy_link_topo_empty(req_info->dev)) + ASSERT_RTNL(); + if (!req_info->phy_index) return req_info->dev->phydev; @@ -329,8 +332,7 @@ void *ethnl_dump_put(struct sk_buff *skb, struct netlink_callback *cb, u8 cmd) void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd) { - return genlmsg_put(skb, 0, ++ethnl_bcast_seq, ðtool_genl_family, 0, - cmd); + return genlmsg_put(skb, 0, 0, ðtool_genl_family, 0, cmd); } void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd) @@ -340,8 +342,15 @@ void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd) int ethnl_multicast(struct sk_buff *skb, struct net_device *dev) { - return genlmsg_multicast_netns(ðtool_genl_family, dev_net(dev), skb, - 0, ETHNL_MCGRP_MONITOR, GFP_KERNEL); + struct nlmsghdr *nlh = nlmsg_hdr(skb); + int ret; + + mutex_lock(ðnl_bcast_lock); + nlh->nlmsg_seq = ++ethnl_bcast_seq; + ret = genlmsg_multicast_netns(ðtool_genl_family, dev_net(dev), skb, + 0, ETHNL_MCGRP_MONITOR, GFP_KERNEL); + mutex_unlock(ðnl_bcast_lock); + return ret; } /* GET request helpers */ @@ -501,6 +510,7 @@ static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info) struct ethnl_req_info *req_info = NULL; const u8 cmd = info->genlhdr->cmd; const struct ethnl_request_ops *ops; + bool need_rtnl = false; int hdr_len, reply_len; struct sk_buff *rskb; void *reply_payload; @@ -526,13 +536,19 @@ static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info) goto err_free; ethnl_init_reply_data(reply_data, ops, req_info->dev); - rtnl_lock(); - if (req_info->dev) + if (req_info->dev) { + need_rtnl = !netdev_need_ops_lock(req_info->dev) || + ethtool_nl_msg_needs_rtnl(req_info->dev, cmd); + if (need_rtnl) + rtnl_lock(); netdev_lock_ops(req_info->dev); + } ret = ops->prepare_data(req_info, reply_data, info); - if (req_info->dev) + if (req_info->dev) { netdev_unlock_ops(req_info->dev); - rtnl_unlock(); + if (need_rtnl) + rtnl_unlock(); + } if (ret < 0) goto err_dev; ret = ops->reply_size(req_info, reply_data); @@ -579,6 +595,7 @@ static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev, const struct ethnl_dump_ctx *ctx, const struct genl_info *info) { + bool need_rtnl; void *ehdr; int ret; @@ -589,11 +606,15 @@ static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev, return -EMSGSIZE; ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev); - rtnl_lock(); + need_rtnl = !netdev_need_ops_lock(dev) || + ethtool_nl_msg_needs_rtnl(dev, ctx->ops->request_cmd); + if (need_rtnl) + rtnl_lock(); netdev_lock_ops(dev); ret = ctx->ops->prepare_data(ctx->req_info, ctx->reply_data, info); netdev_unlock_ops(dev); - rtnl_unlock(); + if (need_rtnl) + rtnl_unlock(); if (ret < 0) goto out_cancel; ret = ethnl_fill_reply_header(skb, dev, ctx->ops->hdr_attr); @@ -882,6 +903,7 @@ static int ethnl_default_set_doit(struct sk_buff *skb, struct genl_info *info) const u8 cmd = info->genlhdr->cmd; struct ethnl_req_info *req_info; struct net_device *dev; + bool need_rtnl; int ret; ops = ethnl_default_requests[cmd]; @@ -906,8 +928,11 @@ static int ethnl_default_set_doit(struct sk_buff *skb, struct genl_info *info) } dev = req_info->dev; + need_rtnl = !netdev_need_ops_lock(dev) || + ethtool_nl_msg_needs_rtnl(dev, cmd); - rtnl_lock(); + if (need_rtnl) + rtnl_lock(); netdev_lock_ops(dev); dev->cfg_pending = kmemdup(dev->cfg, sizeof(*dev->cfg), GFP_KERNEL_ACCOUNT); @@ -937,7 +962,8 @@ static int ethnl_default_set_doit(struct sk_buff *skb, struct genl_info *info) out_tie_cfg: dev->cfg_pending = dev->cfg; netdev_unlock_ops(dev); - rtnl_unlock(); + if (need_rtnl) + rtnl_unlock(); out_dev: ethnl_parse_header_dev_put(req_info); out_free_req: @@ -1003,7 +1029,7 @@ static void ethnl_default_notify(struct net_device *dev, unsigned int cmd, ops->req_info_size - sizeof(*req_info)); } - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); ethnl_init_reply_data(reply_data, ops, dev); ret = ops->prepare_data(req_info, reply_data, &info); @@ -1079,7 +1105,7 @@ void ethnl_notify(struct net_device *dev, unsigned int cmd, { if (unlikely(!ethnl_ok)) return; - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) && ethnl_notify_handlers[cmd])) diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index fd2198e45d2b..4ca2eca2e94b 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -10,7 +10,6 @@ struct ethnl_req_info; -u32 ethnl_bcast_seq_next(void); int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, const struct nlattr *nest, struct net *net, struct netlink_ext_ack *extack, @@ -276,14 +275,15 @@ static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info) /** * ethnl_req_get_phydev() - Gets the phy_device targeted by this request, - * if any. Must be called under rntl_lock(). + * if any. * @req_info: The ethnl request to get the phy from. * @tb: The netlink attributes array, for error reporting. * @header: The netlink header index, used for error reporting. * @extack: The netlink extended ACK, for error reporting. * - * The caller must hold RTNL, until it's done interacting with the returned - * phy_device. + * If a phy_device is returned the caller must hold rtnl_lock when calling + * this function, and until it's done interacting with the returned phy_device. + * IOW caller must hold rtnl_lock unless they know netdev has no phy_device. * * Return: A phy_device pointer corresponding either to the passed phy_index * if one is provided. If not, the phy_device attached to the @@ -333,7 +333,9 @@ int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid, * @hdr_attr: attribute type for request header * @req_info_size: size of request info * @reply_data_size: size of reply data - * @allow_nodev_do: allow non-dump request with no device identification + * @allow_nodev_do: + * Allow non-dump request with no device identification. + * Note that locks (rtnl_lock etc.) are only taken if device is set. * @set_ntf_cmd: notification to generate on changes (SET) * @parse_request: * Parse request except common header (struct ethnl_req_info). Common diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c index ddc6eab701ed..018b0412be86 100644 --- a/net/ethtool/phy.c +++ b/net/ethtool/phy.c @@ -78,7 +78,6 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, struct phy_device *phydev; int ret; - /* RTNL is held by the caller */ phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PHY_HEADER, info->extack); if (IS_ERR_OR_NULL(phydev)) diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c index 0fd5dcc3729f..9054c89c5d7b 100644 --- a/net/ethtool/rings.c +++ b/net/ethtool/rings.c @@ -63,9 +63,9 @@ static int rings_reply_size(const struct ethnl_req_info *req_base, nla_total_size(sizeof(u32)) + /* _RINGS_TX */ 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 */ + nla_total_size(sizeof(u32)) + /* _RINGS_CQE_SIZE */ nla_total_size(sizeof(u8)) + /* _RINGS_TX_PUSH */ - nla_total_size(sizeof(u8))) + /* _RINGS_RX_PUSH */ + nla_total_size(sizeof(u8)) + /* _RINGS_RX_PUSH */ nla_total_size(sizeof(u32)) + /* _RINGS_TX_PUSH_BUF_LEN */ nla_total_size(sizeof(u32)) + /* _RINGS_TX_PUSH_BUF_LEN_MAX */ nla_total_size(sizeof(u32)) + /* _RINGS_HDS_THRESH */ diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 53792f53f922..d8adc78e3775 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -2,6 +2,7 @@ #include +#include "../core/dev.h" #include "common.h" #include "netlink.h" @@ -468,21 +469,16 @@ int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); struct net *net = sock_net(skb->sk); - struct net_device *dev; int ret = 0; - rtnl_lock(); - for_each_netdev_dump(net, dev, ctx->ifindex) { + for_each_netdev_lock_ops_compat_scoped(net, dev, ctx->ifindex) { if (ctx->match_ifindex && ctx->match_ifindex != ctx->ifindex) break; - netdev_lock_ops(dev); ret = rss_dump_one_dev(skb, cb, dev); - netdev_unlock_ops(dev); if (ret) break; } - rtnl_unlock(); return ret; } @@ -995,7 +991,6 @@ ethnl_rss_create_send_ntf(const struct sk_buff *rsp, struct net_device *dev) nlh = nlmsg_hdr(ntf); /* Convert the reply into a notification */ nlh->nlmsg_pid = 0; - nlh->nlmsg_seq = ethnl_bcast_seq_next(); genl_hdr = nlmsg_data(nlh); genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF; @@ -1038,8 +1033,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) if (ret) goto exit_free_dev; - rtnl_lock(); - netdev_lock_ops(dev); + netdev_lock_ops_compat(dev); ret = ethnl_ops_begin(dev); if (ret < 0) @@ -1126,8 +1120,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) exit_ops: ethnl_ops_complete(dev); exit_dev_unlock: - netdev_unlock_ops(dev); - rtnl_unlock(); + netdev_unlock_ops_compat(dev); exit_free_dev: ethnl_parse_header_dev_put(&req.base); exit_free_rsp: @@ -1180,8 +1173,7 @@ int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info) goto exit_free_dev; } - rtnl_lock(); - netdev_lock_ops(dev); + netdev_lock_ops_compat(dev); ret = ethnl_ops_begin(dev); if (ret < 0) @@ -1211,8 +1203,7 @@ int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info) mutex_unlock(&dev->ethtool->rss_lock); ethnl_ops_complete(dev); exit_dev_unlock: - netdev_unlock_ops(dev); - rtnl_unlock(); + netdev_unlock_ops_compat(dev); exit_free_dev: ethnl_parse_header_dev_put(&req); return ret; diff --git a/net/ethtool/tsconfig.c b/net/ethtool/tsconfig.c index fc4f93cfa459..24b64862011f 100644 --- a/net/ethtool/tsconfig.c +++ b/net/ethtool/tsconfig.c @@ -2,6 +2,7 @@ #include #include +#include #include "bitset.h" #include "common.h" @@ -57,7 +58,7 @@ static int tsconfig_prepare_data(const struct ethnl_req_info *req_base, data->hwtst_config.flags = cfg.flags; data->hwprov_desc.index = -1; - hwprov = rtnl_dereference(dev->hwprov); + hwprov = netdev_ops_lock_dereference(dev->hwprov, dev); if (hwprov) { data->hwprov_desc.index = hwprov->desc.index; data->hwprov_desc.qualifier = hwprov->desc.qualifier; @@ -213,7 +214,7 @@ static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info) return -ENOMEM; } - ASSERT_RTNL(); + netdev_assert_locked_ops_compat(dev); reply_data->base.dev = dev; ret = tsconfig_prepare_data(&req_info->base, &reply_data->base, info); if (ret < 0) @@ -309,10 +310,6 @@ static int ethnl_set_tsconfig(struct ethnl_req_info *req_base, struct nlattr **tb = info->attrs; int ret; - BUILD_BUG_ON(__HWTSTAMP_TX_CNT >= 32); - BUILD_BUG_ON(__HWTSTAMP_FILTER_CNT >= 32); - BUILD_BUG_ON(__HWTSTAMP_FLAG_CNT > 32); - if (!netif_device_present(dev)) return -ENODEV; @@ -320,7 +317,7 @@ static int ethnl_set_tsconfig(struct ethnl_req_info *req_base, struct hwtstamp_provider_desc __hwprov_desc = {.index = -1}; struct hwtstamp_provider *__hwprov; - __hwprov = rtnl_dereference(dev->hwprov); + __hwprov = netdev_ops_lock_dereference(dev->hwprov, dev); if (__hwprov) { __hwprov_desc.index = __hwprov->desc.index; __hwprov_desc.qualifier = __hwprov->desc.qualifier; @@ -418,7 +415,8 @@ static int ethnl_set_tsconfig(struct ethnl_req_info *req_base, goto err_free_hwprov; /* Change the selected hwtstamp source */ - __hwprov = rcu_replace_pointer_rtnl(dev->hwprov, hwprov); + __hwprov = rcu_replace_pointer(dev->hwprov, hwprov, + netdev_is_locked_ops_compat(dev)); if (__hwprov) kfree_rcu(__hwprov, rcu_head); } diff --git a/net/ethtool/tsinfo.c b/net/ethtool/tsinfo.c index 14bf01e3b55c..c9b680a9cc3f 100644 --- a/net/ethtool/tsinfo.c +++ b/net/ethtool/tsinfo.c @@ -6,6 +6,7 @@ #include #include +#include "../core/dev.h" #include "bitset.h" #include "common.h" #include "netlink.h" @@ -473,28 +474,25 @@ int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; struct net *net = sock_net(skb->sk); - struct net_device *dev; int ret = 0; - rtnl_lock(); if (ctx->req_info->base.dev) { - dev = ctx->req_info->base.dev; - netdev_lock_ops(dev); + struct net_device *dev = ctx->req_info->base.dev; + + netdev_lock_ops_compat(dev); ret = ethnl_tsinfo_dump_one_net_topo(skb, dev, cb); - netdev_unlock_ops(dev); - } else { - for_each_netdev_dump(net, dev, ctx->pos_ifindex) { - netdev_lock_ops(dev); - ret = ethnl_tsinfo_dump_one_net_topo(skb, dev, cb); - netdev_unlock_ops(dev); - if (ret < 0 && ret != -EOPNOTSUPP) - break; - ctx->pos_phyindex = 0; - ctx->netdev_dump_done = false; - ctx->pos_phcqualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE; - } + netdev_unlock_ops_compat(dev); + return ret; + } + + for_each_netdev_lock_ops_compat_scoped(net, dev, ctx->pos_ifindex) { + ret = ethnl_tsinfo_dump_one_net_topo(skb, dev, cb); + if (ret < 0 && ret != -EOPNOTSUPP) + break; + ctx->pos_phyindex = 0; + ctx->netdev_dump_done = false; + ctx->pos_phcqualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE; } - rtnl_unlock(); return ret; } diff --git a/net/handshake/genl.c b/net/handshake/genl.c index 4b20cd9cdd0e..feac1ad063ee 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -38,7 +38,7 @@ static const struct genl_split_ops handshake_nl_ops[] = { .doit = handshake_nl_done_doit, .policy = handshake_done_nl_policy, .maxattr = HANDSHAKE_A_DONE_REMOTE_AUTH, - .flags = GENL_CMD_CAP_DO, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, }; diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index f669a226d728..0774981a65c1 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -110,7 +110,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) } /* end of tlvs must follow at the end */ - if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT && + if (hsr_sup_tlv->HSR_TLV_type != HSR_TLV_EOT || hsr_sup_tlv->HSR_TLV_length != 0) return false; diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index a28dfd8490c5..e44929871274 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -825,18 +825,16 @@ static void fill_last_seq_nrs(struct hsr_node *node, u16 *if1_seq, u16 *if2_seq) block_sz = hsr_seq_block_size(node); block = node->block_buf + block_off * block_sz; - if (!bitmap_empty(block->seq_nrs[HSR_PT_SLAVE_B - 1], - HSR_SEQ_BLOCK_SIZE)) { - seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_B - 1], - HSR_SEQ_BLOCK_SIZE); + seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_B - 1], + HSR_SEQ_BLOCK_SIZE); + if (seq_bit < HSR_SEQ_BLOCK_SIZE) *if1_seq = (block->block_idx << HSR_SEQ_BLOCK_SHIFT) | seq_bit; - } - if (!bitmap_empty(block->seq_nrs[HSR_PT_SLAVE_A - 1], - HSR_SEQ_BLOCK_SIZE)) { - seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_A - 1], - HSR_SEQ_BLOCK_SIZE); + + seq_bit = find_last_bit(block->seq_nrs[HSR_PT_SLAVE_A - 1], + HSR_SEQ_BLOCK_SIZE); + if (seq_bit < HSR_SEQ_BLOCK_SIZE) *if2_seq = (block->block_idx << HSR_SEQ_BLOCK_SHIFT) | seq_bit; - } + spin_unlock_bh(&node->seq_out_lock); } diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index db0b0af7a692..8099f2069a74 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -76,9 +76,14 @@ static int hsr_newlink(struct net_device *dev, return -EINVAL; } - if (data[IFLA_HSR_INTERLINK]) + if (data[IFLA_HSR_INTERLINK]) { interlink = __dev_get_by_index(link_net, nla_get_u32(data[IFLA_HSR_INTERLINK])); + if (!interlink) { + NL_SET_ERR_MSG_MOD(extack, "Interlink does not exist"); + return -EINVAL; + } + } if (interlink && interlink == link[0]) { NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave1 are the same"); @@ -247,7 +252,8 @@ void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN], goto nla_put_failure; genlmsg_end(skb, msg_head); - genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); + genlmsg_multicast_netns(&hsr_genl_family, dev_net(port->dev), + skb, 0, 0, GFP_ATOMIC); return; @@ -283,8 +289,12 @@ void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]) if (res < 0) goto nla_put_failure; + rcu_read_lock(); + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); genlmsg_end(skb, msg_head); - genlmsg_multicast(&hsr_genl_family, skb, 0, 0, GFP_ATOMIC); + genlmsg_multicast_netns(&hsr_genl_family, dev_net(master->dev), + skb, 0, 0, GFP_ATOMIC); + rcu_read_unlock(); return; diff --git a/net/ife/ife.c b/net/ife/ife.c index be05b690b9ef..7a75947a31e3 100644 --- a/net/ife/ife.c +++ b/net/ife/ife.c @@ -79,7 +79,7 @@ void *ife_decode(struct sk_buff *skb, u16 *metalen) if (unlikely(ifehdrln < 2)) return NULL; - if (unlikely(!pskb_may_pull(skb, total_pull))) + if (unlikely(!pskb_may_pull(skb, total_pull + ETH_HLEN))) return NULL; ifehdr = (struct ifeheadr *)(skb->data + skb->dev->hard_header_len); diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 21e5164e30db..301b47660305 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -741,14 +741,12 @@ config DEFAULT_TCP_CONG default "bbr" if DEFAULT_BBR default "cubic" -config TCP_SIGPOOL - tristate - config TCP_AO bool "TCP: Authentication Option (RFC5925)" - select CRYPTO + select CRYPTO_LIB_AES_CBC_MACS + select CRYPTO_LIB_SHA1 + select CRYPTO_LIB_SHA256 select CRYPTO_LIB_UTILS - select TCP_SIGPOOL depends on 64BIT # seq-number extension needs WRITE_ONCE(u64) help TCP-AO specifies the use of stronger Message Authentication Codes (MACs), diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 7f9f98813986..06e21c26b76f 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o -obj-$(CONFIG_TCP_SIGPOOL) += tcp_sigpool.o obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o obj-$(CONFIG_BPF_SYSCALL) += udp_bpf.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o @@ -72,3 +71,7 @@ obj-$(CONFIG_TCP_AO) += tcp_ao.o ifeq ($(CONFIG_BPF_JIT),y) obj-$(CONFIG_BPF_SYSCALL) += bpf_tcp_ca.o endif + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE_netfilter.o := y +endif diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 0e62032e76b1..32d006c1a8ee 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -852,7 +852,6 @@ int inet_send_prepare(struct sock *sk) return 0; } -EXPORT_SYMBOL_GPL(inet_send_prepare); int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) { @@ -881,7 +880,6 @@ void inet_splice_eof(struct socket *sock) if (prot->splice_eof) prot->splice_eof(sock); } -EXPORT_SYMBOL_GPL(inet_splice_eof); int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) @@ -1349,7 +1347,6 @@ int inet_sk_rebuild_header(struct sock *sk) return err; } -EXPORT_SYMBOL(inet_sk_rebuild_header); void inet_sk_set_state(struct sock *sk, int state) { @@ -1540,6 +1537,7 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb) return pp; } +EXPORT_INDIRECT_CALLABLE(inet_gro_receive); static struct sk_buff *ipip_gro_receive(struct list_head *head, struct sk_buff *skb) @@ -1578,7 +1576,6 @@ __be32 inet_current_timestamp(void) /* Convert to network byte order. */ return htonl(msecs); } -EXPORT_SYMBOL(inet_current_timestamp); int inet_recv_error(struct sock *sk, struct msghdr *msg, int len) { @@ -1625,6 +1622,7 @@ int inet_gro_complete(struct sk_buff *skb, int nhoff) out: return err; } +EXPORT_INDIRECT_CALLABLE(inet_gro_complete); static int ipip_gro_complete(struct sk_buff *skb, int nhoff) { @@ -1663,7 +1661,6 @@ unsigned long snmp_fold_field(void __percpu *mib, int offt) res += snmp_get_cpu_field(mib, i, offt); return res; } -EXPORT_SYMBOL_GPL(snmp_fold_field); #if BITS_PER_LONG==32 @@ -1684,7 +1681,6 @@ u64 snmp_get_cpu_field64(void __percpu *mib, int cpu, int offt, return v; } -EXPORT_SYMBOL_GPL(snmp_get_cpu_field64); u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_offset) { @@ -1696,7 +1692,6 @@ u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_offset) } return res; } -EXPORT_SYMBOL_GPL(snmp_fold_field64); #endif #ifdef CONFIG_IP_MULTICAST diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 58fe7cb69545..a35b72662e43 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2128,11 +2128,67 @@ static int inet_validate_link_af(const struct net_device *dev, return 0; } +static bool devinet_conf_post_set(struct net *net, struct ipv4_devconf *cnf, + int attr, int new, int old, int ifindex) +{ + if (new == old) + return false; + + switch (attr) { + case IPV4_DEVCONF_ROUTE_LOCALNET: + case IPV4_DEVCONF_ACCEPT_LOCAL: + if (new == 0) + return true; + break; + case IPV4_DEVCONF_NOXFRM: + case IPV4_DEVCONF_NOPOLICY: + case IPV4_DEVCONF_PROMOTE_SECONDARIES: + case IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST: + case IPV4_DEVCONF_BC_FORWARDING: + return true; + case IPV4_DEVCONF_RP_FILTER: + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_RP_FILTER, + ifindex, cnf); + break; + case IPV4_DEVCONF_PROXY_ARP: + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_PROXY_NEIGH, + ifindex, cnf); + break; + case IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN: + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + ifindex, cnf); + break; + case IPV4_DEVCONF_FORWARDING: + if (new == 1) { + /* it is safe to use container_of() because forwarding case + * is only used by the netlink path + */ + struct in_device *idev = container_of(cnf, struct in_device, cnf); + + netif_disable_lro(idev->dev); + } + + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, + ifindex, cnf); + return true; + default: + break; + } + + return false; +} + static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla, struct netlink_ext_ack *extack) { struct in_device *in_dev = __in_dev_get_rtnl(dev); struct nlattr *a, *tb[IFLA_INET_MAX+1]; + struct net *net = dev_net(dev); + bool flush_cache = false; int rem; if (!in_dev) @@ -2142,8 +2198,17 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla, return -EINVAL; if (tb[IFLA_INET_CONF]) { - nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) - ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); + nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { + int old_value = ipv4_devconf_get(in_dev, nla_type(a)); + int new_value = nla_get_u32(a); + + ipv4_devconf_set(in_dev, nla_type(a), new_value); + if (devinet_conf_post_set(net, &in_dev->cnf, nla_type(a), new_value, + old_value, dev->ifindex)) + flush_cache = true; + } + if (flush_cache) + rt_cache_flush(net); } return 0; @@ -2509,44 +2574,31 @@ static int devinet_conf_proc(const struct ctl_table *ctl, int write, if (write) { struct ipv4_devconf *cnf = ctl->extra1; - struct net *net = ctl->extra2; int i = (int *)ctl->data - cnf->data; + struct net *net = ctl->extra2; int ifindex; - set_bit(i, cnf->state); + /* These attributes are bypassing the tracking state, + * for the rest track the state and propagate the changes + * to default config + */ + switch (i + 1) { + case IPV4_DEVCONF_NOXFRM: + case IPV4_DEVCONF_NOPOLICY: + case IPV4_DEVCONF_PROMOTE_SECONDARIES: + case IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST: + break; + default: + set_bit(i, cnf->state); + if (cnf == net->ipv4.devconf_dflt) + devinet_copy_dflt_conf(net, i); + break; + } - if (cnf == net->ipv4.devconf_dflt) - devinet_copy_dflt_conf(net, i); - if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || - i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) - if ((new_value == 0) && (old_value != 0)) - rt_cache_flush(net); - - if (i == IPV4_DEVCONF_BC_FORWARDING - 1 && - new_value != old_value) + ifindex = devinet_conf_ifindex(net, cnf); + if (devinet_conf_post_set(net, cnf, i + 1, new_value, + old_value, ifindex)) rt_cache_flush(net); - - if (i == IPV4_DEVCONF_RP_FILTER - 1 && - new_value != old_value) { - ifindex = devinet_conf_ifindex(net, cnf); - inet_netconf_notify_devconf(net, RTM_NEWNETCONF, - NETCONFA_RP_FILTER, - ifindex, cnf); - } - if (i == IPV4_DEVCONF_PROXY_ARP - 1 && - new_value != old_value) { - ifindex = devinet_conf_ifindex(net, cnf); - inet_netconf_notify_devconf(net, RTM_NEWNETCONF, - NETCONFA_PROXY_NEIGH, - ifindex, cnf); - } - if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && - new_value != old_value) { - ifindex = devinet_conf_ifindex(net, cnf); - inet_netconf_notify_devconf(net, RTM_NEWNETCONF, - NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, - ifindex, cnf); - } } return ret; @@ -2599,20 +2651,6 @@ static int devinet_sysctl_forward(const struct ctl_table *ctl, int write, return ret; } -static int ipv4_doint_and_flush(const struct ctl_table *ctl, int write, - void *buffer, size_t *lenp, loff_t *ppos) -{ - int *valp = ctl->data; - int val = *valp; - int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); - struct net *net = ctl->extra2; - - if (write && *valp != val) - rt_cache_flush(net); - - return ret; -} - #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ { \ .procname = name, \ @@ -2633,9 +2671,6 @@ static int ipv4_doint_and_flush(const struct ctl_table *ctl, int write, #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) -#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ - DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) - static struct devinet_sysctl_table { struct ctl_table_header *sysctl_header; struct ctl_table devinet_vars[IPV4_DEVCONF_MAX]; @@ -2678,15 +2713,14 @@ static struct devinet_sysctl_table { "ignore_routes_with_linkdown"), DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, "drop_gratuitous_arp"), - - DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), - DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), - DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, - "promote_secondaries"), - DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, - "route_localnet"), - DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, - "drop_unicast_in_l2_multicast"), + DEVINET_SYSCTL_RW_ENTRY(NOXFRM, "disable_xfrm"), + DEVINET_SYSCTL_RW_ENTRY(NOPOLICY, "disable_policy"), + DEVINET_SYSCTL_RW_ENTRY(PROMOTE_SECONDARIES, + "promote_secondaries"), + DEVINET_SYSCTL_RW_ENTRY(ROUTE_LOCALNET, + "route_localnet"), + DEVINET_SYSCTL_RW_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, + "drop_unicast_in_l2_multicast"), }, }; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 1dab44e13d3b..42212970d735 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -88,7 +88,8 @@ struct fib_table *fib_new_table(struct net *net, u32 id) if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules) alias = fib_new_table(net, RT_TABLE_MAIN); - tb = fib_trie_table(id, alias); + if (check_net(net)) + tb = fib_trie_table(id, alias); if (!tb) return NULL; @@ -946,9 +947,6 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, struct rtmsg *rtm; int err, i; - if (filter->rtnl_held) - ASSERT_RTNL(); - rtm = nlmsg_payload(nlh, sizeof(*rtm)); if (!rtm) { NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request"); @@ -992,10 +990,8 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, break; case RTA_OIF: ifindex = nla_get_u32(tb[i]); - if (filter->rtnl_held) - filter->dev = __dev_get_by_index(net, ifindex); - else - filter->dev = dev_get_by_index_rcu(net, ifindex); + + filter->dev = dev_get_by_index_rcu(net, ifindex); if (!filter->dev) return -ENODEV; break; @@ -1017,18 +1013,16 @@ EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req); static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) { + const struct nlmsghdr *nlh = cb->nlh; + struct net *net = sock_net(skb->sk); struct fib_dump_filter filter = { .dump_routes = true, .dump_exceptions = true, - .rtnl_held = false, }; - const struct nlmsghdr *nlh = cb->nlh; - struct net *net = sock_net(skb->sk); - unsigned int h, s_h; - unsigned int e = 0, s_e; - struct fib_table *tb; + unsigned int e = 0, s_e, h, s_h; struct hlist_head *head; int dumped = 0, err = 0; + struct fib_table *tb; rcu_read_lock(); if (cb->strict_check) { @@ -1613,18 +1607,11 @@ static void ip_fib_net_exit(struct net *net) struct fib_table *tb; hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) { - hlist_del(&tb->tb_hlist); + hlist_del_rcu(&tb->tb_hlist); fib_table_flush(net, tb, true); fib_free_table(tb); } } - -#ifdef CONFIG_IP_MULTIPLE_TABLES - fib4_rules_exit(net); -#endif - - kfree(net->ipv4.fib_table_hash); - fib4_notifier_exit(net); } static int __net_init fib_net_init(struct net *net) @@ -1660,35 +1647,42 @@ static int __net_init fib_net_init(struct net *net) rtnl_net_lock(net); ip_fib_net_exit(net); rtnl_net_unlock(net); + +#ifdef CONFIG_IP_MULTIPLE_TABLES + fib4_rules_exit(net); +#endif + kfree(net->ipv4.fib_table_hash); + fib4_notifier_exit(net); goto out; } -static void __net_exit fib_net_exit(struct net *net) +static void __net_exit fib_net_pre_exit(struct net *net) { fib_proc_exit(net); nl_fib_lookup_exit(net); } -static void __net_exit fib_net_exit_batch(struct list_head *net_list) +static void __net_exit fib_net_exit_rtnl(struct net *net, + struct list_head *dev_kill_list) { - struct net *net; + ip_fib_net_exit(net); +} - rtnl_lock(); - list_for_each_entry(net, net_list, exit_list) { - __rtnl_net_lock(net); - ip_fib_net_exit(net); - __rtnl_net_unlock(net); - } - rtnl_unlock(); - - list_for_each_entry(net, net_list, exit_list) - fib4_semantics_exit(net); +static void __net_exit fib_net_exit(struct net *net) +{ +#ifdef CONFIG_IP_MULTIPLE_TABLES + fib4_rules_exit(net); +#endif + kfree(net->ipv4.fib_table_hash); + fib4_notifier_exit(net); + fib4_semantics_exit(net); } static struct pernet_operations fib_net_ops = { .init = fib_net_init, + .pre_exit = fib_net_pre_exit, + .exit_rtnl = fib_net_exit_rtnl, .exit = fib_net_exit, - .exit_batch = fib_net_exit_batch, }; static const struct rtnl_msg_handler fib_rtnl_msg_handlers[] __initconst = { diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 51f0193092f0..e068a5bace73 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -352,24 +352,17 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, static int fib4_rule_delete(struct fib_rule *rule) { struct net *net = rule->fr_net; - int err; - - /* split local/main if they are not already split */ - err = fib_unmerge(net); - if (err) - goto errout; #ifdef CONFIG_IP_ROUTE_CLASSID if (((struct fib4_rule *)rule)->tclassid) atomic_dec(&net->ipv4.fib_num_tclassid_users); #endif - net->ipv4.fib_has_custom_rules = true; if (net->ipv4.fib_rules_require_fldissect && fib_rule_requires_fldissect(rule)) net->ipv4.fib_rules_require_fldissect--; -errout: - return err; + + return 0; } static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3e8fadc28798..4f3c0740dde9 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -207,7 +207,6 @@ void fib_nh_common_release(struct fib_nh_common *nhc) rt_fibinfo_free(&nhc->nhc_rth_input); free_nh_exceptions(nhc); } -EXPORT_SYMBOL_GPL(fib_nh_common_release); void fib_nh_release(struct net *net, struct fib_nh *fib_nh) { @@ -639,7 +638,6 @@ int fib_nh_common_init(struct net *net, struct fib_nh_common *nhc, nhc->nhc_pcpu_rth_output = NULL; return err; } -EXPORT_SYMBOL_GPL(fib_nh_common_init); int fib_nh_init(struct net *net, struct fib_nh *nh, struct fib_config *cfg, int nh_weight, @@ -1642,7 +1640,6 @@ int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nhc, nla_put_failure: return -EMSGSIZE; } -EXPORT_SYMBOL_GPL(fib_nexthop_info); #if IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) || IS_ENABLED(CONFIG_IPV6) int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nhc, @@ -1675,7 +1672,6 @@ int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nhc, nla_put_failure: return -EMSGSIZE; } -EXPORT_SYMBOL_GPL(fib_add_nexthop); #endif #ifdef CONFIG_IP_ROUTE_MULTIPATH diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 1308213791f1..e11dc86ceda0 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2046,18 +2046,12 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) { struct fib_info *fi = fa->fa_info; - if (!fi || tb->tb_id != fa->tb_id || - (!(fi->fib_flags & RTNH_F_DEAD) && - !fib_props[fa->fa_type].error)) { + if (!fi || tb->tb_id != fa->tb_id) { slen = fa->fa_slen; continue; } - /* When not flushing the entire table, skip error - * routes that are not marked for deletion. - */ - if (!flush_all && fib_props[fa->fa_type].error && - !(fi->fib_flags & RTNH_F_DEAD)) { + if (!flush_all && !(fi->fib_flags & RTNH_F_DEAD)) { slen = fa->fa_slen; continue; } @@ -2172,10 +2166,14 @@ static int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, if (fa->fa_slen == last_slen) continue; + if (!fib_info_hold_safe(fa->fa_info)) + continue; + last_slen = fa->fa_slen; err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE, l->key, KEYLENGTH - fa->fa_slen, fa, extack); + fib_info_put(fa->fa_info); if (err) return err; } diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c index 5bae3cf7fe76..865bd7205122 100644 --- a/net/ipv4/fou_core.c +++ b/net/ipv4/fou_core.c @@ -22,7 +22,7 @@ #include "fou_nl.h" struct fou { - struct socket *sock; + struct sock *sk; u8 protocol; u8 flags; __be16 port; @@ -508,8 +508,8 @@ static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) static bool fou_cfg_cmp(struct fou *fou, struct fou_cfg *cfg) { - struct sock *sk = fou->sock->sk; struct udp_port_cfg *udp_cfg = &cfg->udp_config; + struct sock *sk = fou->sk; if (fou->family != udp_cfg->family || fou->port != udp_cfg->local_udp_port || @@ -558,11 +558,8 @@ static int fou_add_to_port_list(struct net *net, struct fou *fou, static void fou_release(struct fou *fou) { - struct socket *sock = fou->sock; - list_del(&fou->list); - udp_tunnel_sock_release(sock); - + udp_tunnel_sock_release(fou->sk); kfree_rcu(fou, rcu); } @@ -593,7 +590,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, fou->family = cfg->udp_config.family; fou->flags = cfg->flags; fou->type = cfg->type; - fou->sock = sock; + fou->sk = sk; memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); tunnel_cfg.encap_type = 1; @@ -618,7 +615,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, goto error; } - setup_udp_tunnel_sock(net, sock, &tunnel_cfg); + setup_udp_tunnel_sock(net, sk, &tunnel_cfg); sk->sk_allocation = GFP_ATOMIC; @@ -634,7 +631,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, error: kfree(fou); if (sock) - udp_tunnel_sock_release(sock); + udp_tunnel_sock_release(sock->sk); return err; } @@ -779,9 +776,9 @@ int fou_nl_del_doit(struct sk_buff *skb, struct genl_info *info) static int fou_fill_info(struct fou *fou, struct sk_buff *msg) { - struct sock *sk = fou->sock->sk; + struct sock *sk = fou->sk; - if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) || + if (nla_put_u8(msg, FOU_ATTR_AF, sk->sk_family) || nla_put_be16(msg, FOU_ATTR_PORT, fou->port) || nla_put_be16(msg, FOU_ATTR_PEER_PORT, sk->sk_dport) || nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) || @@ -793,7 +790,7 @@ static int fou_fill_info(struct fou *fou, struct sk_buff *msg) if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL)) return -1; - if (fou->sock->sk->sk_family == AF_INET) { + if (sk->sk_family == AF_INET) { if (nla_put_in_addr(msg, FOU_ATTR_LOCAL_V4, sk->sk_rcv_saddr)) return -1; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index a9ad39064f3b..b6337a47c141 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -220,8 +220,8 @@ static void igmp_stop_timer(struct ip_mc_list *im) spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) refcount_dec(&im->refcnt); - im->tm_running = 0; - im->reporter = 0; + WRITE_ONCE(im->tm_running, 0); + WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); } @@ -231,7 +231,7 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { int tv = get_random_u32_below(max_delay); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); if (refcount_inc_not_zero(&im->refcnt)) { if (mod_timer(&im->timer, jiffies + tv + 2)) ip_ma_put(im); @@ -267,7 +267,7 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) if (timer_delete(&im->timer)) { if ((long)(im->timer.expires-jiffies) < max_delay) { add_timer(&im->timer); - im->tm_running = 1; + WRITE_ONCE(im->tm_running, 1); spin_unlock_bh(&im->lock); return; } @@ -857,12 +857,12 @@ static void igmp_timer_expire(struct timer_list *t) struct in_device *in_dev = im->interface; spin_lock(&im->lock); - im->tm_running = 0; + WRITE_ONCE(im->tm_running, 0); if (im->unsolicit_count && --im->unsolicit_count) igmp_start_timer(im, unsolicited_report_interval(in_dev)); - im->reporter = 1; + WRITE_ONCE(im->reporter, 1); spin_unlock(&im->lock); if (IGMP_V1_SEEN(in_dev)) @@ -1002,7 +1002,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, * different encoding. We use the v3 encoding as more likely * to be intended in a v3 query. */ - max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); + max_delay = igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE); if (!max_delay) max_delay = 1; /* can't mod w/ 0 */ } else { /* v3 */ @@ -1019,7 +1019,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, ih3 = igmpv3_query_hdr(skb); } - max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); + max_delay = igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE); if (!max_delay) max_delay = 1; /* can't mod w/ 0 */ WRITE_ONCE(in_dev->mr_maxdelay, max_delay); @@ -1030,7 +1030,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, */ WRITE_ONCE(in_dev->mr_qrv, ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv)); - mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL; + mr_qi = igmpv3_qqi(ih3) * HZ ? : IGMP_QUERY_INTERVAL; WRITE_ONCE(in_dev->mr_qi, mr_qi); /* RFC3376, 8.3. Query Response Interval: * The number of seconds represented by the [Query Response @@ -1325,7 +1325,7 @@ static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp) !READ_ONCE(net->ipv4.sysctl_igmp_llm_reports)) return; - reporter = im->reporter; + reporter = READ_ONCE(im->reporter); igmp_stop_timer(im); if (!in_dev->dead) { @@ -1541,7 +1541,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, } if (im) { - im->users++; + WRITE_ONCE(im->users, im->users + 1); ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0); goto out; } @@ -1550,7 +1550,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, if (!im) goto out; - im->users = 1; + WRITE_ONCE(im->users, 1); im->interface = in_dev; in_dev_hold(in_dev); im->multiaddr = addr; @@ -1566,7 +1566,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr, #endif im->next_rcu = in_dev->mc_list; - in_dev->mc_count++; + WRITE_ONCE(in_dev->mc_count, in_dev->mc_count + 1); rcu_assign_pointer(in_dev->mc_list, im); ip_mc_hash_add(in_dev, im); @@ -1784,10 +1784,14 @@ void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp) (i = rtnl_dereference(*ip)) != NULL; ip = &i->next_rcu) { if (i->multiaddr == addr) { - if (--i->users == 0) { + int new_users = i->users - 1; + + WRITE_ONCE(i->users, new_users); + if (new_users == 0) { ip_mc_hash_remove(in_dev, i); *ip = i->next_rcu; - in_dev->mc_count--; + WRITE_ONCE(in_dev->mc_count, + in_dev->mc_count - 1); __igmp_group_dropped(i, gfp); inet_ifmcaddr_notify(in_dev->dev, i, RTM_DELMULTICAST); @@ -1919,7 +1923,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev) while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { in_dev->mc_list = i->next_rcu; - in_dev->mc_count--; + WRITE_ONCE(in_dev->mc_count, in_dev->mc_count - 1); ip_mc_clear_src(i); ip_ma_put(i); } @@ -2959,6 +2963,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) struct ip_mc_list *im = v; struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); char *querier; + int tm_running; long delta; #ifdef CONFIG_IP_MULTICAST @@ -2971,16 +2976,19 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) if (rcu_access_pointer(state->in_dev->mc_list) == im) { seq_printf(seq, "%d\t%-10s: %5d %7s\n", - state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); + state->dev->ifindex, state->dev->name, + READ_ONCE(state->in_dev->mc_count), + querier); } - delta = im->timer.expires - jiffies; + tm_running = READ_ONCE(im->tm_running); + delta = READ_ONCE(im->timer.expires) - jiffies; seq_printf(seq, "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", - im->multiaddr, im->users, - im->tm_running, - im->tm_running ? jiffies_delta_to_clock_t(delta) : 0, - im->reporter); + im->multiaddr, READ_ONCE(im->users), + tm_running, + tm_running ? jiffies_delta_to_clock_t(delta) : 0, + READ_ONCE(im->reporter)); } return 0; } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 5b934ce8d98a..56902bba5483 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -824,7 +824,6 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk, __IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); return NULL; } -EXPORT_SYMBOL_GPL(inet_csk_route_child_sock); /* Decide when to expire the request and when to resend SYN-ACK */ static void syn_ack_recalc(struct request_sock *req, @@ -901,7 +900,6 @@ struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, return req; } -EXPORT_SYMBOL(inet_reqsk_alloc); void __reqsk_free(struct request_sock *req) { @@ -911,7 +909,6 @@ void __reqsk_free(struct request_sock *req) kfree(req->saved_syn); kmem_cache_free(req->rsk_ops->slab, req); } -EXPORT_SYMBOL_GPL(__reqsk_free); static struct request_sock *inet_reqsk_clone(struct request_sock *req, struct sock *sk) @@ -1280,11 +1277,11 @@ void inet_csk_destroy_sock(struct sock *sk) sock_put(sk); } -EXPORT_SYMBOL(inet_csk_destroy_sock); void inet_csk_prepare_for_destroy_sock(struct sock *sk) { /* The below has to be done to allow calling inet_csk_destroy_sock */ + tcp_clear_sock_ops_cb_flags(sk); sock_set_flag(sk, SOCK_DEAD); tcp_orphan_count_inc(); } @@ -1301,7 +1298,6 @@ void inet_csk_prepare_forced_close(struct sock *sk) inet_csk_prepare_for_destroy_sock(sk); inet_sk(sk)->inet_num = 0; } -EXPORT_SYMBOL(inet_csk_prepare_forced_close); static int inet_ulp_can_listen(const struct sock *sk) { @@ -1405,7 +1401,6 @@ struct sock *inet_csk_reqsk_queue_add(struct sock *sk, spin_unlock(&queue->rskq_lock); return child; } -EXPORT_SYMBOL(inet_csk_reqsk_queue_add); struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child, struct request_sock *req, bool own_req) diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 1127519b8416..848c0f0c2ed9 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -24,8 +24,6 @@ #include #include -#include "../core/sock_destructor.h" - /* Use skb->cb to track consecutive/adjacent fragments coming at * the end of the queue. Nodes in the rb-tree queue will * contain "runs" of one or more adjacent fragments. @@ -524,7 +522,6 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, head = skb; goto out_restore_sk; } - FRAG_CB(fp)->next_frag = FRAG_CB(skb)->next_frag; if (RB_EMPTY_NODE(&skb->rbnode)) FRAG_CB(parent)->next_frag = fp; else @@ -539,7 +536,6 @@ void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, skb->destructor = NULL; } skb_morph(skb, head); - FRAG_CB(skb)->next_frag = FRAG_CB(head)->next_frag; rb_replace_node(&head->rbnode, &skb->rbnode, &q->rb_fragments); consume_skb(head); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 3e795547b40c..ba0faa9ae2bb 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -252,7 +252,6 @@ void inet_put_port(struct sock *sk) __inet_put_port(sk); local_bh_enable(); } -EXPORT_SYMBOL(inet_put_port); int __inet_inherit_port(const struct sock *sk, struct sock *child) { @@ -331,7 +330,6 @@ int __inet_inherit_port(const struct sock *sk, struct sock *child) spin_unlock(&head->lock); return -ENOMEM; } -EXPORT_SYMBOL_GPL(__inet_inherit_port); static struct inet_listen_hashbucket * inet_lhash2_bucket_sk(struct inet_hashinfo *h, struct sock *sk) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 169e2921a851..208dd48012d9 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -207,11 +207,11 @@ static int ipgre_err(struct sk_buff *skb, u32 info, if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) return 0; - if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO)) - t->err_count++; + if (time_before(jiffies, READ_ONCE(t->err_time) + IPTUNNEL_ERR_TIMEO)) + WRITE_ONCE(t->err_count, READ_ONCE(t->err_count) + 1); else - t->err_count = 1; - t->err_time = jiffies; + WRITE_ONCE(t->err_count, 1); + WRITE_ONCE(t->err_time, jiffies); return 0; } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 5bcd73cbdb41..3b4e9b8af044 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -195,7 +195,6 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, /* Send it out. */ return ip_local_out(net, skb->sk, skb); } -EXPORT_SYMBOL_GPL(ip_build_and_send_pkt); static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 50d0f5fe4e4c..9d114bd575f9 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -679,6 +679,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi4 fl4; bool md = false; bool connected; + int err_count; u8 tos, ttl; __be32 dst; __be16 df; @@ -807,14 +808,16 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, goto tx_error; } - if (tunnel->err_count > 0) { + err_count = READ_ONCE(tunnel->err_count); + if (err_count > 0) { if (time_before(jiffies, - tunnel->err_time + IPTUNNEL_ERR_TIMEO)) { - tunnel->err_count--; + READ_ONCE(tunnel->err_time) + IPTUNNEL_ERR_TIMEO)) { + WRITE_ONCE(tunnel->err_count, err_count - 1); dst_link_failure(skb); - } else - tunnel->err_count = 0; + } else { + WRITE_ONCE(tunnel->err_count, 0); + } } tos = ip_tunnel_ecn_encap(tos, inner_iph, skb); diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 9a45aed508d1..b1ea2d37e8c5 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -77,7 +77,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x) memcpy(&t->mark, &x->mark, sizeof(t->mark)); t->if_id = x->if_id; - if (xfrm_init_state(t)) + if (xfrm_init_state(t, NULL)) goto error; atomic_set(&t->tunnel_users, 1); diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index ff95b1b9908e..4f89a03e0b49 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -190,11 +190,11 @@ static int ipip_err(struct sk_buff *skb, u32 info) if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) goto out; - if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO)) - t->err_count++; + if (time_before(jiffies, READ_ONCE(t->err_time) + IPTUNNEL_ERR_TIMEO)) + WRITE_ONCE(t->err_count, READ_ONCE(t->err_count) + 1); else - t->err_count = 1; - t->err_time = jiffies; + WRITE_ONCE(t->err_count, 1); + WRITE_ONCE(t->err_time, jiffies); out: return err; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 2628cd3a93a6..1d9a4ac14fce 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -761,7 +761,8 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) struct sk_buff *skb; struct nlmsgerr *e; - atomic_dec(&mrt->cache_resolve_queue_len); + WRITE_ONCE(mrt->cache_resolve_queue_len, + mrt->cache_resolve_queue_len - 1); while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved))) { if (ip_hdr(skb)->version == 0) { @@ -1180,11 +1181,12 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, if (err < 0) goto err; - atomic_inc(&mrt->cache_resolve_queue_len); + WRITE_ONCE(mrt->cache_resolve_queue_len, + mrt->cache_resolve_queue_len + 1); list_add(&c->_c.list, &mrt->mfc_unres_queue); mroute_netlink_event(mrt, c, RTM_NEWROUTE); - if (atomic_read(&mrt->cache_resolve_queue_len) == 1) + if (mrt->cache_resolve_queue_len == 1) mod_timer(&mrt->ipmr_expire_timer, c->_c.mfc_un.unres.expires); } @@ -1297,7 +1299,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, if (uc->mfc_origin == c->mfc_origin && uc->mfc_mcastgrp == c->mfc_mcastgrp) { list_del(&_uc->list); - atomic_dec(&mrt->cache_resolve_queue_len); + WRITE_ONCE(mrt->cache_resolve_queue_len, + mrt->cache_resolve_queue_len - 1); found = true; break; } @@ -1356,7 +1359,7 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags, } if (flags & MRT_FLUSH_MFC) { - if (atomic_read(&mrt->cache_resolve_queue_len) != 0 || !check_net(net)) { + if (READ_ONCE(mrt->cache_resolve_queue_len) || !check_net(net)) { spin_lock_bh(&mfc_unres_lock); list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { list_del(&c->list); @@ -2777,9 +2780,7 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) { - struct fib_dump_filter filter = { - .rtnl_held = false, - }; + struct fib_dump_filter filter = {}; int err; rcu_read_lock(); @@ -2964,10 +2965,9 @@ static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb) { - u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len); - if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) || - nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) || + nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, + READ_ONCE(mrt->cache_resolve_queue_len)) || nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM, READ_ONCE(mrt->mroute_reg_vif_num)) || nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT, diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c index 3930d612c3de..867b24beded1 100644 --- a/net/ipv4/ipmr_base.c +++ b/net/ipv4/ipmr_base.c @@ -39,7 +39,7 @@ static void __mr_free_table(struct work_struct *work) void mr_table_free(struct mr_table *mrt) { - queue_rcu_work(system_unbound_wq, &mrt->work); + queue_rcu_work(system_dfl_wq, &mrt->work); } struct mr_table * diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 85502d4dfbb4..dbfb1c4739a8 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -51,3 +51,7 @@ obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o obj-$(CONFIG_NF_DUP_IPV4) += nf_dup_ipv4.o + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE := y +endif diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 0ea513bf77fb..a87e07e80d0d 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1427,6 +1427,8 @@ static int do_arpt_set_ctl(struct sock *sk, int cmd, sockptr_t arg, if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case ARPT_SO_SET_REPLACE: @@ -1455,6 +1457,8 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case ARPT_SO_GET_INFO: diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index ca8ff0ae6cdb..809441cedced 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1621,6 +1621,8 @@ do_ipt_set_ctl(struct sock *sk, int cmd, sockptr_t arg, unsigned int len) if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case IPT_SO_SET_REPLACE: @@ -1650,6 +1652,8 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case IPT_SO_GET_INFO: diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 10e1b0837731..183e8a3ff2ba 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -100,6 +100,9 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, __be16 port; union nf_inet_addr addr; + if (!info) + return -1; + for (i = 0; i < count; i++) { if (get_h225_addr(ct, *data, &taddr[i], &addr, &port)) { if (addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && @@ -184,6 +187,9 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, int i; u_int16_t nated_port; + if (!info) + return -1; + /* Set expectations for NAT */ rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; rtp_exp->expectfn = nf_nat_follow_master; @@ -325,6 +331,9 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = ntohs(port); + if (!info) + return -1; + /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = nf_nat_follow_master; @@ -404,6 +413,9 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, u_int16_t nated_port = ntohs(port); union nf_inet_addr addr; + if (!info) + return -1; + /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = ip_nat_q931_expect; diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index fab357cc8559..fed5249001a4 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -53,11 +53,13 @@ static void pptp_nat_expected(struct nf_conn *ct, struct nf_conn_nat *nat; nat = nf_ct_nat_ext_add(ct); - if (WARN_ON_ONCE(!nat)) + if (!nat) return; nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(master); + if (!ct_pptp_info) + return; /* And here goes the grand finale of corrosion... */ if (exp->dir == IP_CT_DIR_ORIGINAL) { @@ -132,11 +134,13 @@ pptp_outbound_pkt(struct sk_buff *skb, __be16 new_callid; unsigned int cid_off; - if (WARN_ON_ONCE(!nat)) + if (!nat) return NF_DROP; nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(ct); + if (!ct_pptp_info) + return NF_DROP; new_callid = ct_pptp_info->pns_call_id; @@ -204,11 +208,13 @@ pptp_exp_gre(struct nf_conntrack_expect *expect_orig, struct nf_ct_pptp_master *ct_pptp_info; struct nf_nat_pptp *nat_pptp_info; - if (WARN_ON_ONCE(!nat)) + if (!nat) return; nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(ct); + if (!ct_pptp_info) + return; /* save original PAC call ID in nat_info */ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; @@ -241,7 +247,7 @@ pptp_inbound_pkt(struct sk_buff *skb, __be16 new_pcid; unsigned int pcid_off; - if (WARN_ON_ONCE(!nat)) + if (!nat) return NF_DROP; nat_pptp_info = &nat->help.nat_pptp_info; diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c index 717b726504fe..0ede138dfd29 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c @@ -202,29 +202,34 @@ static const struct nf_conntrack_expect_policy snmp_exp_policy = { .timeout = 180, }; -static struct nf_conntrack_helper snmp_trap_helper __read_mostly = { - .me = THIS_MODULE, - .help = help, - .expect_policy = &snmp_exp_policy, - .name = "snmp_trap", - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(SNMP_TRAP_PORT), - .tuple.dst.protonum = IPPROTO_UDP, -}; +static struct nf_conntrack_helper snmp_trap_helper __read_mostly; +static struct nf_conntrack_helper *snmp_trap_helper_ptr __read_mostly; static int __init nf_nat_snmp_basic_init(void) { + int err; + BUG_ON(nf_nat_snmp_hook != NULL); RCU_INIT_POINTER(nf_nat_snmp_hook, help); - return nf_conntrack_helper_register(&snmp_trap_helper); + nf_ct_helper_init(&snmp_trap_helper, AF_INET, IPPROTO_UDP, + "snmp_trap", SNMP_TRAP_PORT, SNMP_TRAP_PORT, SNMP_TRAP_PORT, + &snmp_exp_policy, 0, help, NULL, THIS_MODULE); + + err = nf_conntrack_helper_register(&snmp_trap_helper, &snmp_trap_helper_ptr); + if (err < 0) { + RCU_INIT_POINTER(nf_nat_snmp_hook, NULL); + return err; + } + + return 0; } static void __exit nf_nat_snmp_basic_fini(void) { RCU_INIT_POINTER(nf_nat_snmp_hook, NULL); synchronize_rcu(); - nf_conntrack_helper_unregister(&snmp_trap_helper); + nf_conntrack_helper_unregister(snmp_trap_helper_ptr); } module_init(nf_nat_snmp_basic_init); diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index f92fcc39fc4c..6205bd57aa85 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -20,8 +20,8 @@ #define NH_RES_DEFAULT_IDLE_TIMER (120 * HZ) #define NH_RES_DEFAULT_UNBALANCED_TIMER 0 /* No forced rebalancing. */ -static void remove_nexthop(struct net *net, struct nexthop *nh, - struct nl_info *nlinfo); +static bool __must_check remove_nexthop(struct net *net, struct nexthop *nh, + struct nl_info *nlinfo); #define NH_DEV_HASHBITS 8 #define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS) @@ -2016,9 +2016,9 @@ static void nh_hthr_group_rebalance(struct nh_group *nhg) } } -static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, - struct nl_info *nlinfo, - struct list_head *deferred_free) +static bool __must_check +remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, + struct nl_info *nlinfo, struct list_head *deferred_free) { struct nh_grp_entry *nhges, *new_nhges; struct nexthop *nhp = nhge->nh_parent; @@ -2033,10 +2033,8 @@ static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, newg = nhg->spare; /* last entry, keep it visible and remove the parent */ - if (nhg->num_nh == 1) { - remove_nexthop(net, nhp, nlinfo); - return; - } + if (nhg->num_nh == 1) + return remove_nexthop(net, nhp, nlinfo); newg->has_v4 = false; newg->is_multipath = nhg->is_multipath; @@ -2093,22 +2091,27 @@ static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, if (nlinfo) nexthop_notify(RTM_NEWNEXTHOP, nhp, nlinfo); + + return false; } -static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh, - struct nl_info *nlinfo) +static bool __must_check +remove_nexthop_from_groups(struct net *net, struct nexthop *nh, + struct nl_info *nlinfo) { struct nh_grp_entry *nhge, *tmp; LIST_HEAD(deferred_free); + bool need_flush = false; /* If there is nothing to do, let's avoid the costly call to * synchronize_net() */ if (list_empty(&nh->grp_list)) - return; + return false; list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list) - remove_nh_grp_entry(net, nhge, nlinfo, &deferred_free); + need_flush |= remove_nh_grp_entry(net, nhge, nlinfo, + &deferred_free); /* make sure all see the newly published array before releasing rtnl */ synchronize_net(); @@ -2118,6 +2121,8 @@ static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh, list_del(&nhge->nh_list); free_percpu(nhge->stats); } + + return need_flush; } static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo) @@ -2142,18 +2147,15 @@ static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo) } /* not called for nexthop replace */ -static void __remove_nexthop_fib(struct net *net, struct nexthop *nh) +static bool __must_check __remove_nexthop_fib(struct net *net, + struct nexthop *nh) { + bool need_flush = !list_empty(&nh->fi_list); struct fib6_info *f6i; - bool do_flush = false; struct fib_info *fi; - list_for_each_entry(fi, &nh->fi_list, nh_list) { + list_for_each_entry(fi, &nh->fi_list, nh_list) fi->fib_flags |= RTNH_F_DEAD; - do_flush = true; - } - if (do_flush) - fib_flush(net); spin_lock_bh(&nh->lock); @@ -2173,12 +2175,14 @@ static void __remove_nexthop_fib(struct net *net, struct nexthop *nh) } spin_unlock_bh(&nh->lock); + + return need_flush; } -static void __remove_nexthop(struct net *net, struct nexthop *nh, - struct nl_info *nlinfo) +static bool __must_check __remove_nexthop(struct net *net, struct nexthop *nh, + struct nl_info *nlinfo) { - __remove_nexthop_fib(net, nh); + bool need_flush = __remove_nexthop_fib(net, nh); if (nh->is_group) { remove_nexthop_group(nh, nlinfo); @@ -2189,13 +2193,17 @@ static void __remove_nexthop(struct net *net, struct nexthop *nh, if (nhi->fib_nhc.nhc_dev) hlist_del(&nhi->dev_hash); - remove_nexthop_from_groups(net, nh, nlinfo); + need_flush |= remove_nexthop_from_groups(net, nh, nlinfo); } + + return need_flush; } -static void remove_nexthop(struct net *net, struct nexthop *nh, - struct nl_info *nlinfo) +static bool __must_check remove_nexthop(struct net *net, struct nexthop *nh, + struct nl_info *nlinfo) { + bool need_flush; + call_nexthop_notifiers(net, NEXTHOP_EVENT_DEL, nh, NULL); /* remove from the tree */ @@ -2204,10 +2212,19 @@ static void remove_nexthop(struct net *net, struct nexthop *nh, if (nlinfo) nexthop_notify(RTM_DELNEXTHOP, nh, nlinfo); - __remove_nexthop(net, nh, nlinfo); + need_flush = __remove_nexthop(net, nh, nlinfo); nh_base_seq_inc(net); nexthop_put(nh); + + return need_flush; +} + +static void remove_one_nexthop(struct net *net, struct nexthop *nh, + struct nl_info *nlinfo) +{ + if (remove_nexthop(net, nh, nlinfo)) + fib_flush(net); } /* if any FIB entries reference this nexthop, any dst entries @@ -2592,7 +2609,7 @@ static int replace_nexthop(struct net *net, struct nexthop *old, if (!err) { nh_rt_cache_flush(net, old, new); - __remove_nexthop(net, new, NULL); + WARN_ON_ONCE(__remove_nexthop(net, new, NULL)); nexthop_put(new); } @@ -2699,6 +2716,7 @@ static void nexthop_flush_dev(struct net_device *dev, unsigned long event) unsigned int hash = nh_dev_hashfn(dev->ifindex); struct net *net = dev_net(dev); struct hlist_head *head = &net->nexthop.devhash[hash]; + bool need_flush = false; struct hlist_node *n; struct nh_info *nhi; @@ -2710,22 +2728,28 @@ static void nexthop_flush_dev(struct net_device *dev, unsigned long event) (event == NETDEV_DOWN || event == NETDEV_CHANGE)) continue; - remove_nexthop(net, nhi->nh_parent, NULL); + need_flush |= remove_nexthop(net, nhi->nh_parent, NULL); } + + if (need_flush) + fib_flush(net); } /* rtnl; called when net namespace is deleted */ static void flush_all_nexthops(struct net *net) { struct rb_root *root = &net->nexthop.rb_root; + bool need_flush = false; struct rb_node *node; struct nexthop *nh; while ((node = rb_first(root))) { nh = rb_entry(node, struct nexthop, rb_node); - remove_nexthop(net, nh, NULL); + need_flush |= remove_nexthop(net, nh, NULL); cond_resched(); } + if (need_flush) + fib_flush(net); } static struct nexthop *nexthop_create_group(struct net *net, @@ -2994,7 +3018,7 @@ static struct nexthop *nexthop_add(struct net *net, struct nh_config *cfg, err = insert_nexthop(net, nh, cfg, extack); if (err) { - __remove_nexthop(net, nh, NULL); + WARN_ON_ONCE(__remove_nexthop(net, nh, NULL)); nexthop_put(nh); nh = ERR_PTR(err); } @@ -3363,7 +3387,7 @@ static int rtm_del_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh, nh = nexthop_find_by_id(net, id); if (nh) - remove_nexthop(net, nh, &nlinfo); + remove_one_nexthop(net, nh, &nlinfo); else err = -ENOENT; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 68e88cb3e55c..e9fbab6ad914 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -103,7 +103,6 @@ int raw_hash_sk(struct sock *sk) return 0; } -EXPORT_SYMBOL_GPL(raw_hash_sk); void raw_unhash_sk(struct sock *sk) { @@ -114,7 +113,6 @@ void raw_unhash_sk(struct sock *sk) sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); spin_unlock(&h->lock); } -EXPORT_SYMBOL_GPL(raw_unhash_sk); bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num, __be32 raddr, __be32 laddr, int dif, int sdif) @@ -928,7 +926,6 @@ int raw_abort(struct sock *sk, int err) return 0; } -EXPORT_SYMBOL_GPL(raw_abort); struct proto raw_prot = { .name = "RAW", @@ -1009,7 +1006,6 @@ void *raw_seq_start(struct seq_file *seq, loff_t *pos) return *pos ? raw_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; } -EXPORT_SYMBOL_GPL(raw_seq_start); void *raw_seq_next(struct seq_file *seq, void *v, loff_t *pos) { @@ -1022,7 +1018,6 @@ void *raw_seq_next(struct seq_file *seq, void *v, loff_t *pos) ++*pos; return sk; } -EXPORT_SYMBOL_GPL(raw_seq_next); void raw_seq_stop(struct seq_file *seq, void *v) __releases(&h->lock) @@ -1031,7 +1026,6 @@ void raw_seq_stop(struct seq_file *seq, void *v) spin_unlock(&h->lock); } -EXPORT_SYMBOL_GPL(raw_seq_stop); static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i) { diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3d62d45d84bd..3f3de5164d6e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1171,7 +1171,6 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) bh_unlock_sock(sk); dst_release(odst); } -EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); void ipv4_redirect(struct sk_buff *skb, struct net *net, int oif, u8 protocol) @@ -1203,7 +1202,6 @@ void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk) ip_rt_put(rt); } } -EXPORT_SYMBOL_GPL(ipv4_sk_redirect); INDIRECT_CALLABLE_SCOPE struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) @@ -1701,7 +1699,6 @@ struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt) } return new_rt; } -EXPORT_SYMBOL(rt_dst_clone); /* called in rcu_read_lock() section */ enum skb_drop_reason diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index df479277fb80..73e129768184 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -280,9 +280,13 @@ static int cookie_tcp_reqsk_init(struct sock *sk, struct sk_buff *skb, treq->snt_synack = 0; treq->snt_tsval_first = 0; treq->tfo_listener = false; - treq->txhash = net_tx_rndhash(); treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = ntohl(th->ack_seq) - 1; + /* The request socket was freed after the SYN-ACK; use the cookie + * (snt_isn) as txhash so the full socket and the SYN-ACK make the + * same egress choice (IPv6 ECMP path; IPv4 TX queue). + */ + treq->txhash = treq->snt_isn; treq->syn_tos = TCP_SKB_CB(skb)->ip_dsfield; #if IS_ENABLED(CONFIG_MPTCP) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 389a7cc17110..455441f1b694 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -297,7 +297,6 @@ enum { }; DEFINE_PER_CPU(unsigned int, tcp_orphan_count); -EXPORT_PER_CPU_SYMBOL_GPL(tcp_orphan_count); long sysctl_tcp_mem[3] __read_mostly; @@ -3027,7 +3026,6 @@ void tcp_set_state(struct sock *sk, int state) */ inet_sk_state_store(sk, state); } -EXPORT_SYMBOL_GPL(tcp_set_state); /* * State processing on a close. This implements the state shift for @@ -3506,7 +3504,6 @@ int tcp_disconnect(struct sock *sk, int flags) sk_error_report(sk); return 0; } -EXPORT_SYMBOL(tcp_disconnect); static inline bool tcp_can_repair_sock(const struct sock *sk) { @@ -5104,7 +5101,6 @@ void tcp_done(struct sock *sk) else inet_csk_destroy_sock(sk); } -EXPORT_SYMBOL_GPL(tcp_done); int tcp_abort(struct sock *sk, int err) { @@ -5227,9 +5223,13 @@ static void __init tcp_struct_check(void) CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_read_rx, snd_ssthresh); /* TX read-write hotpath cache lines */ - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, segs_out); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, data_segs_out); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, delivered); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, delivered_ce); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, bytes_acked); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, bytes_sent); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, first_tx_mstamp); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, delivered_mstamp); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, snd_sml); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, chrono_start); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, chrono_stat); @@ -5240,6 +5240,10 @@ static void __init tcp_struct_check(void) CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, tcp_wstamp_ns); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, accecn_opt_tstamp); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, rtt_seq); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, max_packets_out); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, cwnd_usage_seq); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, rate_delivered); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, rate_interval_us); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, tsorted_sent_queue); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, highest_sack); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_tx, ecn_flags); @@ -5255,8 +5259,6 @@ static void __init tcp_struct_check(void) CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, srtt_us); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, packets_out); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, snd_up); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, delivered); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, delivered_ce); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, received_ce); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, received_ecn_bytes); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, app_limited); @@ -5264,22 +5266,16 @@ static void __init tcp_struct_check(void) CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, rcv_mwnd_seq); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, rcv_tstamp); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, rx_opt); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, segs_in); + CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_txrx, segs_out); /* RX read-write hotpath cache lines */ CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, bytes_received); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, segs_in); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, data_segs_in); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rcv_wup); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, max_packets_out); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, cwnd_usage_seq); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rate_delivered); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rate_interval_us); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rcv_rtt_last_tsecr); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, delivered_ecn_bytes); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, pkts_acked_ewma); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, first_tx_mstamp); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, delivered_mstamp); - CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, bytes_acked); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rcv_rtt_est); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rcvq_space); } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 0a4b38b315fe..2f69bcecae78 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -9,7 +9,9 @@ */ #define pr_fmt(fmt) "TCP: " fmt -#include +#include +#include +#include #include #include #include @@ -21,32 +23,133 @@ DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ); -int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, - unsigned int len, struct tcp_sigpool *hp) +static const struct tcp_ao_algo { + const char *name; + unsigned int digest_size; +} tcp_ao_algos[] = { + [TCP_AO_ALGO_HMAC_SHA1] = { + .name = "hmac(sha1)", + .digest_size = SHA1_DIGEST_SIZE, + }, + [TCP_AO_ALGO_HMAC_SHA256] = { + .name = "hmac(sha256)", + .digest_size = SHA256_DIGEST_SIZE, + }, + [TCP_AO_ALGO_AES_128_CMAC] = { + .name = "cmac(aes128)", + .digest_size = AES_BLOCK_SIZE, /* same as AES_KEYSIZE_128 */ + }, +}; + +struct tcp_ao_mac_ctx { + enum tcp_ao_algo_id algo; + union { + struct hmac_sha1_ctx hmac_sha1; + struct hmac_sha256_ctx hmac_sha256; + struct { + struct aes_cmac_key key; + struct aes_cmac_ctx ctx; + } aes_cmac; + }; +}; + +static const struct tcp_ao_algo *tcp_ao_find_algo(const char *name) { - struct scatterlist sg; - int ret; + for (size_t i = 0; i < ARRAY_SIZE(tcp_ao_algos); i++) { + const struct tcp_ao_algo *algo = &tcp_ao_algos[i]; - if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), - mkt->key, mkt->keylen)) - goto clear_hash; + if (!algo->name) + continue; + if (WARN_ON_ONCE(algo->digest_size > TCP_AO_MAX_MAC_LEN || + algo->digest_size > + TCP_AO_MAX_TRAFFIC_KEY_LEN)) + continue; + if (strcmp(name, algo->name) == 0) + return algo; + } + return NULL; +} - ret = crypto_ahash_init(hp->req); - if (ret) - goto clear_hash; +static void tcp_ao_mac_init(struct tcp_ao_mac_ctx *mac_ctx, + enum tcp_ao_algo_id algo, const u8 *traffic_key) +{ + mac_ctx->algo = algo; + switch (mac_ctx->algo) { + case TCP_AO_ALGO_HMAC_SHA1: + hmac_sha1_init_usingrawkey(&mac_ctx->hmac_sha1, traffic_key, + SHA1_DIGEST_SIZE); + return; + case TCP_AO_ALGO_HMAC_SHA256: + hmac_sha256_init_usingrawkey(&mac_ctx->hmac_sha256, traffic_key, + SHA256_DIGEST_SIZE); + return; + case TCP_AO_ALGO_AES_128_CMAC: + aes_cmac_preparekey(&mac_ctx->aes_cmac.key, traffic_key, + AES_KEYSIZE_128); + aes_cmac_init(&mac_ctx->aes_cmac.ctx, &mac_ctx->aes_cmac.key); + return; + default: + WARN_ON_ONCE(1); /* algo was validated earlier. */ + } +} - sg_init_one(&sg, ctx, len); - ahash_request_set_crypt(hp->req, &sg, key, len); - crypto_ahash_update(hp->req); +void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data, + size_t data_len) +{ + switch (mac_ctx->algo) { + case TCP_AO_ALGO_HMAC_SHA1: + hmac_sha1_update(&mac_ctx->hmac_sha1, data, data_len); + return; + case TCP_AO_ALGO_HMAC_SHA256: + hmac_sha256_update(&mac_ctx->hmac_sha256, data, data_len); + return; + case TCP_AO_ALGO_AES_128_CMAC: + aes_cmac_update(&mac_ctx->aes_cmac.ctx, data, data_len); + return; + default: + WARN_ON_ONCE(1); /* algo was validated earlier. */ + } +} - ret = crypto_ahash_final(hp->req); - if (ret) - goto clear_hash; +static void tcp_ao_mac_final(struct tcp_ao_mac_ctx *mac_ctx, u8 *out) +{ + switch (mac_ctx->algo) { + case TCP_AO_ALGO_HMAC_SHA1: + hmac_sha1_final(&mac_ctx->hmac_sha1, out); + return; + case TCP_AO_ALGO_HMAC_SHA256: + hmac_sha256_final(&mac_ctx->hmac_sha256, out); + return; + case TCP_AO_ALGO_AES_128_CMAC: + aes_cmac_final(&mac_ctx->aes_cmac.ctx, out); + return; + default: + WARN_ON_ONCE(1); /* algo was validated earlier. */ + } +} - return 0; -clear_hash: - memset(key, 0, tcp_ao_digest_size(mkt)); - return 1; +void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key, + const void *input, unsigned int input_len) +{ + switch (mkt->algo) { + case TCP_AO_ALGO_HMAC_SHA1: + hmac_sha1_usingrawkey(mkt->key, mkt->keylen, input, input_len, + traffic_key); + return; + case TCP_AO_ALGO_HMAC_SHA256: + hmac_sha256_usingrawkey(mkt->key, mkt->keylen, input, input_len, + traffic_key); + return; + case TCP_AO_ALGO_AES_128_CMAC: { + struct aes_cmac_key k; + + aes_cmac_preparekey(&k, mkt->key, AES_KEYSIZE_128); + aes_cmac(&k, input, input_len, traffic_key); + return; + } + default: + WARN_ON_ONCE(1); /* algo was validated earlier. */ + } } bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code) @@ -255,7 +358,6 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, *new_key = *key; INIT_HLIST_NODE(&new_key->node); - tcp_sigpool_get(new_key->tcp_sigpool_id); atomic64_set(&new_key->pkt_good, 0); atomic64_set(&new_key->pkt_bad, 0); @@ -266,7 +368,6 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head) { struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); - tcp_sigpool_release(key->tcp_sigpool_id); kfree_sensitive(key); } @@ -277,7 +378,6 @@ static void tcp_ao_info_free(struct tcp_ao_info *ao) hlist_for_each_entry_safe(key, n, &ao->head, node) { hlist_del(&key->node); - tcp_sigpool_release(key->tcp_sigpool_id); kfree_sensitive(key); } kfree(ao); @@ -336,10 +436,10 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) } /* 4 tuple and ISNs are expected in NBO */ -static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, - __be32 saddr, __be32 daddr, - __be16 sport, __be16 dport, - __be32 sisn, __be32 disn) +static void tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, + __be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, + __be32 sisn, __be32 disn) { /* See RFC5926 3.1.1 */ struct kdf_input_block { @@ -347,148 +447,146 @@ static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, u8 label[6]; struct tcp4_ao_context ctx; __be16 outlen; - } __packed * tmp; - struct tcp_sigpool hp; - int err; + } __packed input = { + .counter = 1, + .label = "TCP-AO", + .ctx = { + .saddr = saddr, + .daddr = daddr, + .sport = sport, + .dport = dport, + .sisn = sisn, + .disn = disn, + }, + .outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */ + }; - err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); - if (err) - return err; - - tmp = hp.scratch; - tmp->counter = 1; - memcpy(tmp->label, "TCP-AO", 6); - tmp->ctx.saddr = saddr; - tmp->ctx.daddr = daddr; - tmp->ctx.sport = sport; - tmp->ctx.dport = dport; - tmp->ctx.sisn = sisn; - tmp->ctx.disn = disn; - tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ - - err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); - tcp_sigpool_end(&hp); - - return err; + tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input)); } -int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, - const struct sock *sk, - __be32 sisn, __be32 disn, bool send) +void tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send) { if (send) - return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, - sk->sk_daddr, htons(sk->sk_num), - sk->sk_dport, sisn, disn); + tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, sk->sk_daddr, + htons(sk->sk_num), sk->sk_dport, sisn, disn); else - return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, - sk->sk_rcv_saddr, sk->sk_dport, - htons(sk->sk_num), disn, sisn); + tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, sk->sk_rcv_saddr, + sk->sk_dport, htons(sk->sk_num), disn, sisn); } static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send) { - if (mkt->family == AF_INET) - return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); + if (mkt->family == AF_INET) { + tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); + return 0; + } #if IS_ENABLED(CONFIG_IPV6) - else if (mkt->family == AF_INET6) - return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); + if (mkt->family == AF_INET6) { + tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); + return 0; + } #endif - else - return -EOPNOTSUPP; + return -EOPNOTSUPP; } -int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, - struct request_sock *req) +void tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) { struct inet_request_sock *ireq = inet_rsk(req); - return tcp_v4_ao_calc_key(mkt, key, - ireq->ir_loc_addr, ireq->ir_rmt_addr, - htons(ireq->ir_num), ireq->ir_rmt_port, - htonl(tcp_rsk(req)->snt_isn), - htonl(tcp_rsk(req)->rcv_isn)); + tcp_v4_ao_calc_key(mkt, key, ireq->ir_loc_addr, ireq->ir_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); } -static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, - const struct sk_buff *skb, - __be32 sisn, __be32 disn) +static void tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn) { const struct iphdr *iph = ip_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); - return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, - th->source, th->dest, sisn, disn); + tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, th->source, + th->dest, sisn, disn); } static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, const struct sk_buff *skb, __be32 sisn, __be32 disn, int family) { - if (family == AF_INET) - return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); + if (family == AF_INET) { + tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); + return 0; + } #if IS_ENABLED(CONFIG_IPV6) - else if (family == AF_INET6) - return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); + if (family == AF_INET6) { + tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); + return 0; + } #endif return -EAFNOSUPPORT; } -static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, - __be32 daddr, __be32 saddr, - int nbytes) +static void tcp_v4_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx, + __be32 daddr, __be32 saddr, int nbytes) { - struct tcp4_pseudohdr *bp; - struct scatterlist sg; + struct tcp4_pseudohdr phdr = { + .saddr = saddr, + .daddr = daddr, + .pad = 0, + .protocol = IPPROTO_TCP, + .len = cpu_to_be16(nbytes), + }; - bp = hp->scratch; - bp->saddr = saddr; - bp->daddr = daddr; - bp->pad = 0; - bp->protocol = IPPROTO_TCP; - bp->len = cpu_to_be16(nbytes); - - sg_init_one(&sg, bp, sizeof(*bp)); - ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); - return crypto_ahash_update(hp->req); + tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr)); } static int tcp_ao_hash_pseudoheader(unsigned short int family, const struct sock *sk, const struct sk_buff *skb, - struct tcp_sigpool *hp, int nbytes) + struct tcp_ao_mac_ctx *mac_ctx, int nbytes) { const struct tcphdr *th = tcp_hdr(skb); /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ if (!th->check) { - if (family == AF_INET) - return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, - sk->sk_rcv_saddr, skb->len); + if (family == AF_INET) { + tcp_v4_ao_hash_pseudoheader(mac_ctx, sk->sk_daddr, + sk->sk_rcv_saddr, skb->len); + return 0; + } #if IS_ENABLED(CONFIG_IPV6) - else if (family == AF_INET6) - return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr, - &sk->sk_v6_rcv_saddr, skb->len); + if (family == AF_INET6) { + tcp_v6_ao_hash_pseudoheader(mac_ctx, &sk->sk_v6_daddr, + &sk->sk_v6_rcv_saddr, + skb->len); + return 0; + } #endif - else - return -EAFNOSUPPORT; + return -EAFNOSUPPORT; } if (family == AF_INET) { const struct iphdr *iph = ip_hdr(skb); - return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr, - iph->saddr, skb->len); + tcp_v4_ao_hash_pseudoheader(mac_ctx, iph->daddr, iph->saddr, + skb->len); + return 0; + } #if IS_ENABLED(CONFIG_IPV6) - } else if (family == AF_INET6) { + if (family == AF_INET6) { const struct ipv6hdr *iph = ipv6_hdr(skb); - return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr, - &iph->saddr, skb->len); -#endif + tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr, &iph->saddr, + skb->len); + return 0; } +#endif return -EAFNOSUPPORT; } @@ -507,31 +605,20 @@ u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq) return sne; } -/* tcp_ao_hash_sne(struct tcp_sigpool *hp) - * @hp - used for hashing - * @sne - sne value - */ -static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne) +static void tcp_ao_hash_sne(struct tcp_ao_mac_ctx *mac_ctx, u32 sne) { - struct scatterlist sg; - __be32 *bp; + __be32 sne_be32 = htonl(sne); - bp = (__be32 *)hp->scratch; - *bp = htonl(sne); - - sg_init_one(&sg, bp, sizeof(*bp)); - ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); - return crypto_ahash_update(hp->req); + tcp_ao_mac_update(mac_ctx, &sne_be32, sizeof(sne_be32)); } -static int tcp_ao_hash_header(struct tcp_sigpool *hp, - const struct tcphdr *th, - bool exclude_options, u8 *hash, - int hash_offset, int hash_len) +static void tcp_ao_hash_header(struct tcp_ao_mac_ctx *mac_ctx, + const struct tcphdr *th, bool exclude_options, + u8 *hash, int hash_offset, int hash_len) { - struct scatterlist sg; - u8 *hdr = hp->scratch; - int err, len; + /* Full TCP header (th->doff << 2) should fit into scratch area. */ + u8 hdr[60]; + int len; /* We are not allowed to change tcphdr, make a local copy */ if (exclude_options) { @@ -551,11 +638,7 @@ static int tcp_ao_hash_header(struct tcp_sigpool *hp, memset(hdr + hash_offset, 0, hash_len); } - sg_init_one(&sg, hdr, len); - ahash_request_set_crypt(hp->req, &sg, NULL, len); - err = crypto_ahash_update(hp->req); - WARN_ON_ONCE(err != 0); - return err; + tcp_ao_mac_update(mac_ctx, hdr, len); } int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, @@ -564,109 +647,92 @@ int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, const union tcp_ao_addr *saddr, const struct tcphdr *th, u32 sne) { - int tkey_len = tcp_ao_digest_size(key); int hash_offset = ao_hash - (char *)th; - struct tcp_sigpool hp; - void *hash_buf = NULL; + struct tcp_ao_mac_ctx mac_ctx; + u8 hash_buf[TCP_AO_MAX_MAC_LEN]; - hash_buf = kmalloc(tkey_len, GFP_ATOMIC); - if (!hash_buf) - goto clear_hash_noput; - - if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) - goto clear_hash_noput; - - if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) - goto clear_hash; - - if (crypto_ahash_init(hp.req)) - goto clear_hash; - - if (tcp_ao_hash_sne(&hp, sne)) - goto clear_hash; + tcp_ao_mac_init(&mac_ctx, key->algo, tkey); + tcp_ao_hash_sne(&mac_ctx, sne); if (family == AF_INET) { - if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr, - saddr->a4.s_addr, th->doff * 4)) - goto clear_hash; + tcp_v4_ao_hash_pseudoheader(&mac_ctx, daddr->a4.s_addr, + saddr->a4.s_addr, th->doff * 4); #if IS_ENABLED(CONFIG_IPV6) } else if (family == AF_INET6) { - if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6, - &saddr->a6, th->doff * 4)) - goto clear_hash; + tcp_v6_ao_hash_pseudoheader(&mac_ctx, &daddr->a6, + &saddr->a6, th->doff * 4); #endif } else { WARN_ON_ONCE(1); goto clear_hash; } - if (tcp_ao_hash_header(&hp, th, - !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), - ao_hash, hash_offset, tcp_ao_maclen(key))) - goto clear_hash; - ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); - if (crypto_ahash_final(hp.req)) - goto clear_hash; + tcp_ao_hash_header(&mac_ctx, th, + !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), + ao_hash, hash_offset, tcp_ao_maclen(key)); + tcp_ao_mac_final(&mac_ctx, hash_buf); memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); - tcp_sigpool_end(&hp); - kfree(hash_buf); return 0; clear_hash: - tcp_sigpool_end(&hp); -clear_hash_noput: memset(ao_hash, 0, tcp_ao_maclen(key)); - kfree(hash_buf); return 1; } +static void tcp_ao_hash_skb_data(struct tcp_ao_mac_ctx *mac_ctx, + const struct sk_buff *skb, + unsigned int header_len) +{ + const unsigned int head_data_len = skb_headlen(skb) > header_len ? + skb_headlen(skb) - header_len : 0; + const struct skb_shared_info *shi = skb_shinfo(skb); + struct sk_buff *frag_iter; + unsigned int i; + + tcp_ao_mac_update(mac_ctx, (const u8 *)tcp_hdr(skb) + header_len, + head_data_len); + + for (i = 0; i < shi->nr_frags; ++i) { + const skb_frag_t *f = &shi->frags[i]; + u32 p_off, p_len, copied; + const void *vaddr; + struct page *p; + + skb_frag_foreach_page(f, skb_frag_off(f), skb_frag_size(f), + p, p_off, p_len, copied) { + vaddr = kmap_local_page(p); + tcp_ao_mac_update(mac_ctx, vaddr + p_off, p_len); + kunmap_local(vaddr); + } + } + + skb_walk_frags(skb, frag_iter) + tcp_ao_hash_skb_data(mac_ctx, frag_iter, 0); +} + int tcp_ao_hash_skb(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne) { const struct tcphdr *th = tcp_hdr(skb); - int tkey_len = tcp_ao_digest_size(key); - struct tcp_sigpool hp; - void *hash_buf = NULL; + struct tcp_ao_mac_ctx mac_ctx; + u8 hash_buf[TCP_AO_MAX_MAC_LEN]; - hash_buf = kmalloc(tkey_len, GFP_ATOMIC); - if (!hash_buf) - goto clear_hash_noput; - - if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) - goto clear_hash_noput; - - if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) - goto clear_hash; - - /* For now use sha1 by default. Depends on alg in tcp_ao_key */ - if (crypto_ahash_init(hp.req)) - goto clear_hash; - - if (tcp_ao_hash_sne(&hp, sne)) - goto clear_hash; - if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) - goto clear_hash; - if (tcp_ao_hash_header(&hp, th, - !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), - ao_hash, hash_offset, tcp_ao_maclen(key))) - goto clear_hash; - if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2)) - goto clear_hash; - ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); - if (crypto_ahash_final(hp.req)) + tcp_ao_mac_init(&mac_ctx, key->algo, tkey); + tcp_ao_hash_sne(&mac_ctx, sne); + if (tcp_ao_hash_pseudoheader(family, sk, skb, &mac_ctx, skb->len)) goto clear_hash; + tcp_ao_hash_header(&mac_ctx, th, + !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), + ao_hash, hash_offset, tcp_ao_maclen(key)); + tcp_ao_hash_skb_data(&mac_ctx, skb, th->doff << 2); + tcp_ao_mac_final(&mac_ctx, hash_buf); memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); - tcp_sigpool_end(&hp); - kfree(hash_buf); return 0; clear_hash: - tcp_sigpool_end(&hp); -clear_hash_noput: memset(ao_hash, 0, tcp_ao_maclen(key)); - kfree(hash_buf); return 1; } @@ -682,22 +748,12 @@ int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, struct request_sock *req, const struct sk_buff *skb, int hash_offset, u32 sne) { - void *hash_buf = NULL; - int err; + u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN]; - hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); - if (!hash_buf) - return -ENOMEM; + tcp_v4_ao_calc_key_rsk(ao_key, tkey_buf, req); - err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req); - if (err) - goto out; - - err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, - hash_buf, hash_offset, sne); -out: - kfree(hash_buf); - return err; + return tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, + tkey_buf, hash_offset, sne); } struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, @@ -807,14 +863,14 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, return 0; } -int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, - struct tcp_ao_key *key, struct tcphdr *th, - __u8 *hash_location) +void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, + struct tcp_ao_key *key, struct tcphdr *th, + __u8 *hash_location) { struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN]; struct tcp_sock *tp = tcp_sk(sk); struct tcp_ao_info *ao; - void *tkey_buf = NULL; u8 *traffic_key; u32 sne; @@ -826,9 +882,6 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, if (!(tcb->tcp_flags & TCPHDR_ACK)) { disn = 0; - tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); - if (!tkey_buf) - return -ENOMEM; traffic_key = tkey_buf; } else { disn = ao->risn; @@ -840,8 +893,6 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, ntohl(th->seq)); tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key, hash_location - (u8 *)th, sne); - kfree(tkey_buf); - return 0; } static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, @@ -906,7 +957,7 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, { const struct tcphdr *th = tcp_hdr(skb); u8 maclen = tcp_ao_hdr_maclen(aoh); - void *hash_buf = NULL; + u8 hash_buf[TCP_AO_MAX_MAC_LEN]; if (maclen != tcp_ao_maclen(key)) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); @@ -917,10 +968,6 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, return SKB_DROP_REASON_TCP_AOFAILURE; } - hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); - if (!hash_buf) - return SKB_DROP_REASON_NOT_SPECIFIED; - /* XXX: make it per-AF callback? */ tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, (phash - (u8 *)th), sne); @@ -930,13 +977,11 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, atomic64_inc(&key->pkt_bad); trace_tcp_ao_mismatch(sk, skb, aoh->keyid, aoh->rnext_keyid, maclen); - kfree(hash_buf); return SKB_DROP_REASON_TCP_AOFAILURE; } NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); atomic64_inc(&info->counters.pkt_good); atomic64_inc(&key->pkt_good); - kfree(hash_buf); return SKB_NOT_DROPPED_YET; } @@ -945,11 +990,11 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, const struct request_sock *req, int l3index, const struct tcp_ao_hdr *aoh) { + u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN]; const struct tcphdr *th = tcp_hdr(skb); u8 maclen = tcp_ao_hdr_maclen(aoh); u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ struct tcp_ao_info *info; - enum skb_drop_reason ret; struct tcp_ao_key *key; __be32 sisn, disn; u8 *traffic_key; @@ -1057,14 +1102,9 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, return SKB_DROP_REASON_TCP_AOFAILURE; } verify_hash: - traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); - if (!traffic_key) - return SKB_DROP_REASON_NOT_SPECIFIED; - tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); - ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, - traffic_key, phash, sne, l3index); - kfree(traffic_key); - return ret; + tcp_ao_calc_key_skb(key, tkey_buf, skb, sisn, disn, family); + return tcp_ao_verify_hash(sk, skb, family, info, aoh, key, + tkey_buf, phash, sne, l3index); key_not_found: NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); @@ -1281,7 +1321,6 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, free_and_exit: hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) { hlist_del(&key->node); - tcp_sigpool_release(key->tcp_sigpool_id); atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); kfree_sensitive(key); } @@ -1337,23 +1376,10 @@ static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, return 0; } -static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) +static int tcp_ao_parse_crypto(const struct tcp_ao_add *cmd, + struct tcp_ao_key *key) { unsigned int syn_tcp_option_space; - bool is_kdf_aes_128_cmac = false; - struct crypto_ahash *tfm; - struct tcp_sigpool hp; - void *tmp_key = NULL; - int err; - - /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ - if (!strcmp("cmac(aes128)", cmd->alg_name)) { - strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); - is_kdf_aes_128_cmac = (cmd->keylen != 16); - tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); - if (!tmp_key) - return -ENOMEM; - } key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ @@ -1389,64 +1415,27 @@ static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) syn_tcp_option_space -= TCPOLEN_MSS_ALIGNED; syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; - if (tcp_ao_len_aligned(key) > syn_tcp_option_space) { - err = -EMSGSIZE; - goto err_kfree; + if (tcp_ao_len_aligned(key) > syn_tcp_option_space) + return -EMSGSIZE; + + if (key->algo == TCP_AO_ALGO_AES_128_CMAC && + cmd->keylen != AES_KEYSIZE_128) { + /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ + static const u8 zeroes[AES_KEYSIZE_128]; + struct aes_cmac_key extractor; + + aes_cmac_preparekey(&extractor, zeroes, AES_KEYSIZE_128); + aes_cmac(&extractor, cmd->key, cmd->keylen, key->key); + key->keylen = AES_KEYSIZE_128; + } else { + memcpy(key->key, cmd->key, cmd->keylen); + key->keylen = cmd->keylen; } - key->keylen = cmd->keylen; - memcpy(key->key, cmd->key, cmd->keylen); - - err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); - if (err) - goto err_kfree; - - tfm = crypto_ahash_reqtfm(hp.req); - if (is_kdf_aes_128_cmac) { - void *scratch = hp.scratch; - struct scatterlist sg; - - memcpy(tmp_key, cmd->key, cmd->keylen); - sg_init_one(&sg, tmp_key, cmd->keylen); - - /* Using zero-key of 16 bytes as described in RFC5926 */ - memset(scratch, 0, 16); - err = crypto_ahash_setkey(tfm, scratch, 16); - if (err) - goto err_pool_end; - - err = crypto_ahash_init(hp.req); - if (err) - goto err_pool_end; - - ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); - err = crypto_ahash_update(hp.req); - if (err) - goto err_pool_end; - - err |= crypto_ahash_final(hp.req); - if (err) - goto err_pool_end; - key->keylen = 16; - } - - err = crypto_ahash_setkey(tfm, key->key, key->keylen); - if (err) - goto err_pool_end; - - tcp_sigpool_end(&hp); - kfree_sensitive(tmp_key); - if (tcp_ao_maclen(key) > key->digest_size) return -EINVAL; return 0; - -err_pool_end: - tcp_sigpool_end(&hp); -err_kfree: - kfree_sensitive(tmp_key); - return err; } #if IS_ENABLED(CONFIG_IPV6) @@ -1550,50 +1539,33 @@ static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk) static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, struct tcp_ao_add *cmd) { - const char *algo = cmd->alg_name; - unsigned int digest_size; - struct crypto_ahash *tfm; + const struct tcp_ao_algo *algo; struct tcp_ao_key *key; - struct tcp_sigpool hp; - int err, pool_id; size_t size; /* Force null-termination of alg_name */ cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; - /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ - if (!strcmp("cmac(aes128)", algo)) - algo = "cmac(aes)"; - - /* Full TCP header (th->doff << 2) should fit into scratch area, - * see tcp_ao_hash_header(). + /* + * For backwards compatibility, accept "cmac(aes)" as an alias for + * "cmac(aes128)", provided that the key length is exactly 128 bits. */ - pool_id = tcp_sigpool_alloc_ahash(algo, 60); - if (pool_id < 0) - return ERR_PTR(pool_id); + if (strcmp(cmd->alg_name, "cmac(aes)") == 0 && + cmd->keylen == AES_KEYSIZE_128) + strscpy(cmd->alg_name, "cmac(aes128)"); - err = tcp_sigpool_start(pool_id, &hp); - if (err) - goto err_free_pool; + algo = tcp_ao_find_algo(cmd->alg_name); + if (!algo) + return ERR_PTR(-ENOENT); - tfm = crypto_ahash_reqtfm(hp.req); - digest_size = crypto_ahash_digestsize(tfm); - tcp_sigpool_end(&hp); - - size = sizeof(struct tcp_ao_key) + (digest_size << 1); + size = sizeof(struct tcp_ao_key) + (algo->digest_size << 1); key = sock_kmalloc(sk, size, GFP_KERNEL); - if (!key) { - err = -ENOMEM; - goto err_free_pool; - } + if (!key) + return ERR_PTR(-ENOMEM); - key->tcp_sigpool_id = pool_id; - key->digest_size = digest_size; + key->algo = algo - tcp_ao_algos; + key->digest_size = algo->digest_size; return key; - -err_free_pool: - tcp_sigpool_release(pool_id); - return ERR_PTR(err); } static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, @@ -1754,7 +1726,6 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, err_free_sock: atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); - tcp_sigpool_release(key->tcp_sigpool_id); kfree_sensitive(key); err_free_ao: if (first) @@ -2285,7 +2256,11 @@ static int tcp_ao_copy_mkts_to_user(const struct sock *sk, opt_out.pkt_good = atomic64_read(&key->pkt_good); opt_out.pkt_bad = atomic64_read(&key->pkt_bad); memcpy(&opt_out.key, key->key, key->keylen); - tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); + if (key->algo == TCP_AO_ALGO_AES_128_CMAC) + /* This is needed for backwards compatibility. */ + strscpy(opt_out.alg_name, "cmac(aes)"); + else + strscpy(opt_out.alg_name, tcp_ao_algos[key->algo].name); /* Copy key to user */ if (copy_to_sockptr_offset(optval, out_offset, diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index aec7805b1d37..82378a2bfd1e 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Bottleneck Bandwidth and RTT (BBR) congestion control * * BBR congestion control computes the sending rate based on the delivery diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index de9f68a9c0cf..61045a8886e4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4812,18 +4812,20 @@ static enum skb_drop_reason tcp_sequence(const struct sock *sk, const struct tcphdr *th) { const struct tcp_sock *tp = tcp_sk(sk); + u32 seq_limit; if (before(end_seq, tp->rcv_wup)) return SKB_DROP_REASON_TCP_OLD_SEQUENCE; - if (unlikely(after(end_seq, tp->rcv_nxt + tcp_max_receive_window(tp)))) { + seq_limit = tp->rcv_nxt + tcp_max_receive_window(tp); + if (unlikely(after(end_seq, seq_limit))) { /* Some stacks are known to handle FIN incorrectly; allow the * FIN to extend beyond the window and check it in detail later. */ - if (!after(end_seq - th->fin, tp->rcv_nxt + tcp_receive_window(tp))) + if (!after(end_seq - th->fin, seq_limit)) return SKB_NOT_DROPPED_YET; - if (after(seq, tp->rcv_nxt + tcp_max_receive_window(tp))) + if (after(seq, seq_limit)) return SKB_DROP_REASON_TCP_INVALID_SEQUENCE; /* Only accept this packet if receive queue is empty. */ @@ -5020,8 +5022,9 @@ static void tcp_rcv_spurious_retrans(struct sock *sk, skb->protocol == htons(ETH_P_IPV6) && (tcp_sk(sk)->inet_conn.icsk_ack.lrcv_flowlabel != ntohl(ip6_flowlabel(ipv6_hdr(skb)))) && - sk_rethink_txhash(sk)) + __sk_rethink_txhash_reset_dst(sk)) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDUPLICATEDATAREHASH); + } /* Save last flowlabel after a spurious retrans. */ tcp_save_lrcv_flowlabel(sk, skb); @@ -6474,7 +6477,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb) /* TCP congestion window tracking */ trace_tcp_probe(sk, skb); - tcp_mstamp_refresh(tp); + tcp_mstamp_refresh_inline(tp); if (unlikely(!rcu_access_pointer(sk->sk_rx_dst))) inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); /* @@ -7633,6 +7636,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tcp_rsk(req)->af_specific = af_ops; tcp_rsk(req)->ts_off = 0; tcp_rsk(req)->req_usec_ts = false; + tcp_rsk(req)->txhash = net_tx_rndhash(); #if IS_ENABLED(CONFIG_MPTCP) tcp_rsk(req)->is_mptcp = 0; #endif @@ -7656,7 +7660,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, /* Note: tcp_v6_init_req() might override ir_iif for link locals */ inet_rsk(req)->ir_iif = inet_request_bound_dev_if(sk, skb); - dst = af_ops->route_req(sk, skb, &fl, req, isn); + if (want_cookie) { + isn = cookie_init_sequence(af_ops, skb, &req->mss); + /* Use the cookie as txhash so the SYN-ACK and the later full + * socket make the same egress choice (IPv6 ECMP path; IPv4 TX queue). + */ + tcp_rsk(req)->txhash = isn; + } + + dst = af_ops->route_req(sk, skb, &fl, req, want_cookie ? 0 : isn); if (!dst) goto drop_and_free; @@ -7696,7 +7708,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tcp_ecn_create_request(req, skb, sk, dst); if (want_cookie) { - isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); + cookie_record_sent(sk); if (!tmp_opt.tstamp_ok) inet_rsk(req)->ecn_ok = 0; } @@ -7714,7 +7726,6 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, } #endif tcp_rsk(req)->snt_isn = isn; - tcp_rsk(req)->txhash = net_tx_rndhash(); tcp_rsk(req)->syn_tos = TCP_SKB_CB(skb)->ip_dsfield; tcp_openreq_init_rwin(req, sk, dst); sk_rx_queue_set(req_to_sk(req), skb); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index fdc81150ff6c..ec09f97cc9e6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1894,7 +1894,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); goto discard; } -EXPORT_SYMBOL(tcp_v4_do_rcv); enum skb_drop_reason tcp_add_backlog(struct sock *sk, struct sk_buff *skb) { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index e6092c3ac840..ddc4b17a826b 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -392,7 +392,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) tcp_update_metrics(sk); tcp_done(sk); } -EXPORT_SYMBOL(tcp_time_wait); void tcp_twsk_destructor(struct sock *sk) { @@ -670,7 +669,6 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, return newsk; } -EXPORT_SYMBOL(tcp_create_openreq_child); /* * Process an incoming packet for SYN_RECV sockets represented as a diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 6e4bb411dc04..26dd751ec72a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -52,15 +52,9 @@ #include -/* Refresh clocks of a TCP socket, - * ensuring monotically increasing values. - */ -void tcp_mstamp_refresh(struct tcp_sock *tp) +void noinline tcp_mstamp_refresh(struct tcp_sock *tp) { - u64 val = tcp_clock_ns(); - - tp->tcp_clock_cache = val; - tp->tcp_mstamp = div_u64(val, NSEC_PER_USEC); + tcp_mstamp_refresh_inline(tp); } static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, @@ -472,22 +466,22 @@ static int bpf_skops_write_hdr_opt_arg0(struct sk_buff *skb, } /* req, syn_skb and synack_type are used when writing synack */ -static void bpf_skops_hdr_opt_len(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct sk_buff *syn_skb, - enum tcp_synack_type synack_type, - struct tcp_out_options *opts, - unsigned int *remaining) +static u32 bpf_skops_hdr_opt_len(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct sk_buff *syn_skb, + enum tcp_synack_type synack_type, + struct tcp_out_options *opts, + u32 remaining) { struct bpf_sock_ops_kern sock_ops; int err; if (likely(!BPF_SOCK_OPS_TEST_FLAG(tcp_sk(sk), BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG)) || - !*remaining) - return; + !remaining) + return remaining; - /* *remaining has already been aligned to 4 bytes, so *remaining >= 4 */ + /* remaining has already been aligned to 4 bytes, so remaining >= 4 */ /* init sock_ops */ memset(&sock_ops, 0, offsetof(struct bpf_sock_ops_kern, temp)); @@ -519,21 +513,21 @@ static void bpf_skops_hdr_opt_len(struct sock *sk, struct sk_buff *skb, } sock_ops.args[0] = bpf_skops_write_hdr_opt_arg0(skb, synack_type); - sock_ops.remaining_opt_len = *remaining; + sock_ops.remaining_opt_len = remaining; /* tcp_current_mss() does not pass a skb */ if (skb) bpf_skops_init_skb(&sock_ops, skb, 0); err = BPF_CGROUP_RUN_PROG_SOCK_OPS_SK(&sock_ops, sk); - if (err || sock_ops.remaining_opt_len == *remaining) - return; + if (err || sock_ops.remaining_opt_len == remaining) + return remaining; - opts->bpf_opt_len = *remaining - sock_ops.remaining_opt_len; + opts->bpf_opt_len = remaining - sock_ops.remaining_opt_len; /* round up to 4 bytes */ opts->bpf_opt_len = (opts->bpf_opt_len + 3) & ~3; - *remaining -= opts->bpf_opt_len; + return remaining - opts->bpf_opt_len; } static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb, @@ -581,13 +575,14 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb, max_opt_len - nr_written); } #else -static void bpf_skops_hdr_opt_len(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct sk_buff *syn_skb, - enum tcp_synack_type synack_type, - struct tcp_out_options *opts, - unsigned int *remaining) +static u32 bpf_skops_hdr_opt_len(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct sk_buff *syn_skb, + enum tcp_synack_type synack_type, + struct tcp_out_options *opts, + u32 remaining) { + return remaining; } static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb, @@ -1056,7 +1051,8 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, remaining -= tcp_options_fit_accecn(opts, 0, remaining); } - bpf_skops_hdr_opt_len(sk, skb, NULL, NULL, 0, opts, &remaining); + remaining = bpf_skops_hdr_opt_len(sk, skb, NULL, NULL, 0, opts, + remaining); return MAX_TCP_OPTION_SPACE - remaining; } @@ -1143,8 +1139,8 @@ static unsigned int tcp_synack_options(const struct sock *sk, remaining -= tcp_options_fit_accecn(opts, 0, remaining); } - bpf_skops_hdr_opt_len((struct sock *)sk, skb, req, syn_skb, - synack_type, opts, &remaining); + remaining = bpf_skops_hdr_opt_len((struct sock *)sk, skb, req, syn_skb, + synack_type, opts, remaining); return MAX_TCP_OPTION_SPACE - remaining; } @@ -1179,6 +1175,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb size += TCPOLEN_TSTAMP_ALIGNED; } +#if IS_ENABLED(CONFIG_MPTCP) /* MPTCP options have precedence over SACK for the limited TCP * option space because a MPTCP connection would be forced to * fall back to regular TCP if a required multipath option is @@ -1187,14 +1184,22 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb */ if (sk_is_mptcp(sk)) { unsigned int remaining = MAX_TCP_OPTION_SPACE - size; - unsigned int opt_size = 0; + bool has_ts = opts->options & OPTION_TS; + int opt_size; - if (mptcp_established_options(sk, skb, &opt_size, remaining, - &opts->mptcp)) { + opts->mptcp.drop_ts = 0; + + opt_size = mptcp_established_options(sk, skb, remaining, has_ts, + &opts->mptcp); + if (opt_size >= 0) { opts->options |= OPTION_MPTCP; size += opt_size; + + if (opts->mptcp.drop_ts) + opts->options &= ~OPTION_TS; } } +#endif eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; if (unlikely(eff_sacks)) { @@ -1233,7 +1238,8 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG))) { unsigned int remaining = MAX_TCP_OPTION_SPACE - size; - bpf_skops_hdr_opt_len(sk, skb, NULL, NULL, 0, opts, &remaining); + remaining = bpf_skops_hdr_opt_len(sk, skb, NULL, NULL, 0, opts, + remaining); size = MAX_TCP_OPTION_SPACE - remaining; } @@ -1421,6 +1427,7 @@ void tcp_wfree(struct sk_buff *skb) out: sk_free(sk); } +EXPORT_SYMBOL_GPL(tcp_wfree); /* Note: Called under soft irq. * We can call TCP stack right away, unless socket is owned by user. @@ -1663,14 +1670,8 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, key.md5_key, sk, skb); #endif } else if (tcp_key_is_ao(&key)) { - int err; - - err = tcp_ao_transmit_skb(sk, skb, key.ao_key, th, - opts.hash_location); - if (err) { - sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_NOT_SPECIFIED); - return -ENOMEM; - } + tcp_ao_transmit_skb(sk, skb, key.ao_key, th, + opts.hash_location); } /* BPF prog is the last one writing header option */ @@ -2972,7 +2973,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, sent_pkts = 0; - tcp_mstamp_refresh(tp); + tcp_mstamp_refresh_inline(tp); /* AccECN option beacon depends on mstamp, it may change mss */ if (tcp_ecn_mode_accecn(tp) && tcp_accecn_option_beacon_check(sk)) diff --git a/net/ipv4/tcp_plb.c b/net/ipv4/tcp_plb.c index c11a0cd3f8fe..bcc2f0add6af 100644 --- a/net/ipv4/tcp_plb.c +++ b/net/ipv4/tcp_plb.c @@ -78,7 +78,7 @@ void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb) if (plb->pause_until) return; - sk_rethink_txhash(sk); + __sk_rethink_txhash_reset_dst(sk); plb->consec_cong_rounds = 0; WRITE_ONCE(tcp_sk(sk)->plb_rehash, tcp_sk(sk)->plb_rehash + 1); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPLBREHASH); diff --git a/net/ipv4/tcp_sigpool.c b/net/ipv4/tcp_sigpool.c deleted file mode 100644 index 10b2e5970c40..000000000000 --- a/net/ipv4/tcp_sigpool.c +++ /dev/null @@ -1,366 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include - -static size_t __scratch_size; -struct sigpool_scratch { - local_lock_t bh_lock; - void __rcu *pad; -}; - -static DEFINE_PER_CPU(struct sigpool_scratch, sigpool_scratch) = { - .bh_lock = INIT_LOCAL_LOCK(bh_lock), -}; - -struct sigpool_entry { - struct crypto_ahash *hash; - const char *alg; - struct kref kref; - uint16_t needs_key:1, - reserved:15; -}; - -#define CPOOL_SIZE (PAGE_SIZE / sizeof(struct sigpool_entry)) -static struct sigpool_entry cpool[CPOOL_SIZE]; -static unsigned int cpool_populated; -static DEFINE_MUTEX(cpool_mutex); - -/* Slow-path */ -struct scratches_to_free { - struct rcu_head rcu; - unsigned int cnt; - void *scratches[]; -}; - -static void free_old_scratches(struct rcu_head *head) -{ - struct scratches_to_free *stf; - - stf = container_of(head, struct scratches_to_free, rcu); - while (stf->cnt--) - kfree(stf->scratches[stf->cnt]); - kfree(stf); -} - -/** - * sigpool_reserve_scratch - re-allocates scratch buffer, slow-path - * @size: request size for the scratch/temp buffer - */ -static int sigpool_reserve_scratch(size_t size) -{ - struct scratches_to_free *stf; - size_t stf_sz = struct_size(stf, scratches, num_possible_cpus()); - int cpu, err = 0; - - lockdep_assert_held(&cpool_mutex); - if (__scratch_size >= size) - return 0; - - stf = kmalloc(stf_sz, GFP_KERNEL); - if (!stf) - return -ENOMEM; - stf->cnt = 0; - - size = max(size, __scratch_size); - cpus_read_lock(); - for_each_possible_cpu(cpu) { - void *scratch, *old_scratch; - - scratch = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu)); - if (!scratch) { - err = -ENOMEM; - break; - } - - old_scratch = rcu_replace_pointer(per_cpu(sigpool_scratch.pad, cpu), - scratch, lockdep_is_held(&cpool_mutex)); - if (!cpu_online(cpu) || !old_scratch) { - kfree(old_scratch); - continue; - } - stf->scratches[stf->cnt++] = old_scratch; - } - cpus_read_unlock(); - if (!err) - __scratch_size = size; - - call_rcu(&stf->rcu, free_old_scratches); - return err; -} - -static void sigpool_scratch_free(void) -{ - int cpu; - - for_each_possible_cpu(cpu) - kfree(rcu_replace_pointer(per_cpu(sigpool_scratch.pad, cpu), - NULL, lockdep_is_held(&cpool_mutex))); - __scratch_size = 0; -} - -static int __cpool_try_clone(struct crypto_ahash *hash) -{ - struct crypto_ahash *tmp; - - tmp = crypto_clone_ahash(hash); - if (IS_ERR(tmp)) - return PTR_ERR(tmp); - - crypto_free_ahash(tmp); - return 0; -} - -static int __cpool_alloc_ahash(struct sigpool_entry *e, const char *alg) -{ - struct crypto_ahash *cpu0_hash; - int ret; - - e->alg = kstrdup(alg, GFP_KERNEL); - if (!e->alg) - return -ENOMEM; - - cpu0_hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(cpu0_hash)) { - ret = PTR_ERR(cpu0_hash); - goto out_free_alg; - } - - e->needs_key = crypto_ahash_get_flags(cpu0_hash) & CRYPTO_TFM_NEED_KEY; - - ret = __cpool_try_clone(cpu0_hash); - if (ret) - goto out_free_cpu0_hash; - e->hash = cpu0_hash; - kref_init(&e->kref); - return 0; - -out_free_cpu0_hash: - crypto_free_ahash(cpu0_hash); -out_free_alg: - kfree(e->alg); - e->alg = NULL; - return ret; -} - -/** - * tcp_sigpool_alloc_ahash - allocates pool for ahash requests - * @alg: name of async hash algorithm - * @scratch_size: reserve a tcp_sigpool::scratch buffer of this size - */ -int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size) -{ - int i, ret; - - /* slow-path */ - mutex_lock(&cpool_mutex); - ret = sigpool_reserve_scratch(scratch_size); - if (ret) - goto out; - for (i = 0; i < cpool_populated; i++) { - if (!cpool[i].alg) - continue; - if (strcmp(cpool[i].alg, alg)) - continue; - - /* pairs with tcp_sigpool_release() */ - if (!kref_get_unless_zero(&cpool[i].kref)) - kref_init(&cpool[i].kref); - ret = i; - goto out; - } - - for (i = 0; i < cpool_populated; i++) { - if (!cpool[i].alg) - break; - } - if (i >= CPOOL_SIZE) { - ret = -ENOSPC; - goto out; - } - - ret = __cpool_alloc_ahash(&cpool[i], alg); - if (!ret) { - ret = i; - if (i == cpool_populated) - cpool_populated++; - } -out: - mutex_unlock(&cpool_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(tcp_sigpool_alloc_ahash); - -static void __cpool_free_entry(struct sigpool_entry *e) -{ - crypto_free_ahash(e->hash); - kfree(e->alg); - memset(e, 0, sizeof(*e)); -} - -static void cpool_cleanup_work_cb(struct work_struct *work) -{ - bool free_scratch = true; - unsigned int i; - - mutex_lock(&cpool_mutex); - for (i = 0; i < cpool_populated; i++) { - if (kref_read(&cpool[i].kref) > 0) { - free_scratch = false; - continue; - } - if (!cpool[i].alg) - continue; - __cpool_free_entry(&cpool[i]); - } - if (free_scratch) - sigpool_scratch_free(); - mutex_unlock(&cpool_mutex); -} - -static DECLARE_WORK(cpool_cleanup_work, cpool_cleanup_work_cb); -static void cpool_schedule_cleanup(struct kref *kref) -{ - schedule_work(&cpool_cleanup_work); -} - -/** - * tcp_sigpool_release - decreases number of users for a pool. If it was - * the last user of the pool, releases any memory that was consumed. - * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() - */ -void tcp_sigpool_release(unsigned int id) -{ - if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg)) - return; - - /* slow-path */ - kref_put(&cpool[id].kref, cpool_schedule_cleanup); -} -EXPORT_SYMBOL_GPL(tcp_sigpool_release); - -/** - * tcp_sigpool_get - increases number of users (refcounter) for a pool - * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() - */ -void tcp_sigpool_get(unsigned int id) -{ - if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg)) - return; - kref_get(&cpool[id].kref); -} -EXPORT_SYMBOL_GPL(tcp_sigpool_get); - -int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) __cond_acquires(0, RCU_BH) -{ - struct crypto_ahash *hash; - - rcu_read_lock_bh(); - if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg)) { - rcu_read_unlock_bh(); - return -EINVAL; - } - - hash = crypto_clone_ahash(cpool[id].hash); - if (IS_ERR(hash)) { - rcu_read_unlock_bh(); - return PTR_ERR(hash); - } - - c->req = ahash_request_alloc(hash, GFP_ATOMIC); - if (!c->req) { - crypto_free_ahash(hash); - rcu_read_unlock_bh(); - return -ENOMEM; - } - ahash_request_set_callback(c->req, 0, NULL, NULL); - - /* Pairs with tcp_sigpool_reserve_scratch(), scratch area is - * valid (allocated) until tcp_sigpool_end(). - */ - local_lock_nested_bh(&sigpool_scratch.bh_lock); - c->scratch = rcu_dereference_bh(*this_cpu_ptr(&sigpool_scratch.pad)); - return 0; -} -EXPORT_SYMBOL_GPL(tcp_sigpool_start); - -void tcp_sigpool_end(struct tcp_sigpool *c) __releases(RCU_BH) -{ - struct crypto_ahash *hash = crypto_ahash_reqtfm(c->req); - - local_unlock_nested_bh(&sigpool_scratch.bh_lock); - rcu_read_unlock_bh(); - ahash_request_free(c->req); - crypto_free_ahash(hash); -} -EXPORT_SYMBOL_GPL(tcp_sigpool_end); - -/** - * tcp_sigpool_algo - return algorithm of tcp_sigpool - * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() - * @buf: buffer to return name of algorithm - * @buf_len: size of @buf - */ -size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len) -{ - if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg)) - return -EINVAL; - - return strscpy(buf, cpool[id].alg, buf_len); -} -EXPORT_SYMBOL_GPL(tcp_sigpool_algo); - -/** - * tcp_sigpool_hash_skb_data - hash data in skb with initialized tcp_sigpool - * @hp: tcp_sigpool pointer - * @skb: buffer to add sign for - * @header_len: TCP header length for this segment - */ -int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp, - const struct sk_buff *skb, - unsigned int header_len) -{ - const unsigned int head_data_len = skb_headlen(skb) > header_len ? - skb_headlen(skb) - header_len : 0; - const struct skb_shared_info *shi = skb_shinfo(skb); - const struct tcphdr *tp = tcp_hdr(skb); - struct ahash_request *req = hp->req; - struct sk_buff *frag_iter; - struct scatterlist sg; - unsigned int i; - - sg_init_table(&sg, 1); - - sg_set_buf(&sg, ((u8 *)tp) + header_len, head_data_len); - ahash_request_set_crypt(req, &sg, NULL, head_data_len); - if (crypto_ahash_update(req)) - return 1; - - for (i = 0; i < shi->nr_frags; ++i) { - const skb_frag_t *f = &shi->frags[i]; - unsigned int offset = skb_frag_off(f); - struct page *page; - - page = skb_frag_page(f) + (offset >> PAGE_SHIFT); - sg_set_page(&sg, page, skb_frag_size(f), offset_in_page(offset)); - ahash_request_set_crypt(req, &sg, NULL, skb_frag_size(f)); - if (crypto_ahash_update(req)) - return 1; - } - - skb_walk_frags(skb, frag_iter) - if (tcp_sigpool_hash_skb_data(hp, frag_iter, 0)) - return 1; - - return 0; -} -EXPORT_SYMBOL(tcp_sigpool_hash_skb_data); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Per-CPU pool of crypto requests"); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 322db13333c7..bf171b5e1eb3 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -297,7 +297,7 @@ static int tcp_write_timeout(struct sock *sk) return 1; } - if (sk_rethink_txhash(sk)) { + if (__sk_rethink_txhash_reset_dst(sk)) { WRITE_ONCE(tp->timeout_rehash, tp->timeout_rehash + 1); __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTREHASH); } diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c index b1f667c52cb2..9ab3728f9630 100644 --- a/net/ipv4/udp_tunnel_core.c +++ b/net/ipv4/udp_tunnel_core.c @@ -68,11 +68,9 @@ static bool sk_saddr_any(struct sock *sk) #endif } -void setup_udp_tunnel_sock(struct net *net, struct socket *sock, +void setup_udp_tunnel_sock(struct net *net, struct sock *sk, struct udp_tunnel_sock_cfg *cfg) { - struct sock *sk = sock->sk; - /* Disable multicast loopback */ inet_clear_bit(MC_LOOP, sk); @@ -99,10 +97,9 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, } EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock); -void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, +void udp_tunnel_push_rx_port(struct net_device *dev, struct sock *sk, unsigned short type) { - struct sock *sk = sock->sk; struct udp_tunnel_info ti; ti.type = type; @@ -113,10 +110,9 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, } EXPORT_SYMBOL_GPL(udp_tunnel_push_rx_port); -void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock, +void udp_tunnel_drop_rx_port(struct net_device *dev, struct sock *sk, unsigned short type) { - struct sock *sk = sock->sk; struct udp_tunnel_info ti; ti.type = type; @@ -128,9 +124,8 @@ void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock, EXPORT_SYMBOL_GPL(udp_tunnel_drop_rx_port); /* Notify netdevs that UDP port started listening */ -void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type) +void udp_tunnel_notify_add_rx_port(struct sock *sk, unsigned short type) { - struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct udp_tunnel_info ti; struct net_device *dev; @@ -150,9 +145,8 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type) EXPORT_SYMBOL_GPL(udp_tunnel_notify_add_rx_port); /* Notify netdevs that UDP port is no more listening */ -void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type) +void udp_tunnel_notify_del_rx_port(struct sock *sk, unsigned short type) { - struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct udp_tunnel_info ti; struct net_device *dev; @@ -195,10 +189,11 @@ void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb } EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb); -void udp_tunnel_sock_release(struct socket *sock) +void udp_tunnel_sock_release(struct sock *sk) { - rcu_assign_sk_user_data(sock->sk, NULL); - synchronize_rcu(); + struct socket *sock = sk->sk_socket; + + rcu_assign_sk_user_data(sk, NULL); kernel_sock_shutdown(sock, SHUT_RDWR); sock_release(sock); } diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 2c9ce2ccbde1..5b0cd6488021 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -54,3 +54,7 @@ obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o obj-y += mcast_snoop.o obj-$(CONFIG_TCP_AO) += tcp_ao.o endif + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE_netfilter.o := y +endif diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c9e5d3e48ab9..1f21ccb55caa 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -380,7 +380,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) int err = -ENOMEM; ASSERT_RTNL(); - netdev_ops_assert_locked(dev); + netdev_assert_locked_ops_compat(dev); if (dev->mtu < IPV6_MIN_MTU && dev != blackhole_netdev) return ERR_PTR(-EINVAL); @@ -1174,6 +1174,7 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, ipv6_link_dev_addr(idev, ifa); if (ifa->flags&IFA_F_TEMPORARY) { + /* manage_tempaddrs() relies on addresses being added to the head */ list_add(&ifa->tmp_list, &idev->tempaddr_list); in6_ifa_hold(ifa); } @@ -2168,16 +2169,18 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) struct net *net = dev_net(idev->dev); int max_addresses; - if (addrconf_dad_end(ifp)) { + spin_lock_bh(&ifp->lock); + + if (ifp->state != INET6_IFADDR_STATE_DAD) { + spin_unlock_bh(&ifp->lock); in6_ifa_put(ifp); return; } + ifp->state = INET6_IFADDR_STATE_POSTDAD; net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n", ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); - spin_lock_bh(&ifp->lock); - if (ifp->flags & IFA_F_STABLE_PRIVACY) { struct in6_addr new_addr; struct inet6_ifaddr *ifp2; @@ -2225,6 +2228,11 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) in6_ifa_put(ifp2); lock_errdad: spin_lock_bh(&ifp->lock); + if (ifp->state != INET6_IFADDR_STATE_POSTDAD) { + spin_unlock_bh(&ifp->lock); + in6_ifa_put(ifp); + return; + } } errdad: @@ -2597,8 +2605,10 @@ static void manage_tempaddrs(struct inet6_dev *idev, __u32 valid_lft, __u32 prefered_lft, bool create, unsigned long now) { - u32 flags; + u32 orig_prefered_lft = prefered_lft; struct inet6_ifaddr *ift; + bool reset_done = false; + u32 flags; read_lock_bh(&idev->lock); /* update all temporary addresses in the list */ @@ -2633,6 +2643,11 @@ static void manage_tempaddrs(struct inet6_dev *idev, prefered_lft = max_prefered; spin_lock(&ift->lock); + /* the first match is the most recent temp address */ + if (!reset_done && orig_prefered_lft > 0) { + ift->regen_count = 0; + reset_done = true; + } flags = ift->flags; ift->valid_lft = valid_lft; ift->prefered_lft = prefered_lft; diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index fa27a90ab3cd..29095c90aa16 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -107,7 +107,6 @@ int inet6addr_notifier_call_chain(unsigned long val, void *v) { return atomic_notifier_call_chain(&inet6addr_chain, val, v); } -EXPORT_SYMBOL(inet6addr_notifier_call_chain); int register_inet6addr_validator_notifier(struct notifier_block *nb) { @@ -126,7 +125,6 @@ int inet6addr_validator_notifier_call_chain(unsigned long val, void *v) { return blocking_notifier_call_chain(&inet6addr_validator_chain, val, v); } -EXPORT_SYMBOL(inet6addr_validator_notifier_call_chain); /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ const struct in6_addr in6addr_loopback __aligned(BITS_PER_LONG/8) @@ -140,16 +138,12 @@ const struct in6_addr in6addr_linklocal_allnodes __aligned(BITS_PER_LONG/8) EXPORT_SYMBOL(in6addr_linklocal_allnodes); const struct in6_addr in6addr_linklocal_allrouters __aligned(BITS_PER_LONG/8) = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; -EXPORT_SYMBOL(in6addr_linklocal_allrouters); const struct in6_addr in6addr_interfacelocal_allnodes __aligned(BITS_PER_LONG/8) = IN6ADDR_INTERFACELOCAL_ALLNODES_INIT; -EXPORT_SYMBOL(in6addr_interfacelocal_allnodes); const struct in6_addr in6addr_interfacelocal_allrouters __aligned(BITS_PER_LONG/8) = IN6ADDR_INTERFACELOCAL_ALLROUTERS_INIT; -EXPORT_SYMBOL(in6addr_interfacelocal_allrouters); const struct in6_addr in6addr_sitelocal_allrouters __aligned(BITS_PER_LONG/8) = IN6ADDR_SITELOCAL_ALLROUTERS_INIT; -EXPORT_SYMBOL(in6addr_sitelocal_allrouters); static void snmp6_free_dev(struct inet6_dev *idev) { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 0a88b376141d..282912a11999 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -211,7 +211,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, np->pmtudisc = IPV6_PMTUDISC_WANT; inet6_assign_bit(REPFLOW, sk, READ_ONCE(net->ipv6.sysctl.flowlabel_reflect) & FLOWLABEL_REFLECT_ESTABLISHED); - sk->sk_ipv6only = net->ipv6.sysctl.bindv6only; + sk->sk_ipv6only = READ_ONCE(net->ipv6.sysctl.bindv6only); sk->sk_txrehash = READ_ONCE(net->core.sysctl_txrehash); /* Init the ipv4 part of the socket since we can have sockets @@ -498,7 +498,6 @@ void inet6_cleanup_sock(struct sock *sk) txopt_put(opt); } } -EXPORT_SYMBOL_GPL(inet6_cleanup_sock); /* * This does both peername and sockname. @@ -823,6 +822,8 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6->flowi6_uid = sk_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); + ip6_ecmp_set_mp_hash(sock_net(sk), fl6, sk->sk_txhash); + rcu_read_lock(); final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &np->final); rcu_read_unlock(); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 43f46ef9c53b..9c677eb1d1a6 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -178,14 +178,10 @@ static bool ip6_parse_tlv(bool hopbyhop, case IPV6_TLV_IOAM: if (!ipv6_hop_ioam(skb, off)) return false; - - nh = skb_network_header(skb); break; case IPV6_TLV_JUMBO: if (!ipv6_hop_jumbo(skb, off)) return false; - - nh = skb_network_header(skb); break; case IPV6_TLV_CALIPSO: if (!ipv6_hop_calipso(skb, off)) @@ -203,8 +199,6 @@ static bool ip6_parse_tlv(bool hopbyhop, case IPV6_TLV_HAO: if (!ipv6_dest_hao(skb, off)) return false; - - nh = skb_network_header(skb); break; #endif default: @@ -215,6 +209,9 @@ static bool ip6_parse_tlv(bool hopbyhop, } } padlen = 0; + + /* recompute the network header pointer in case it has changed */ + nh = skb_network_header(skb); } off += optlen; len -= optlen; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 37534e116899..3e4ce8cb478e 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -48,6 +48,8 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk, fl6->flowi6_uid = sk_uid(sk); security_req_classify_flow(req, flowi6_to_flowi_common(fl6)); + ip6_ecmp_set_mp_hash(sock_net(sk), fl6, tcp_rsk(req)->txhash); + if (!dst) { dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); if (IS_ERR(dst)) @@ -70,6 +72,9 @@ struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->saddr = np->saddr; fl6->flowlabel = np->flow_label; IP6_ECN_flow_xmit(sk, fl6->flowlabel); + + if (sk->sk_protocol == IPPROTO_TCP) + ip6_ecmp_set_mp_hash(sock_net(sk), fl6, sk->sk_txhash); fl6->flowi6_oif = sk->sk_bound_dev_if; fl6->flowi6_mark = sk->sk_mark; fl6->fl6_sport = inet->inet_sport; @@ -102,7 +107,8 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused if (IS_ERR(dst)) { WRITE_ONCE(sk->sk_err_soft, -PTR_ERR(dst)); sk->sk_route_caps = 0; - kfree_skb(skb); + sk_skb_reason_drop(sk, skb, + SKB_DROP_REASON_IP_OUTNOROUTES); return PTR_ERR(dst); } /* Restore final destination back after routing done */ diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index e963a71858a7..943861a5581e 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -800,7 +800,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, /* queue depth */ if (trace->type.bit6) { struct netdev_queue *queue; - struct Qdisc *qdisc; + const struct Qdisc *qdisc; __u32 qlen, backlog; if (dev->flags & IFF_LOOPBACK || @@ -810,9 +810,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, queue = skb_get_tx_queue(dev, skb); qdisc = rcu_dereference(queue->qdisc); - spin_lock_bh(qdisc_lock(qdisc)); qdisc_qstats_qlen_backlog(qdisc, &qlen, &backlog); - spin_unlock_bh(qdisc_lock(qdisc)); *(__be32 *)data = cpu_to_be32(backlog); } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b897b3c5023b..fc95738ded76 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -633,7 +633,6 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true, .filter.dump_routes = true, - .filter.rtnl_held = false, }; const struct nlmsghdr *nlh = cb->nlh; struct net *net = sock_net(skb->sk); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index b1ccdf0dc646..1ab5ad0dcf24 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -620,7 +620,7 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq, int err; if (freq->flr_flags & IPV6_FL_F_REFLECT) { - if (net->ipv6.sysctl.flowlabel_consistency) { + if (READ_ONCE(net->ipv6.sysctl.flowlabel_consistency)) { net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n"); return -EPERM; } @@ -633,7 +633,7 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq, if (freq->flr_label & ~IPV6_FLOWLABEL_MASK) return -EINVAL; - if (net->ipv6.sysctl.flowlabel_state_ranges && + if (READ_ONCE(net->ipv6.sysctl.flowlabel_state_ranges) && (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG)) return -ERANGE; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 365b4059eb20..795be59946f7 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -445,11 +445,11 @@ static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; } - if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO)) - t->err_count++; + if (time_before(jiffies, READ_ONCE(t->err_time) + IP6TUNNEL_ERR_TIMEO)) + WRITE_ONCE(t->err_count, READ_ONCE(t->err_count) + 1); else - t->err_count = 1; - t->err_time = jiffies; + WRITE_ONCE(t->err_count, 1); + WRITE_ONCE(t->err_time, jiffies); return 0; } @@ -1058,7 +1058,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, /* TooBig packet may have updated dst->dev's mtu */ if (!t->parms.collect_md && dst) { mtu = READ_ONCE(dst_dev(dst)->mtu); - if (dst6_mtu(dst) > mtu) + if (dst_mtu(dst) > mtu) dst->ops->update_pmtu(dst, NULL, skb, mtu, false); } err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index d8072ad6b8c4..78f50c93c536 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -297,6 +297,7 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, return pp; } +EXPORT_INDIRECT_CALLABLE(ipv6_gro_receive); static struct sk_buff *sit_ip6ip6_gro_receive(struct list_head *head, struct sk_buff *skb) @@ -359,6 +360,7 @@ INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff) out: return err; } +EXPORT_INDIRECT_CALLABLE(ipv6_gro_complete); static int sit_gro_complete(struct sk_buff *skb, int nhoff) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c14adcdd4396..9f1e0e4f7464 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1332,7 +1332,6 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, return dst; } -EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src, gfp_t gfp) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9d1037ac082f..73fc5a0b8203 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -650,7 +650,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, /* change mtu on this route */ if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) { - if (rel_info > dst6_mtu(skb_dst(skb2))) + if (rel_info > dst4_mtu(skb_dst(skb2))) goto out; skb_dst_update_pmtu_no_confirm(skb2, rel_info); @@ -1104,7 +1104,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, struct ipv6_tel_txoption opt; struct dst_entry *dst = NULL, *ndst = NULL; struct net_device *tdev; - int mtu; + int err_count, mtu; unsigned int eth_hlen = t->dev->type == ARPHRD_ETHER ? ETH_HLEN : 0; unsigned int psh_hlen = sizeof(struct ipv6hdr) + t->encap_hlen; unsigned int max_headroom = psh_hlen; @@ -1214,14 +1214,15 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, goto tx_err_dst_release; } - if (t->err_count > 0) { + err_count = READ_ONCE(t->err_count); + if (err_count > 0) { if (time_before(jiffies, - t->err_time + IP6TUNNEL_ERR_TIMEO)) { - t->err_count--; + READ_ONCE(t->err_time) + IP6TUNNEL_ERR_TIMEO)) { + WRITE_ONCE(t->err_count, err_count - 1); dst_link_failure(skb); } else { - t->err_count = 0; + WRITE_ONCE(t->err_count, 0); } } diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c index 405ef1cb8864..9adb5775487f 100644 --- a/net/ipv6/ip6_udp_tunnel.c +++ b/net/ipv6/ip6_udp_tunnel.c @@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb); * @skb: Packet for which lookup is done * @dev: Tunnel device * @net: Network namespace of tunnel device - * @sock: Socket which provides route info + * @sk: Socket which provides route info * @oif: Index of the output interface * @saddr: Memory to store the src ip address * @key: Tunnel information @@ -135,7 +135,7 @@ EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb); struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb, struct net_device *dev, struct net *net, - struct socket *sock, + struct sock *sk, int oif, struct in6_addr *saddr, const struct ip_tunnel_key *key, @@ -162,7 +162,7 @@ struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb, fl6.fl6_dport = dport; fl6.flowlabel = ip6_make_flowinfo(dsfield, key->label); - dst = ip6_dst_lookup_flow(net, sock->sk, &fl6, NULL); + dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL); if (IS_ERR(dst)) { netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr); return ERR_PTR(-ENETUNREACH); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 85010ff21c98..604a58838901 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -85,7 +85,8 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; static struct mr_table *ip6mr_new_table(struct net *net, u32 id); -static void ip6mr_free_table(struct mr_table *mrt); +static void ip6mr_free_table(struct mr_table *mrt, + struct list_head *dev_kill_list); static void ip6_mr_forward(struct net *net, struct mr_table *mrt, struct net_device *dev, struct sk_buff *skb, @@ -99,7 +100,8 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack); static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb); -static void mroute_clean_tables(struct mr_table *mrt, int flags); +static void mroute_clean_tables(struct mr_table *mrt, int flags, + struct list_head *dev_kill_list); static void ipmr_expire_process(struct timer_list *t); #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES @@ -136,16 +138,6 @@ static struct mr_table *__ip6mr_get_table(struct net *net, u32 id) return NULL; } -static struct mr_table *ip6mr_get_table(struct net *net, u32 id) -{ - struct mr_table *mrt; - - rcu_read_lock(); - mrt = __ip6mr_get_table(net, id); - rcu_read_unlock(); - return mrt; -} - static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, struct mr_table **mrt) { @@ -237,6 +229,7 @@ static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = { static int __net_init ip6mr_rules_init(struct net *net) { struct fib_rules_ops *ops; + LIST_HEAD(dev_kill_list); struct mr_table *mrt; int err; @@ -260,24 +253,26 @@ static int __net_init ip6mr_rules_init(struct net *net) return 0; err2: - rtnl_lock(); - ip6mr_free_table(mrt); - rtnl_unlock(); + ip6mr_free_table(mrt, &dev_kill_list); err1: fib_rules_unregister(ops); return err; } static void __net_exit ip6mr_rules_exit(struct net *net) +{ + fib_rules_unregister(net->ipv6.mr6_rules_ops); +} + +static void __net_exit ip6mr_rules_exit_rtnl(struct net *net, + struct list_head *dev_kill_list) { struct mr_table *mrt, *next; - ASSERT_RTNL(); list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) { - list_del(&mrt->list); - ip6mr_free_table(mrt); + list_del_rcu(&mrt->list); + ip6mr_free_table(mrt, dev_kill_list); } - fib_rules_unregister(net->ipv6.mr6_rules_ops); } static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, @@ -298,28 +293,30 @@ bool ip6mr_rule_default(const struct fib_rule *rule) } EXPORT_SYMBOL(ip6mr_rule_default); #else -#define ip6mr_for_each_table(mrt, net) \ - for (mrt = net->ipv6.mrt6; mrt; mrt = NULL) - static struct mr_table *ip6mr_mr_table_iter(struct net *net, struct mr_table *mrt) { if (!mrt) - return net->ipv6.mrt6; + return rcu_dereference(net->ipv6.mrt6); return NULL; } -static struct mr_table *ip6mr_get_table(struct net *net, u32 id) +static struct mr_table *__ip6mr_get_table(struct net *net, u32 id) { - return net->ipv6.mrt6; + return rcu_dereference_check(net->ipv6.mrt6, + lockdep_rtnl_is_held() || + !rcu_access_pointer(net->ipv6.mrt6)); } -#define __ip6mr_get_table ip6mr_get_table +#define ip6mr_for_each_table(mrt, net) \ + for (mrt = __ip6mr_get_table(net, 0); mrt; mrt = NULL) static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, struct mr_table **mrt) { - *mrt = net->ipv6.mrt6; + *mrt = rcu_dereference(net->ipv6.mrt6); + if (!*mrt) + return -EAGAIN; return 0; } @@ -330,15 +327,22 @@ static int __net_init ip6mr_rules_init(struct net *net) mrt = ip6mr_new_table(net, RT6_TABLE_DFLT); if (IS_ERR(mrt)) return PTR_ERR(mrt); - net->ipv6.mrt6 = mrt; + + rcu_assign_pointer(net->ipv6.mrt6, mrt); return 0; } static void __net_exit ip6mr_rules_exit(struct net *net) { - ASSERT_RTNL(); - ip6mr_free_table(net->ipv6.mrt6); - net->ipv6.mrt6 = NULL; +} + +static void __net_exit ip6mr_rules_exit_rtnl(struct net *net, + struct list_head *dev_kill_list) +{ + struct mr_table *mrt = rcu_dereference_protected(net->ipv6.mrt6, 1); + + RCU_INIT_POINTER(net->ipv6.mrt6, NULL); + ip6mr_free_table(mrt, dev_kill_list); } static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, @@ -353,6 +357,17 @@ static unsigned int ip6mr_rules_seq_read(const struct net *net) } #endif +static struct mr_table *ip6mr_get_table(struct net *net, u32 id) +{ + struct mr_table *mrt; + + rcu_read_lock(); + mrt = __ip6mr_get_table(net, id); + rcu_read_unlock(); + + return mrt; +} + static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg, const void *ptr) { @@ -402,17 +417,23 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id) ipmr_expire_process, ip6mr_new_table_set); } -static void ip6mr_free_table(struct mr_table *mrt) +static void ip6mr_free_table(struct mr_table *mrt, + struct list_head *dev_kill_list) { struct net *net = read_pnet(&mrt->net); + LIST_HEAD(ip6mr_dev_kill_list); WARN_ON_ONCE(!mr_can_free_table(net)); timer_shutdown_sync(&mrt->ipmr_expire_timer); mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC | - MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC); - rhltable_destroy(&mrt->mfc_hash); - kfree(mrt); + MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC, + &ip6mr_dev_kill_list); + + mr_table_free(mrt); + + WARN_ON_ONCE(!net_initialized(net) && !list_empty(&ip6mr_dev_kill_list)); + list_splice(&ip6mr_dev_kill_list, dev_kill_list); } #ifdef CONFIG_PROC_FS @@ -623,18 +644,22 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, if (!pskb_inet_may_pull(skb)) goto tx_err; + rcu_read_lock(); + if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) - goto tx_err; + goto tx_lookup_err; DEV_STATS_ADD(dev, tx_bytes, skb->len); DEV_STATS_INC(dev, tx_packets); - rcu_read_lock(); + ip6mr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num), MRT6MSG_WHOLEPKT); rcu_read_unlock(); kfree_skb(skb); return NETDEV_TX_OK; +tx_lookup_err: + rcu_read_unlock(); tx_err: DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); @@ -792,7 +817,8 @@ static void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c) struct net *net = read_pnet(&mrt->net); struct sk_buff *skb; - atomic_dec(&mrt->cache_resolve_queue_len); + WRITE_ONCE(mrt->cache_resolve_queue_len, + mrt->cache_resolve_queue_len - 1); while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved)) != NULL) { if (ipv6_hdr(skb)->version == 0) { @@ -1063,10 +1089,14 @@ static void ip6mr_cache_resolve(struct net *net, struct mr_table *mrt, static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt, mifi_t mifi, int assert) { + enum skb_drop_reason reason; struct sock *mroute6_sk; struct sk_buff *skb; struct mrt6msg *msg; - int ret; + + mroute6_sk = rcu_dereference(mrt->mroute_sk); + if (!mroute6_sk) + return -EINVAL; #ifdef CONFIG_IPV6_PIMSM_V2 if (assert == MRT6MSG_WHOLEPKT || assert == MRT6MSG_WRMIFWHOLE) @@ -1136,34 +1166,35 @@ static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt, skb->ip_summed = CHECKSUM_UNNECESSARY; } - mroute6_sk = rcu_dereference(mrt->mroute_sk); - if (!mroute6_sk) { - kfree_skb(skb); - return -EINVAL; - } - mrt6msg_netlink_event(mrt, skb); /* Deliver to user space multicast routing algorithms */ - ret = sock_queue_rcv_skb(mroute6_sk, skb); + reason = sock_queue_rcv_skb_reason(mroute6_sk, skb); - if (ret < 0) { - net_warn_ratelimited("mroute6: pending queue full, dropping entries\n"); - kfree_skb(skb); + if (reason) { + sk_skb_reason_drop(mroute6_sk, skb, reason); + return -ENOMEM; } - return ret; + return 0; } /* Queue a packet for resolution. It gets locked cache entry! */ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, struct sk_buff *skb, struct net_device *dev) { - struct mfc6_cache *c; + struct net *net = read_pnet(&mrt->net); + struct mfc6_cache *c = NULL; bool found = false; int err; spin_lock_bh(&mfc_unres_lock); + + if (!check_net(net)) { + err = -EINVAL; + goto err; + } + list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) { if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) && ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) { @@ -1179,10 +1210,8 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, c = ip6mr_cache_alloc_unres(); if (!c) { - spin_unlock_bh(&mfc_unres_lock); - - kfree_skb(skb); - return -ENOBUFS; + err = -ENOBUFS; + goto err; } /* Fill in the new cache entry */ @@ -1194,18 +1223,11 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, * Reflect first query at pim6sd */ err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE); - if (err < 0) { - /* If the report failed throw the cache entry - out - Brad Parker - */ - spin_unlock_bh(&mfc_unres_lock); + if (err < 0) + goto err; - ip6mr_cache_free(c); - kfree_skb(skb); - return err; - } - - atomic_inc(&mrt->cache_resolve_queue_len); + WRITE_ONCE(mrt->cache_resolve_queue_len, + mrt->cache_resolve_queue_len + 1); list_add(&c->_c.list, &mrt->mfc_unres_queue); mr6_netlink_event(mrt, c, RTM_NEWROUTE); @@ -1214,18 +1236,26 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, /* See if we can append the packet */ if (c->_c.mfc_un.unres.unresolved.qlen > 3) { - kfree_skb(skb); + c = NULL; err = -ENOBUFS; - } else { - if (dev) { - skb->dev = dev; - skb->skb_iif = dev->ifindex; - } - skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); - err = 0; + goto err; } + if (dev) { + skb->dev = dev; + skb->skb_iif = dev->ifindex; + } + + skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); + spin_unlock_bh(&mfc_unres_lock); + return 0; + +err: + spin_unlock_bh(&mfc_unres_lock); + if (c) + ip6mr_cache_free(c); + kfree_skb(skb); return err; } @@ -1238,7 +1268,6 @@ static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc, { struct mfc6_cache *c; - /* The entries are added/deleted only under RTNL */ rcu_read_lock(); c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr, &mfc->mf6cc_mcastgrp.sin6_addr, parent); @@ -1325,8 +1354,13 @@ static void __net_exit ip6mr_notifier_exit(struct net *net) /* Setup for IP multicast routing */ static int __net_init ip6mr_net_init(struct net *net) { +#ifdef CONFIG_PROC_FS + LIST_HEAD(dev_kill_list); +#endif int err; + mutex_init(&net->ipv6.mfc_mutex); + err = ip6mr_notifier_init(net); if (err) return err; @@ -1351,9 +1385,8 @@ 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_rtnl(net, &dev_kill_list); ip6mr_rules_exit(net); - rtnl_unlock(); #endif ip6mr_rules_fail: ip6mr_notifier_exit(net); @@ -1366,29 +1399,27 @@ 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) +static void __net_exit ip6mr_net_exit_rtnl(struct net *net, + struct list_head *dev_kill_list) { - struct net *net; - - rtnl_lock(); - list_for_each_entry(net, net_list, exit_list) - ip6mr_rules_exit(net); - rtnl_unlock(); + ip6mr_rules_exit_rtnl(net, dev_kill_list); } static struct pernet_operations ip6mr_net_ops = { .init = ip6mr_net_init, .exit = ip6mr_net_exit, - .exit_batch = ip6mr_net_exit_batch, + .exit_rtnl = ip6mr_net_exit_rtnl, }; static const struct rtnl_msg_handler ip6mr_rtnl_msg_handlers[] __initconst_or_module = { {.owner = THIS_MODULE, .protocol = RTNL_FAMILY_IP6MR, .msgtype = RTM_GETROUTE, - .doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute}, + .doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute, + .flags = RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED}, }; int __init ip6_mr_init(void) @@ -1458,7 +1489,6 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, ttls[i] = 1; } - /* The entries are added/deleted only under RTNL */ rcu_read_lock(); c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr, &mfc->mf6cc_mcastgrp.sin6_addr, parent); @@ -1510,7 +1540,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) && ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) { list_del(&_uc->list); - atomic_dec(&mrt->cache_resolve_queue_len); + WRITE_ONCE(mrt->cache_resolve_queue_len, + mrt->cache_resolve_queue_len - 1); found = true; break; } @@ -1533,10 +1564,11 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, * Close the multicast socket, and clear the vif tables etc */ -static void mroute_clean_tables(struct mr_table *mrt, int flags) +static void mroute_clean_tables(struct mr_table *mrt, int flags, + struct list_head *dev_kill_list) { + struct net *net = read_pnet(&mrt->net); struct mr_mfc *c, *tmp; - LIST_HEAD(list); int i; /* Shut down all active vif entries */ @@ -1546,29 +1578,31 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags) !(flags & MRT6_FLUSH_MIFS_STATIC)) || (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS))) continue; - mif6_delete(mrt, i, 0, &list); + mif6_delete(mrt, i, 0, dev_kill_list); } - unregister_netdevice_many(&list); } /* Wipe the cache */ if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) { + mutex_lock(&net->ipv6.mfc_mutex); + list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) || (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC))) continue; rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params); list_del_rcu(&c->list); - call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net), - FIB_EVENT_ENTRY_DEL, + call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, (struct mfc6_cache *)c, mrt->id); mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE); mr_cache_put(c); } + + mutex_unlock(&net->ipv6.mfc_mutex); } if (flags & MRT6_FLUSH_MFC) { - if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { + if (READ_ONCE(mrt->cache_resolve_queue_len) || !check_net(net)) { spin_lock_bh(&mfc_unres_lock); list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { list_del(&c->list); @@ -1611,6 +1645,7 @@ int ip6mr_sk_done(struct sock *sk) { struct net *net = sock_net(sk); struct ipv6_devconf *devconf; + LIST_HEAD(dev_kill_list); struct mr_table *mrt; int err = -EACCES; @@ -1638,11 +1673,13 @@ int ip6mr_sk_done(struct sock *sk) NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); - mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC); + mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC, + &dev_kill_list); err = 0; break; } } + unregister_netdevice_many(&dev_kill_list); rtnl_unlock(); return err; @@ -1744,27 +1781,33 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, return -EFAULT; if (parent == 0) parent = mfc.mf6cc_parent; - rtnl_lock(); + + mutex_lock(&net->ipv6.mfc_mutex); + if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY) ret = ip6mr_mfc_delete(mrt, &mfc, parent); else ret = ip6mr_mfc_add(net, mrt, &mfc, sk == - rtnl_dereference(mrt->mroute_sk), + rcu_access_pointer(mrt->mroute_sk), parent); - rtnl_unlock(); + + mutex_unlock(&net->ipv6.mfc_mutex); return ret; case MRT6_FLUSH: { + LIST_HEAD(dev_kill_list); int flags; if (optlen != sizeof(flags)) return -EINVAL; if (copy_from_sockptr(&flags, optval, sizeof(flags))) return -EFAULT; + rtnl_lock(); - mroute_clean_tables(mrt, flags); + mroute_clean_tables(mrt, flags, &dev_kill_list); + unregister_netdevice_many(&dev_kill_list); rtnl_unlock(); return 0; } @@ -1780,7 +1823,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, return -EINVAL; if (copy_from_sockptr(&v, optval, sizeof(v))) return -EFAULT; - mrt->mroute_do_assert = v; + WRITE_ONCE(mrt->mroute_do_assert, v); return 0; } @@ -1800,9 +1843,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, 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; + WRITE_ONCE(mrt->mroute_do_pim, v); + WRITE_ONCE(mrt->mroute_do_assert, v); + WRITE_ONCE(mrt->mroute_do_wrvifwhole, do_wrmifwhole); } rtnl_unlock(); return ret; @@ -1870,11 +1913,11 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval, break; #ifdef CONFIG_IPV6_PIMSM_V2 case MRT6_PIM: - val = mrt->mroute_do_pim; + val = READ_ONCE(mrt->mroute_do_pim); break; #endif case MRT6_ASSERT: - val = mrt->mroute_do_assert; + val = READ_ONCE(mrt->mroute_do_assert); break; default: return -ENOPROTOOPT; @@ -2177,20 +2220,20 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt, if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { atomic_long_inc(&c->_c.mfc_un.res.wrong_if); - if (true_vifi >= 0 && mrt->mroute_do_assert && + if (true_vifi >= 0 && READ_ONCE(mrt->mroute_do_assert) && /* pimsm uses asserts, when switching from RPT to SPT, so that we cannot check that packet arrived on an oif. It is bad, but otherwise we would need to move pretty large chunk of pimd to kernel. Ough... --ANK */ - (mrt->mroute_do_pim || + (READ_ONCE(mrt->mroute_do_pim) || c->_c.mfc_un.res.ttls[true_vifi] < 255) && time_after(jiffies, c->_c.mfc_un.res.last_assert + 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) + if (READ_ONCE(mrt->mroute_do_wrvifwhole)) ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRMIFWHOLE); } @@ -2539,7 +2582,7 @@ static int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, cmd, flags); } -static int mr6_msgsize(bool unresolved, int maxvif) +static int mr6_msgsize(bool unresolved) { size_t len = NLMSG_ALIGN(sizeof(struct rtmsg)) @@ -2552,7 +2595,7 @@ static int mr6_msgsize(bool unresolved, int maxvif) len = len + nla_total_size(4) /* RTA_IIF */ + nla_total_size(0) /* RTA_MULTIPATH */ - + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) + + MAXMIFS * NLA_ALIGN(sizeof(struct rtnexthop)) /* RTA_MFC_STATS */ + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) ; @@ -2567,8 +2610,7 @@ static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc, struct sk_buff *skb; int err = -ENOBUFS; - skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif), - GFP_ATOMIC); + skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS), GFP_ATOMIC); if (!skb) goto errout; @@ -2703,54 +2745,60 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, if (err < 0) return err; + skb = nlmsg_new(mr6_msgsize(false), GFP_KERNEL); + if (!skb) + return -ENOBUFS; + if (tb[RTA_SRC]) src = nla_get_in6_addr(tb[RTA_SRC]); if (tb[RTA_DST]) grp = nla_get_in6_addr(tb[RTA_DST]); tableid = nla_get_u32_default(tb[RTA_TABLE], 0); + rcu_read_lock(); + mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT); if (!mrt) { NL_SET_ERR_MSG_MOD(extack, "MR table does not exist"); - return -ENOENT; + err = -ENOENT; + goto err; } - /* entries are added/deleted only under RTNL */ - rcu_read_lock(); cache = ip6mr_cache_find(mrt, &src, &grp); - rcu_read_unlock(); if (!cache) { NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found"); - return -ENOENT; + err = -ENOENT; + goto err; } - skb = nlmsg_new(mr6_msgsize(false, mrt->maxvif), GFP_KERNEL); - if (!skb) - return -ENOBUFS; - err = ip6mr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, cache, RTM_NEWROUTE, 0); - if (err < 0) { - kfree_skb(skb); - return err; - } + if (err < 0) + goto err; + + rcu_read_unlock(); return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); + +err: + rcu_read_unlock(); + kfree_skb(skb); + return err; } static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) { const struct nlmsghdr *nlh = cb->nlh; - struct fib_dump_filter filter = { - .rtnl_held = true, - }; + struct fib_dump_filter filter = {}; int err; + rcu_read_lock(); + if (cb->strict_check) { err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh, &filter, cb); if (err < 0) - return err; + goto unlock; } if (filter.table_id) { @@ -2758,17 +2806,26 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id); if (!mrt) { - if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) - return skb->len; + if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) { + err = skb->len; + goto unlock; + } NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist"); - return -ENOENT; + err = -ENOENT; + goto unlock; } + err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute, &mfc_unres_lock, &filter); - return skb->len ? : err; + err = skb->len ? : err; + goto unlock; } - return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter, - _ip6mr_fill_mroute, &mfc_unres_lock, &filter); + err = mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter, + _ip6mr_fill_mroute, &mfc_unres_lock, &filter); +unlock: + rcu_read_unlock(); + + return err; } diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 8607569de34f..b340d67eb1d9 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -95,7 +95,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x) memcpy(&t->mark, &x->mark, sizeof(t->mark)); t->if_id = x->if_id; - if (xfrm_init_state(t)) + if (xfrm_init_state(t, NULL)) goto error; atomic_set(&t->tunnel_users, 1); diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index d9b855d5191b..04b811b3be97 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -676,7 +676,7 @@ static void igmp6_group_added(struct ifmcaddr6 *mc) return; if (!(mc->mca_flags&MAF_LOADED)) { - mc->mca_flags |= MAF_LOADED; + WRITE_ONCE(mc->mca_flags, mc->mca_flags | MAF_LOADED); if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) dev_mc_add(dev, buf); } @@ -712,7 +712,7 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc) return; if (mc->mca_flags&MAF_LOADED) { - mc->mca_flags &= ~MAF_LOADED; + WRITE_ONCE(mc->mca_flags, mc->mca_flags & ~MAF_LOADED); if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) dev_mc_del(dev, buf); } @@ -876,7 +876,7 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev, mc->mca_addr = *addr; mc->idev = idev; /* reference taken by caller */ - mc->mca_users = 1; + WRITE_ONCE(mc->mca_users, 1); /* mca_stamp should be updated upon changes */ mc->mca_cstamp = mc->mca_tstamp = jiffies; refcount_set(&mc->mca_refcnt, 1); @@ -886,7 +886,7 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev, if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) || IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) - mc->mca_flags |= MAF_NOREPORT; + WRITE_ONCE(mc->mca_flags, mc->mca_flags | MAF_NOREPORT); return mc; } @@ -950,7 +950,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev, for_each_mc_mclock(idev, mc) { if (ipv6_addr_equal(&mc->mca_addr, addr)) { - mc->mca_users++; + WRITE_ONCE(mc->mca_users, mc->mca_users + 1); ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0); mutex_unlock(&idev->mc_lock); in6_dev_put(idev); @@ -995,7 +995,10 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) (ma = mc_dereference(*map, idev)); map = &ma->next) { if (ipv6_addr_equal(&ma->mca_addr, addr)) { - if (--ma->mca_users == 0) { + int new_users = ma->mca_users - 1; + + WRITE_ONCE(ma->mca_users, new_users); + if (new_users == 0) { *map = ma->next; igmp6_group_dropped(ma); @@ -1164,7 +1167,7 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) refcount_inc(&ma->mca_refcnt); - ma->mca_flags |= MAF_TIMER_RUNNING; + WRITE_ONCE(ma->mca_flags, ma->mca_flags | MAF_TIMER_RUNNING); } /* mark EXCLUDE-mode sources */ @@ -1192,7 +1195,7 @@ static bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs, } } } - pmc->mca_flags &= ~MAF_GSQUERY; + WRITE_ONCE(pmc->mca_flags, pmc->mca_flags & ~MAF_GSQUERY); if (scount == nsrcs) /* all sources excluded */ return false; return true; @@ -1224,10 +1227,10 @@ static bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs, } } if (!scount) { - pmc->mca_flags &= ~MAF_GSQUERY; + WRITE_ONCE(pmc->mca_flags, pmc->mca_flags & ~MAF_GSQUERY); return false; } - pmc->mca_flags |= MAF_GSQUERY; + WRITE_ONCE(pmc->mca_flags, pmc->mca_flags | MAF_GSQUERY); return true; } @@ -1315,20 +1318,7 @@ static void mld_update_qi(struct inet6_dev *idev, * - 9.12. Older Version Querier Present Timeout * (the [Query Interval] in the last Query received) */ - unsigned long mc_qqi; - - if (mlh2->mld2q_qqic < 128) { - mc_qqi = mlh2->mld2q_qqic; - } else { - unsigned long mc_man, mc_exp; - - mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic); - mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic); - - mc_qqi = (mc_man | 0x10) << (mc_exp + 3); - } - - idev->mc_qi = mc_qqi * HZ; + idev->mc_qi = mldv2_qqi(mlh2) * HZ; } static void mld_update_qri(struct inet6_dev *idev, @@ -1338,7 +1328,7 @@ static void mld_update_qri(struct inet6_dev *idev, * - 5.1.3. Maximum Response Code * - 9.3. Query Response Interval */ - idev->mc_qri = msecs_to_jiffies(mldv2_mrc(mlh2)); + idev->mc_qri = msecs_to_jiffies(mldv2_mrd(mlh2)); } static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld, @@ -1390,7 +1380,7 @@ static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld, static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, unsigned long *max_delay) { - *max_delay = max(msecs_to_jiffies(mldv2_mrc(mld)), 1UL); + *max_delay = max(msecs_to_jiffies(mldv2_mrd(mld)), 1UL); mld_update_qrv(idev, mld); mld_update_qi(idev, mld); @@ -1509,18 +1499,25 @@ static void __mld_query_work(struct sk_buff *skb) } } else { for_each_mc_mclock(idev, ma) { + unsigned int flags; + if (!ipv6_addr_equal(&group, &ma->mca_addr)) continue; - if (ma->mca_flags & MAF_TIMER_RUNNING) { + flags = ma->mca_flags; + + if (flags & MAF_TIMER_RUNNING) { /* gsquery <- gsquery && mark */ if (!mark) - ma->mca_flags &= ~MAF_GSQUERY; + WRITE_ONCE(ma->mca_flags, + flags & ~MAF_GSQUERY); } else { /* gsquery <- mark */ if (mark) - ma->mca_flags |= MAF_GSQUERY; + WRITE_ONCE(ma->mca_flags, + flags | MAF_GSQUERY); else - ma->mca_flags &= ~MAF_GSQUERY; + WRITE_ONCE(ma->mca_flags, + flags & ~MAF_GSQUERY); } if (!(ma->mca_flags & MAF_GSQUERY) || mld_marksources(ma, ntohs(mlh2->mld2q_nsrcs), mlh2->mld2q_srcs)) @@ -1628,8 +1625,9 @@ static void __mld_report_work(struct sk_buff *skb) if (ipv6_addr_equal(&ma->mca_addr, &mld->mld_mca)) { if (cancel_delayed_work(&ma->mca_work)) refcount_dec(&ma->mca_refcnt); - ma->mca_flags &= ~(MAF_LAST_REPORTER | - MAF_TIMER_RUNNING); + WRITE_ONCE(ma->mca_flags, + ma->mca_flags & ~(MAF_LAST_REPORTER | + MAF_TIMER_RUNNING)); break; } } @@ -2028,8 +2026,10 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, if (pgr) pgr->grec_nsrcs = htons(scount); - if (isquery) - pmc->mca_flags &= ~MAF_GSQUERY; /* clear query state */ + if (isquery) { + /* clear query state */ + WRITE_ONCE(pmc->mca_flags, pmc->mca_flags & ~MAF_GSQUERY); + } return skb; } @@ -2622,7 +2622,8 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) refcount_inc(&ma->mca_refcnt); - ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER; + WRITE_ONCE(ma->mca_flags, ma->mca_flags | + MAF_TIMER_RUNNING | MAF_LAST_REPORTER); } static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, @@ -2723,8 +2724,8 @@ static void mld_mca_work(struct work_struct *work) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); else mld_send_report(ma->idev, ma); - ma->mca_flags |= MAF_LAST_REPORTER; - ma->mca_flags &= ~MAF_TIMER_RUNNING; + WRITE_ONCE(ma->mca_flags, (ma->mca_flags | MAF_LAST_REPORTER) & + ~MAF_TIMER_RUNNING); mutex_unlock(&ma->idev->mc_lock); ma_put(ma); @@ -2982,14 +2983,16 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v) { struct ifmcaddr6 *im = (struct ifmcaddr6 *)v; struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); + unsigned int mca_flags = READ_ONCE(im->mca_flags); + unsigned long expires = READ_ONCE(im->mca_work.timer.expires); seq_printf(seq, "%-4d %-15s %pi6 %5d %08X %ld\n", state->dev->ifindex, state->dev->name, &im->mca_addr, - im->mca_users, im->mca_flags, - (im->mca_flags & MAF_TIMER_RUNNING) ? - jiffies_to_clock_t(im->mca_work.timer.expires - jiffies) : 0); + READ_ONCE(im->mca_users), mca_flags, + (mca_flags & MAF_TIMER_RUNNING) ? + jiffies_to_clock_t(expires - jiffies) : 0); return 0; } diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 66ce6fa5b2f5..72902d8005ad 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -43,3 +43,7 @@ obj-$(CONFIG_IP6_NF_MATCH_SRH) += ip6t_srh.o obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_TARGET_SYNPROXY) += ip6t_SYNPROXY.o + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE := y +endif diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index e34d5ba1460c..f42fb96ef64b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1630,6 +1630,8 @@ do_ip6t_set_ctl(struct sock *sk, int cmd, sockptr_t arg, unsigned int len) if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case IP6T_SO_SET_REPLACE: @@ -1659,6 +1661,8 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; + if (!xt_compat_check()) + return -EPERM; switch (cmd) { case IP6T_SO_GET_INFO: diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 64b1eeb79b57..418f21d24fb8 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -42,7 +42,6 @@ __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) id = __ipv6_select_ident(net, &addrs[1], &addrs[0]); return htonl(id); } -EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); __be32 ipv6_select_ident(struct net *net, const struct in6_addr *daddr, diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 11f9144bebbe..05c51f669754 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -69,7 +69,7 @@ static struct inet_frags ip6_frags; static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb, struct sk_buff *prev_tail, struct net_device *dev, - int *refs); + struct inet6_dev *idev, int *refs); static void ip6_frag_expire(struct timer_list *t) { @@ -107,7 +107,8 @@ fq_find(struct net *net, __be32 id, const struct ipv6hdr *hdr, int iif) static int ip6_frag_queue(struct net *net, struct frag_queue *fq, struct sk_buff *skb, struct frag_hdr *fhdr, int nhoff, - u32 *prob_offset, int *refs) + u32 *prob_offset, int *refs, + struct inet6_dev *idev) { int offset, end, fragsize; struct sk_buff *prev_tail; @@ -133,8 +134,7 @@ static int ip6_frag_queue(struct net *net, * we do not free it here. */ inet_frag_kill(&fq->q, refs); - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_REASMFAILS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS); return -1; } @@ -167,8 +167,7 @@ static int ip6_frag_queue(struct net *net, */ *prob_offset = offsetof(struct ipv6hdr, payload_len); inet_frag_kill(&fq->q, refs); - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_REASMFAILS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS); return -1; } if (end > fq->q.len) { @@ -227,7 +226,7 @@ static int ip6_frag_queue(struct net *net, unsigned long orefdst = skb->_skb_refdst; skb->_skb_refdst = 0UL; - err = ip6_frag_reasm(fq, skb, prev_tail, dev, refs); + err = ip6_frag_reasm(fq, skb, prev_tail, dev, idev, refs); skb->_skb_refdst = orefdst; return err; } @@ -242,12 +241,10 @@ static int ip6_frag_queue(struct net *net, goto err; } err = -EINVAL; - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_REASM_OVERLAPS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASM_OVERLAPS); discard_fq: inet_frag_kill(&fq->q, refs); - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_REASMFAILS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS); err: kfree_skb_reason(skb, reason); return err; @@ -262,7 +259,7 @@ static int ip6_frag_queue(struct net *net, */ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb, struct sk_buff *prev_tail, struct net_device *dev, - int *refs) + struct inet6_dev *idev, int *refs) { struct net *net = fq->q.fqdir->net; unsigned int nhoff; @@ -311,7 +308,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb, skb_postpush_rcsum(skb, skb_network_header(skb), skb_network_header_len(skb)); - __IP6_INC_STATS(net, __in6_dev_stats_get(dev, skb), IPSTATS_MIB_REASMOKS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMOKS); fq->q.rb_fragments = RB_ROOT; fq->q.fragments_tail = NULL; fq->q.last_run_head = NULL; @@ -323,7 +320,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb, out_oom: net_dbg_ratelimited("ip6_frag_reasm: no memory for reassembly\n"); out_fail: - __IP6_INC_STATS(net, __in6_dev_stats_get(dev, skb), IPSTATS_MIB_REASMFAILS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS); inet_frag_kill(&fq->q, refs); return -1; } @@ -332,15 +329,18 @@ static int ipv6_frag_rcv(struct sk_buff *skb) { const struct ipv6hdr *hdr = ipv6_hdr(skb); struct net *net = skb_dst_dev_net(skb); + struct inet6_dev *idev; struct frag_hdr *fhdr; struct frag_queue *fq; u8 nexthdr; int iif; + idev = skb->dev ? __in6_dev_stats_get(skb->dev, skb) : NULL; + if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED) goto fail_hdr; - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMREQDS); /* Jumbo payload inhibits frag. header */ if (hdr->payload_len == 0) @@ -356,8 +356,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb) if (!(fhdr->frag_off & htons(IP6_OFFSET | IP6_MF))) { /* It is not a fragmented frame */ skb->transport_header += sizeof(struct frag_hdr); - __IP6_INC_STATS(net, - ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMOKS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMOKS); IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb); IP6CB(skb)->flags |= IP6SKB_FRAGMENTED; @@ -374,8 +373,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb) */ nexthdr = hdr->nexthdr; if (ipv6frag_thdr_truncated(skb, skb_network_offset(skb) + sizeof(struct ipv6hdr), &nexthdr)) { - __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), - IPSTATS_MIB_INHDRERRORS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_INCOMP, 0); return -1; } @@ -391,14 +389,13 @@ static int ipv6_frag_rcv(struct sk_buff *skb) fq->iif = iif; ret = ip6_frag_queue(net, fq, skb, fhdr, IP6CB(skb)->nhoff, - &prob_offset, &refs); + &prob_offset, &refs, idev); spin_unlock(&fq->q.lock); rcu_read_unlock(); inet_frag_putn(&fq->q, refs); if (prob_offset) { - __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), - IPSTATS_MIB_INHDRERRORS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); /* icmpv6_param_prob() calls kfree_skb(skb) */ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, prob_offset); } @@ -406,13 +403,12 @@ static int ipv6_frag_rcv(struct sk_buff *skb) } rcu_read_unlock(); - __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMFAILS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS); kfree_skb(skb); return -1; fail_hdr: - __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), - IPSTATS_MIB_INHDRERRORS); + __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb_network_header_len(skb)); return -1; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 636f0120d7e3..6361ad2fcf77 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -819,9 +819,11 @@ static int rt6_nh_find_match(struct fib6_nh *nh, void *_arg) { struct fib6_nh_frl_arg *arg = _arg; - arg->nh = nh; - return find_match(nh, arg->flags, arg->oif, arg->strict, - arg->mpri, arg->do_rr); + if (find_match(nh, arg->flags, arg->oif, arg->strict, arg->mpri, + arg->do_rr)) + arg->nh = nh; + + return 0; } static void __find_rr_leaf(struct fib6_info *f6i_start, @@ -861,11 +863,10 @@ static void __find_rr_leaf(struct fib6_info *f6i_start, res->nh = nexthop_fib6_nh(f6i->nh); return; } - if (nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match, - &arg)) { - matched = true; - nh = arg.nh; - } + nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_find_match, + &arg); + matched = !!arg.nh; + nh = arg.nh; } else { nh = f6i->fib6_nh; if (find_match(nh, f6i->fib6_flags, oif, strict, @@ -2275,6 +2276,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, { struct fib6_result res = {}; struct rt6_info *rt = NULL; + bool have_oif_match; int strict = 0; WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) && @@ -2291,7 +2293,9 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, if (res.f6i == net->ipv6.fib6_null_entry) goto out; - fib6_select_path(net, &res, fl6, oif, false, skb, strict); + have_oif_match = fl6->flowi6_iif == LOOPBACK_IFINDEX && + oif == res.nh->fib_nh_dev->ifindex; + fib6_select_path(net, &res, fl6, oif, have_oif_match, skb, strict); /*Search through exception table */ rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); @@ -3044,7 +3048,6 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) ip6_datagram_dst_update(sk, false); bh_unlock_sock(sk); } -EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, const struct flowi6 *fl6) @@ -3255,7 +3258,6 @@ void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, READ_ONCE(sk->sk_mark), sk_uid(sk)); } -EXPORT_SYMBOL_GPL(ip6_sk_redirect); static unsigned int ip6_default_advmss(const struct dst_entry *dst) { @@ -3275,11 +3277,11 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst) /* * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. - * IPV6_MAXPLEN is also valid and means: "any MSS, - * rely only on pmtu discovery" + * Limit the default MSS to GSO_BY_FRAGS - 1 to avoid + * collision with the GSO_BY_FRAGS magic value (0xFFFF). */ if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) - mtu = IPV6_MAXPLEN; + mtu = min_t(unsigned int, IPV6_MAXPLEN, GSO_BY_FRAGS - 1); return mtu; } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b41e231a669b..64f0d1b622d3 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -595,11 +595,11 @@ static int ipip6_err(struct sk_buff *skb, u32 info) if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) goto out; - if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO)) - t->err_count++; + if (time_before(jiffies, READ_ONCE(t->err_time) + IPTUNNEL_ERR_TIMEO)) + WRITE_ONCE(t->err_count, READ_ONCE(t->err_count) + 1); else - t->err_count = 1; - t->err_time = jiffies; + WRITE_ONCE(t->err_count, 1); + WRITE_ONCE(t->err_time, jiffies); out: return err; } @@ -909,8 +909,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *tdev; /* Device to other host */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst = tiph->daddr; + int err_count, mtu; struct flowi4 fl4; - int mtu; u8 ttl; u8 protocol = IPPROTO_IPV6; int t_hlen = tunnel->hlen + sizeof(struct iphdr); @@ -987,13 +987,15 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, } } - if (tunnel->err_count > 0) { + err_count = READ_ONCE(tunnel->err_count); + if (err_count > 0) { if (time_before(jiffies, - tunnel->err_time + IPTUNNEL_ERR_TIMEO)) { - tunnel->err_count--; + READ_ONCE(tunnel->err_time) + IPTUNNEL_ERR_TIMEO)) { + WRITE_ONCE(tunnel->err_count, err_count - 1); dst_link_failure(skb); - } else - tunnel->err_count = 0; + } else { + WRITE_ONCE(tunnel->err_count, 0); + } } /* diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 4f6f0d751d6c..b581cb1ee2e8 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -245,6 +245,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_uid = sk_uid(sk); security_req_classify_flow(req, flowi6_to_flowi_common(&fl6)); + ip6_ecmp_set_mp_hash(net, &fl6, tcp_rsk(req)->txhash); + dst = ip6_dst_lookup_flow(net, sk, &fl6, final_p); if (IS_ERR(dst)) { SKB_DR_SET(reason, IP_OUTNOROUTES); diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 3c09ac26206e..01a8472805d1 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -7,84 +7,73 @@ * Francesco Ruggeri * Salam Noureddine */ -#include #include #include #include -static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, - const struct in6_addr *saddr, - const struct in6_addr *daddr, - __be16 sport, __be16 dport, - __be32 sisn, __be32 disn) +static void tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, + const struct in6_addr *saddr, + const struct in6_addr *daddr, + __be16 sport, __be16 dport, + __be32 sisn, __be32 disn) { struct kdf_input_block { u8 counter; u8 label[6]; struct tcp6_ao_context ctx; __be16 outlen; - } __packed * tmp; - struct tcp_sigpool hp; - int err; + } __packed input = { + .counter = 1, + .label = "TCP-AO", + .ctx = { + .saddr = *saddr, + .daddr = *daddr, + .sport = sport, + .dport = dport, + .sisn = sisn, + .disn = disn, + }, + .outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */ + }; - err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); - if (err) - return err; - - tmp = hp.scratch; - tmp->counter = 1; - memcpy(tmp->label, "TCP-AO", 6); - tmp->ctx.saddr = *saddr; - tmp->ctx.daddr = *daddr; - tmp->ctx.sport = sport; - tmp->ctx.dport = dport; - tmp->ctx.sisn = sisn; - tmp->ctx.disn = disn; - tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ - - err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); - tcp_sigpool_end(&hp); - - return err; + tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input)); } -int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, - const struct sk_buff *skb, - __be32 sisn, __be32 disn) +void tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, __be32 disn) { const struct ipv6hdr *iph = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); - return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, - &iph->daddr, th->source, - th->dest, sisn, disn); + tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr, th->source, + th->dest, sisn, disn); } -int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, - const struct sock *sk, __be32 sisn, - __be32 disn, bool send) +void tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, __be32 sisn, + __be32 disn, bool send) { if (send) - return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr, - &sk->sk_v6_daddr, htons(sk->sk_num), - sk->sk_dport, sisn, disn); + tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr, + &sk->sk_v6_daddr, htons(sk->sk_num), + sk->sk_dport, sisn, disn); else - return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr, - &sk->sk_v6_rcv_saddr, sk->sk_dport, - htons(sk->sk_num), disn, sisn); + tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr, + &sk->sk_v6_rcv_saddr, sk->sk_dport, + htons(sk->sk_num), disn, sisn); } -int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, - struct request_sock *req) +void tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) { struct inet_request_sock *ireq = inet_rsk(req); - return tcp_v6_ao_calc_key(mkt, key, - &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr, - htons(ireq->ir_num), ireq->ir_rmt_port, - htonl(tcp_rsk(req)->snt_isn), - htonl(tcp_rsk(req)->rcv_isn)); + tcp_v6_ao_calc_key(mkt, key, + &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); } struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, @@ -112,23 +101,19 @@ struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, AF_INET6, sndid, rcvid); } -int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, - const struct in6_addr *daddr, - const struct in6_addr *saddr, int nbytes) +void tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx, + const struct in6_addr *daddr, + const struct in6_addr *saddr, int nbytes) { - struct tcp6_pseudohdr *bp; - struct scatterlist sg; - - bp = hp->scratch; /* 1. TCP pseudo-header (RFC2460) */ - bp->saddr = *saddr; - bp->daddr = *daddr; - bp->len = cpu_to_be32(nbytes); - bp->protocol = cpu_to_be32(IPPROTO_TCP); + struct tcp6_pseudohdr phdr = { + .saddr = *saddr, + .daddr = *daddr, + .len = cpu_to_be32(nbytes), + .protocol = cpu_to_be32(IPPROTO_TCP), + }; - sg_init_one(&sg, bp, sizeof(*bp)); - ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); - return crypto_ahash_update(hp->req); + tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr)); } int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, @@ -149,20 +134,10 @@ int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, struct request_sock *req, const struct sk_buff *skb, int hash_offset, u32 sne) { - void *hash_buf = NULL; - int err; + u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN]; - hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); - if (!hash_buf) - return -ENOMEM; + tcp_v6_ao_calc_key_rsk(ao_key, tkey_buf, req); - err = tcp_v6_ao_calc_key_rsk(ao_key, hash_buf, req); - if (err) - goto out; - - err = tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb, - hash_buf, hash_offset, sne); -out: - kfree(hash_buf); - return err; + return tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb, + tkey_buf, hash_offset, sne); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 36d75fb50a70..ebe161d72fbd 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -258,6 +258,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr, if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) saddr = &sk->sk_v6_rcv_saddr; + sk_set_txhash(sk); + fl6->flowi6_proto = IPPROTO_TCP; fl6->daddr = sk->sk_v6_daddr; fl6->saddr = saddr ? *saddr : np->saddr; @@ -275,6 +277,14 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr, security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); + /* Non-zero mp_hash bypasses rt6_multipath_hash() in + * fib6_select_path(), letting txhash control ECMP path + * selection so that sk_rethink_txhash() rehashes onto a + * different path. Policies 1-3 derive a deterministic + * hash from the flow keys and must not be overridden. + */ + ip6_ecmp_set_mp_hash(net, fl6, sk->sk_txhash); + dst = ip6_dst_lookup_flow(net, sk, fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); @@ -315,8 +325,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr, if (err) goto late_failure; - sk_set_txhash(sk); - if (likely(!tp->repair)) { union tcp_seq_and_ts_off st; @@ -957,6 +965,13 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 if (txhash) { /* autoflowlabel/skb_get_hash_flowi6 rely on buff->hash */ skb_set_hash(buff, txhash, PKT_HASH_TYPE_L4); + + /* Select the local ECMP path from the connection's txhash, + * so a control packet (RST, or ACK from a time-wait socket) + * uses the same nexthop as the data. Only policy 0 uses + * mp_hash; policies 1-3 derive a deterministic hash. + */ + ip6_ecmp_set_mp_hash(net, &fl6, txhash); } fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark) ?: mark; fl6.fl6_dport = t1->dest; @@ -982,7 +997,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 return; } - kfree_skb(buff); + sk_skb_reason_drop(sk, buff, SKB_DROP_REASON_IP_OUTNOROUTES); } static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb, diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index c2dc3338670e..fed240b453bd 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1535,7 +1536,7 @@ static int iucv_sock_setsockopt(struct socket *sock, int level, int optname, } static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct iucv_sock *iucv = iucv_sk(sk); @@ -1545,9 +1546,7 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_IUCV) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; - + len = opt->optlen; if (len < 0) return -EINVAL; @@ -1582,9 +1581,8 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, if (rc) return rc; - if (put_user(len, optlen)) - return -EFAULT; - if (copy_to_user(optval, &val, len)) + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) return -EFAULT; return 0; @@ -2236,7 +2234,7 @@ static const struct proto_ops iucv_sock_ops = { .socketpair = sock_no_socketpair, .shutdown = iucv_sock_shutdown, .setsockopt = iucv_sock_setsockopt, - .getsockopt = iucv_sock_getsockopt, + .getsockopt_iter = iucv_sock_getsockopt, }; static int iucv_sock_create(struct net *net, struct socket *sock, int protocol, diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 3912e75079f5..d469abcd989b 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1167,7 +1168,7 @@ static int kcm_setsockopt(struct socket *sock, int level, int optname, } static int kcm_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct kcm_sock *kcm = kcm_sk(sock->sk); int val, len; @@ -1175,9 +1176,7 @@ static int kcm_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_KCM) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; - + len = opt->optlen; if (len < 0) return -EINVAL; @@ -1191,9 +1190,8 @@ static int kcm_getsockopt(struct socket *sock, int level, int optname, return -ENOPROTOOPT; } - if (put_user(len, optlen)) - return -EFAULT; - if (copy_to_user(optval, &val, len)) + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) return -EFAULT; return 0; } @@ -1304,8 +1302,8 @@ static int kcm_attach(struct socket *sock, struct socket *csock, psock->save_write_space = csk->sk_write_space; psock->save_state_change = csk->sk_state_change; csk->sk_user_data = psock; - csk->sk_data_ready = psock_data_ready; - csk->sk_write_space = psock_write_space; + WRITE_ONCE(csk->sk_data_ready, psock_data_ready); + WRITE_ONCE(csk->sk_write_space, psock_write_space); csk->sk_state_change = psock_state_change; write_unlock_bh(&csk->sk_callback_lock); @@ -1381,8 +1379,8 @@ static void kcm_unattach(struct kcm_psock *psock) */ write_lock_bh(&csk->sk_callback_lock); csk->sk_user_data = NULL; - csk->sk_data_ready = psock->save_data_ready; - csk->sk_write_space = psock->save_write_space; + WRITE_ONCE(csk->sk_data_ready, psock->save_data_ready); + WRITE_ONCE(csk->sk_write_space, psock->save_write_space); csk->sk_state_change = psock->save_state_change; strp_stop(&psock->strp); @@ -1755,7 +1753,7 @@ static const struct proto_ops kcm_dgram_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = kcm_setsockopt, - .getsockopt = kcm_getsockopt, + .getsockopt_iter = kcm_getsockopt, .sendmsg = kcm_sendmsg, .recvmsg = kcm_recvmsg, .mmap = sock_no_mmap, @@ -1776,7 +1774,7 @@ static const struct proto_ops kcm_seqpacket_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = kcm_setsockopt, - .getsockopt = kcm_getsockopt, + .getsockopt_iter = kcm_getsockopt, .sendmsg = kcm_sendmsg, .recvmsg = kcm_recvmsg, .mmap = sock_no_mmap, diff --git a/net/key/af_key.c b/net/key/af_key.c index 9cffeef18cd9..a714c997aab2 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1299,7 +1299,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, } } - err = xfrm_init_state(x); + err = xfrm_init_state(x, NULL); if (err) goto out; @@ -2554,7 +2554,7 @@ static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, if ((mode = pfkey_mode_to_xfrm(rq1->sadb_x_ipsecrequest_mode)) < 0) return -EINVAL; m->mode = mode; - m->reqid = rq1->sadb_x_ipsecrequest_reqid; + m->old_reqid = rq1->sadb_x_ipsecrequest_reqid; return ((int)(rq1->sadb_x_ipsecrequest_len + rq2->sadb_x_ipsecrequest_len)); @@ -3655,15 +3655,15 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, if (mode < 0) goto err; if (set_ipsecrequest(skb, mp->proto, mode, - (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE), - mp->reqid, mp->old_family, + (mp->old_reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE), + mp->old_reqid, mp->old_family, &mp->old_saddr, &mp->old_daddr) < 0) goto err; /* new ipsecrequest */ if (set_ipsecrequest(skb, mp->proto, mode, - (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE), - mp->reqid, mp->new_family, + (mp->old_reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE), + mp->old_reqid, mp->new_family, &mp->new_saddr, &mp->new_daddr) < 0) goto err; } diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 9419c8555d22..f940914959b1 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1684,7 +1684,7 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net, .encap_destroy = l2tp_udp_encap_destroy, }; - setup_udp_tunnel_sock(net, sock, &udp_cfg); + setup_udp_tunnel_sock(net, sock->sk, &udp_cfg); } sk->sk_allocation = GFP_ATOMIC; diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index e0b1915be1a6..f8881699e1ca 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -1335,7 +1336,7 @@ static int pppol2tp_session_getsockopt(struct sock *sk, * or the special tunnel type. */ static int pppol2tp_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct l2tp_session *session; @@ -1346,9 +1347,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_PPPOL2TP) return -EINVAL; - if (get_user(len, optlen)) - return -EFAULT; - + len = opt->optlen; if (len < 0) return -EINVAL; @@ -1376,14 +1375,9 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, int optname, goto end_put_sess; } - err = -EFAULT; - if (put_user(len, optlen)) - goto end_put_sess; - - if (copy_to_user((void __user *)optval, &val, len)) - goto end_put_sess; - - err = 0; + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) + err = -EFAULT; end_put_sess: l2tp_session_put(session); @@ -1652,7 +1646,7 @@ static const struct proto_ops pppol2tp_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = pppol2tp_setsockopt, - .getsockopt = pppol2tp_getsockopt, + .getsockopt_iter = pppol2tp_getsockopt, .sendmsg = pppol2tp_sendmsg, .recvmsg = pppol2tp_recvmsg, .mmap = sock_no_mmap, diff --git a/net/llc/Makefile b/net/llc/Makefile index 5e0ef436daae..46b1cd905ffd 100644 --- a/net/llc/Makefile +++ b/net/llc/Makefile @@ -1,15 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 ########################################################################### # Makefile for the Linux 802.2 LLC (fully-functional) layer. # # Copyright (c) 1997 by Procom Technology,Inc. # 2001-2003 by Arnaldo Carvalho de Melo -# -# This program can be redistributed or modified under the terms of the -# GNU General Public License as published by the Free Software Foundation. -# This program is distributed without any warranty or implied warranty -# of merchantability or fitness for a particular purpose. -# -# See the GNU General Public License for more details. ########################################################################### obj-$(CONFIG_LLC) += llc.o diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 1b210db3119e..8ed1be1ecccc 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * af_llc.c - LLC User Interface SAPs * Description: @@ -12,13 +13,6 @@ * * Copyright (c) 2001 by Jay Schulist * 2002-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include @@ -27,6 +21,7 @@ #include #include #include +#include #include #include @@ -1166,25 +1161,21 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, * @sock: Socket to get information from. * @level: Socket level user is requesting operations on. * @optname: Operation name. - * @optval: Variable to return operation data in. - * @optlen: Length of optval. + * @opt: sockopt context with iterator and length for returning data. * * Get connection specific socket information. */ static int llc_ui_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); - int val = 0, len = 0, rc = -EINVAL; + int val = 0, len, rc = -EINVAL; lock_sock(sk); if (unlikely(level != SOL_LLC)) goto out; - rc = get_user(len, optlen); - if (rc) - goto out; - rc = -EINVAL; + len = opt->optlen; if (len != sizeof(int)) goto out; switch (optname) { @@ -1212,7 +1203,8 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname, goto out; } rc = 0; - if (put_user(len, optlen) || copy_to_user(optval, &val, len)) + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) rc = -EFAULT; out: release_sock(sk); @@ -1239,7 +1231,7 @@ static const struct proto_ops llc_ui_ops = { .listen = llc_ui_listen, .shutdown = llc_ui_shutdown, .setsockopt = llc_ui_setsockopt, - .getsockopt = llc_ui_getsockopt, + .getsockopt_iter = llc_ui_getsockopt, .sendmsg = llc_ui_sendmsg, .recvmsg = llc_ui_recvmsg, .mmap = sock_no_mmap, diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c index 0779daa8aa8f..724ecd741d4c 100644 --- a/net/llc/llc_c_ac.c +++ b/net/llc/llc_c_ac.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_c_ac.c - actions performed during connection state transition. * @@ -9,13 +10,6 @@ * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include @@ -1005,8 +999,8 @@ static int llc_conn_ac_inc_npta_value(struct sock *sk, struct sk_buff *skb) llc->dec_step = 0; llc->dec_cntr = llc->inc_cntr = 2; ++llc->npta; - if (llc->npta > (u8) ~LLC_2_SEQ_NBR_MODULO) - llc->npta = (u8) ~LLC_2_SEQ_NBR_MODULO; + if (llc->npta > LLC_2_SEQ_NBR_MODULO - 1) + llc->npta = LLC_2_SEQ_NBR_MODULO - 1; } else --llc->inc_cntr; return 0; @@ -1096,8 +1090,8 @@ int llc_conn_ac_inc_tx_win_size(struct sock *sk, struct sk_buff *skb) struct llc_sock *llc = llc_sk(sk); llc->k += 1; - if (llc->k > (u8) ~LLC_2_SEQ_NBR_MODULO) - llc->k = (u8) ~LLC_2_SEQ_NBR_MODULO; + if (llc->k > LLC_2_SEQ_NBR_MODULO - 1) + llc->k = LLC_2_SEQ_NBR_MODULO - 1; return 0; } diff --git a/net/llc/llc_c_ev.c b/net/llc/llc_c_ev.c index d6627a80cb45..beb2836c7db0 100644 --- a/net/llc/llc_c_ev.c +++ b/net/llc/llc_c_ev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_c_ev.c - Connection component state transition event qualifiers * @@ -25,13 +26,6 @@ * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/llc/llc_c_st.c b/net/llc/llc_c_st.c index 1c267db304df..5fbd8d19c6c4 100644 --- a/net/llc/llc_c_st.c +++ b/net/llc/llc_c_st.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_c_st.c - This module contains state transition of connection component. * @@ -6,13 +7,6 @@ * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 5c0ac243b248..e8f427375c68 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_conn.c - Driver routines for connection component. * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c index d73f5414d8ce..5b0f1986bddc 100644 --- a/net/llc/llc_core.c +++ b/net/llc/llc_core.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_core.c - Minimum needed routines for sap handling and module init/exit * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c index 58a5f419adc6..1514362e613d 100644 --- a/net/llc/llc_if.c +++ b/net/llc/llc_if.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_if.c - Defines LLC interface to upper layer * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index 61b0159b2fbe..8eb3d73c39d1 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_input.c - Minimal input path for LLC * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/llc/llc_pdu.c b/net/llc/llc_pdu.c index 63749dde542f..c1938fa24a3f 100644 --- a/net/llc/llc_pdu.c +++ b/net/llc/llc_pdu.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_pdu.c - access to PDU internals * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index aa81c67b24a1..4804a08c2490 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * proc_llc.c - proc interface for LLC * * Copyright (c) 2001 by Jay Schulist * 2002-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/net/llc/llc_s_ac.c b/net/llc/llc_s_ac.c index 7a0cae9a8111..98deee560373 100644 --- a/net/llc/llc_s_ac.c +++ b/net/llc/llc_s_ac.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_s_ac.c - actions performed during sap state transition. * @@ -9,13 +10,6 @@ * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/net/llc/llc_s_ev.c b/net/llc/llc_s_ev.c index a74d2a1d6581..cfbecba589e7 100644 --- a/net/llc/llc_s_ev.c +++ b/net/llc/llc_s_ev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_s_ev.c - Defines SAP component events * @@ -6,13 +7,6 @@ * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/llc/llc_s_st.c b/net/llc/llc_s_st.c index acccc827c562..e14d4f520327 100644 --- a/net/llc/llc_s_st.c +++ b/net/llc/llc_s_st.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_s_st.c - Defines SAP component state machine transitions. * @@ -6,13 +7,6 @@ * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 6cd03c2ae7d5..1bd446a21092 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_sap.c - driver routines for SAP component. * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index f50654292510..77fd0ac75263 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * llc_station.c - station component of LLC * * Copyright (c) 1997 by Procom Technology, Inc. * 2001-2003 by Arnaldo Carvalho de Melo - * - * This program can be redistributed or modified under the terms of the - * GNU General Public License as published by the Free Software Foundation. - * This program is distributed without any warranty or implied warranty - * of merchantability or fitness for a particular purpose. - * - * See the GNU General Public License for more details. */ #include #include diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 20c3135b73ea..36f1e3e2222b 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -30,7 +30,7 @@ mac80211-y := \ key.o \ util.o parse.o \ wme.o \ - chan.o \ + chan.o ap.o \ trace.o mlme.o \ tdls.o \ ocb.o \ diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 0140ac826b23..9629e00069a1 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -49,9 +49,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h) int i; for (i = 0; i < tid_rx->buf_size; i++) - __skb_queue_purge(&tid_rx->reorder_buf[i]); - kfree(tid_rx->reorder_buf); - kfree(tid_rx->reorder_time); + __skb_queue_purge(&tid_rx->reorder[i].buf); kfree(tid_rx); } @@ -412,7 +410,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, } /* prepare A-MPDU MLME for Rx aggregation */ - tid_agg_rx = kzalloc_obj(*tid_agg_rx); + tid_agg_rx = kzalloc_flex(*tid_agg_rx, reorder, buf_size); if (!tid_agg_rx) goto end; @@ -426,27 +424,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, timer_setup(&tid_agg_rx->reorder_timer, sta_rx_agg_reorder_timer_expired, 0); - /* prepare reordering buffer */ - tid_agg_rx->reorder_buf = - kzalloc_objs(struct sk_buff_head, buf_size); - tid_agg_rx->reorder_time = - kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL); - if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { - kfree(tid_agg_rx->reorder_buf); - kfree(tid_agg_rx->reorder_time); - kfree(tid_agg_rx); - goto end; - } - for (i = 0; i < buf_size; i++) - __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]); + __skb_queue_head_init(&tid_agg_rx->reorder[i].buf); ret = drv_ampdu_action(local, sta->sdata, ¶ms); ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", sta->sta.addr, tid, ret); if (ret) { - kfree(tid_agg_rx->reorder_buf); - kfree(tid_agg_rx->reorder_time); kfree(tid_agg_rx); goto end; } diff --git a/net/mac80211/ap.c b/net/mac80211/ap.c new file mode 100644 index 000000000000..e7ac99c5a22e --- /dev/null +++ b/net/mac80211/ap.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AP handling + * + * Partially + * Copyright (C) 2026 Intel Corporation + */ + +#include "driver-ops.h" +#include "ieee80211_i.h" +#include "rate.h" + +static void +ieee80211_send_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *req, int opt_len) +{ + int len = IEEE80211_MIN_ACTION_SIZE(eml_omn); + struct ieee80211_local *local = sdata->local; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + + len += opt_len; /* optional len */ + skb = dev_alloc_skb(local->tx_headroom + len); + if (!skb) + return; + + skb_reserve(skb, local->tx_headroom); + mgmt = skb_put_zero(skb, len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, req->sa, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT; + mgmt->u.action.action_code = + WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF; + mgmt->u.action.eml_omn.dialog_token = + req->u.action.eml_omn.dialog_token; + mgmt->u.action.eml_omn.control = req->u.action.eml_omn.control & + ~(IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE | + IEEE80211_EML_CTRL_INDEV_COEX_ACT); + /* Copy optional fields from the received notification frame */ + memcpy(mgmt->u.action.eml_omn.variable, + req->u.action.eml_omn.variable, opt_len); + + ieee80211_tx_skb(sdata, skb); +} + +static void +ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + int len = IEEE80211_MIN_ACTION_SIZE(eml_omn); + enum nl80211_iftype type = ieee80211_vif_type_p2p(&sdata->vif); + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + const struct wiphy_iftype_ext_capab *ift_ext_capa; + struct ieee80211_mgmt *mgmt = (void *)skb->data; + struct ieee80211_local *local = sdata->local; + u8 control = mgmt->u.action.eml_omn.control; + u8 *ptr = mgmt->u.action.eml_omn.variable; + struct ieee80211_eml_params eml_params = { + .link_id = status->link_id, + .control = control, + }; + struct sta_info *sta; + int opt_len = 0; + + if (!ieee80211_vif_is_mld(&sdata->vif)) + return; + + /* eMLSR and eMLMR can't be enabled at the same time */ + if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) && + (control & IEEE80211_EML_CTRL_EMLMR_MODE)) + return; + + if ((control & IEEE80211_EML_CTRL_EMLMR_MODE) && + (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE)) + return; + + ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, type); + if (!ift_ext_capa) + return; + + if (!status->link_valid) + return; + + sta = sta_info_get_bss(sdata, mgmt->sa); + if (!sta) + return; + + if (control & IEEE80211_EML_CTRL_EMLSR_MODE) { + u8 emlsr_param_update_len; + + if (!(ift_ext_capa->eml_capabilities & + IEEE80211_EML_CAP_EMLSR_SUPP)) + return; + + opt_len += sizeof(__le16); /* eMLSR link_bitmap */ + /* + * eMLSR param update field is not part of Notification frame + * sent by the AP to client so account it separately. + */ + emlsr_param_update_len = + !!(control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE); + + if (skb->len < len + opt_len + emlsr_param_update_len) + return; + + if (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE) { + u8 pad_delay, trans_delay; + + pad_delay = u8_get_bits(ptr[2], + IEEE80211_EML_EMLSR_PAD_DELAY); + if (pad_delay > + IEEE80211_EML_CAP_EML_PADDING_DELAY_256US) + return; + + trans_delay = u8_get_bits(ptr[2], + IEEE80211_EML_EMLSR_TRANS_DELAY); + if (trans_delay > + IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US) + return; + + /* Update sta padding and transition delay */ + sta->sta.eml_cap = + u8_replace_bits(sta->sta.eml_cap, + pad_delay, + IEEE80211_EML_CAP_EML_PADDING_DELAY); + sta->sta.eml_cap = + u8_replace_bits(sta->sta.eml_cap, + trans_delay, + IEEE80211_EML_CAP_EML_TRANSITION_DELAY); + } + } + + if (control & IEEE80211_EML_CTRL_EMLMR_MODE) { + u8 mcs_map_size; + int i; + + if (!(ift_ext_capa->eml_capabilities & + IEEE80211_EML_CAP_EMLMR_SUPPORT)) + return; + + opt_len += sizeof(__le16); /* eMLMR link_bitmap */ + opt_len++; /* eMLMR mcs_map_count */ + if (skb->len < len + opt_len) + return; + + eml_params.emlmr_mcs_map_count = ptr[2]; + if (eml_params.emlmr_mcs_map_count > 2) + return; + + mcs_map_size = 3 * (1 + eml_params.emlmr_mcs_map_count); + opt_len += mcs_map_size; + if (skb->len < len + opt_len) + return; + + for (i = 0; i < mcs_map_size; i++) { + u8 rx_mcs, tx_mcs; + + rx_mcs = u8_get_bits(ptr[3 + i], + IEEE80211_EML_EMLMR_RX_MCS_MAP); + if (rx_mcs > 8) + return; + + tx_mcs = u8_get_bits(ptr[3 + i], + IEEE80211_EML_EMLMR_TX_MCS_MAP); + if (tx_mcs > 8) + return; + } + + memcpy(eml_params.emlmr_mcs_map_bw, &ptr[3], mcs_map_size); + } + + if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) || + (control & IEEE80211_EML_CTRL_EMLMR_MODE)) { + eml_params.link_bitmap = get_unaligned_le16(ptr); + if ((eml_params.link_bitmap & sdata->vif.active_links) != + eml_params.link_bitmap) + return; + } + + if (drv_set_eml_op_mode(sdata, &sta->sta, &eml_params)) + return; + + ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len); +} + +static void +ieee80211_rx_uhr_link_reconfig_req(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + const struct element *sub; + struct sta_info *sta; + + /* + * rx.c only accepts IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST + * which is valid, so no need to check the frame type/format/etc. + */ + + sta = sta_info_get_bss(sdata, mgmt->sa); + if (!sta) + return; + + struct ieee802_11_elems *elems __free(kfree) = + ieee802_11_parse_elems(mgmt->u.action.uhr_link_reconf_req.variable, + skb->len - IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req), + IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION, + NULL); + /* STA will assume we processed it, not good */ + if (!elems) + return; + + if (!elems->ml_reconf) + return; + + for_each_mle_subelement(sub, (u8 *)elems->ml_reconf, + elems->ml_reconf_len) { + const struct ieee80211_mle_per_sta_profile *prof = + (const void *)sub->data; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + struct ieee80211_link_data *link; + struct link_sta_info *link_sta; + const struct element *chg; + u16 control; + u8 link_id; + + if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) + continue; + + if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data, + sub->datalen)) + return; + + control = le16_to_cpu(prof->control); + link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID; + + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return; + + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; + + chanctx_conf = sdata_dereference(link->conf->chanctx_conf, + sdata); + if (!chanctx_conf) + continue; + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, + conf); + + link_sta = sdata_dereference(sta->link[link_id], sdata); + if (!link_sta) + continue; + + /* do we need to handle any other bits? */ + if (control & ~(IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID | + IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE)) + continue; + + if (u16_get_bits(control, IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE) != + IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD) + continue; + + for_each_element_extid(chg, WLAN_EID_EXT_UHR_MODE_CHG, + prof->variable + prof->sta_info_len - 1, + sub->datalen - sizeof(*prof) - + prof->sta_info_len + 1) { + const struct ieee80211_uhr_mode_change_tuple *tuple; + + for_each_uhr_mode_change_tuple(chg->data + 1, + chg->datalen - 1, + tuple) { + u8 id = le16_get_bits(tuple->control, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID); + bool enabled = le16_get_bits(tuple->control, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE); + + /* only handle DBE (for now?) */ + if (id != IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE) + continue; + + link_sta->uhr_dbe_enabled = enabled; + /* also recalculates and updates per-STA bw */ + ieee80211_recalc_chanctx_min_def(sdata->local, + chanctx); + } + } + } + + /* TODO: send a response */ +} + +void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* rx.c cannot queue any non-action frames to AP interfaces */ + if (WARN_ON(!ieee80211_is_action(mgmt->frame_control))) + return; + + switch (mgmt->u.action.category) { + case WLAN_CATEGORY_PROTECTED_EHT: + switch (mgmt->u.action.action_code) { + case WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF: + ieee80211_rx_eml_op_mode_notif(sdata, skb); + break; + } + break; + case WLAN_CATEGORY_PROTECTED_UHR: + switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST: + ieee80211_rx_uhr_link_reconfig_req(sdata, skb); + break; + } + break; + } +} + +void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link) +{ + struct ieee80211_sub_if_data *sdata = link->sdata; + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + int link_id = link->link_id; + struct sta_info *sta; + + chanctx_conf = sdata_dereference(link->conf->chanctx_conf, sdata); + if (!chanctx_conf) + return; + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + + list_for_each_entry(sta, &local->sta_list, list) { + struct link_sta_info *link_sta; + + if (sta->sdata->bss != sdata->bss) + continue; + + link_sta = sdata_dereference(sta->link[link_id], sdata); + if (!link_sta) + continue; + + link_sta->uhr_dbe_enabled = false; + } + + /* also recalculates and updates per-STA bw */ + ieee80211_recalc_chanctx_min_def(local, chanctx); +} diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f9ee9947a94d..3b58af59f7e4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -716,6 +716,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_PD: case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: @@ -1312,6 +1313,152 @@ ieee80211_copy_rnr_beacon(u8 *pos, struct cfg80211_rnr_elems *dst, return offset; } +static enum ieee80211_sta_rx_bandwidth +ieee80211_calc_ap_he_and_lower(struct cfg80211_beacon_data *params) +{ + const struct ieee80211_vht_operation *vht_oper = params->vht_oper; + int ccfs0, ccfs1; + + if (params->he_oper) { + const struct ieee80211_he_6ghz_oper *he_6ghz_oper; + + if (params->he_oper->he_oper_params & + cpu_to_le32(IEEE80211_HE_OPERATION_VHT_OPER_INFO)) + vht_oper = (void *)params->he_oper->optional; + + he_6ghz_oper = ieee80211_he_6ghz_oper(params->he_oper); + if (he_6ghz_oper) { + switch (u8_get_bits(he_6ghz_oper->control, + IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ: + return IEEE80211_STA_RX_BW_20; + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ: + return IEEE80211_STA_RX_BW_40; + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ: + return IEEE80211_STA_RX_BW_80; + case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ: + return IEEE80211_STA_RX_BW_160; + } + } + } + + if (vht_oper) { + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_USE_HT: + /* check for HT (or fall down to 20) below */ + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + /* deprecated encodings */ + return IEEE80211_STA_RX_BW_160; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + /* + * See IEEE 802.11-2020 Table 9-352-BSS bandwidth + * when the VHT Operation Information field Channel + * Width subfield is 1 + * + * (IEEE80211_VHT_CHANWIDTH_80MHZ == 1) + */ + ccfs0 = vht_oper->center_freq_seg0_idx; + ccfs1 = vht_oper->center_freq_seg1_idx; + if (!ccfs0) + return IEEE80211_STA_RX_BW_80; + if (ccfs1 && abs(ccfs1 - ccfs0) == 8) + return IEEE80211_STA_RX_BW_160; + /* 80+80 - RX BW doesn't cover that / uses 160 */ + if (ccfs1 && abs(ccfs1 - ccfs0) > 16) + return IEEE80211_STA_RX_BW_160; + fallthrough; + default: + /* reserved encoding - assume 80 */ + return IEEE80211_STA_RX_BW_80; + } + } + + if (params->ht_oper) { + switch (u8_get_bits(params->ht_oper->ht_param, + IEEE80211_HT_PARAM_CHA_SEC_OFFSET)) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + default: /* invalid values */ + return IEEE80211_STA_RX_BW_20; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + return IEEE80211_STA_RX_BW_40; + } + } + + /* nothing found, must be 20 MHz */ + return IEEE80211_STA_RX_BW_20; +} + +static enum ieee80211_sta_rx_bandwidth +ieee80211_calc_ap_eht_bw(struct cfg80211_beacon_data *params, + enum ieee80211_sta_rx_bandwidth he_and_lower) +{ + const struct ieee80211_eht_operation_info *info; + + if (!params->eht_oper) + return he_and_lower; + + info = ieee80211_eht_oper_info(params->eht_oper); + if (!info) + return he_and_lower; + + switch (u8_get_bits(info->control, IEEE80211_EHT_OPER_CHAN_WIDTH)) { + case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ: + return IEEE80211_STA_RX_BW_20; + case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ: + return IEEE80211_STA_RX_BW_40; + case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ: + return IEEE80211_STA_RX_BW_80; + case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ: + return IEEE80211_STA_RX_BW_160; + case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ: + return IEEE80211_STA_RX_BW_320; + } + + /* invalid setting, assume 20 MHz */ + return IEEE80211_STA_RX_BW_20; +} + +static void ieee80211_update_ap_bandwidth(struct ieee80211_link_data *link, + struct cfg80211_beacon_data *params) +{ + struct ieee80211_local *local = link->sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + + /* + * Updating the beacon might, without even changing the channel, cause + * the usable bandwidth for some stations to be changed, for example + * if the beacon configuration is EHT with 160 MHz, HE could change + * between 20, 40, 80 and 160 MHz, and HE (or lower) clients need to + * be handled accordingly. + * Calculate the HE and lower bandwidth and apply that to all stations. + * + * In the future, this also needs to calculate EHT bandwidth and apply + * it to all stations not using UHR DBE, since the chandef would then + * include DBE. + */ + + if (link->conf->chanreq.oper.chan->band == NL80211_BAND_S1GHZ) + return; + + link->bss_bw.he_and_lower = ieee80211_calc_ap_he_and_lower(params); + link->bss_bw.eht = ieee80211_calc_ap_eht_bw(params, + link->bss_bw.he_and_lower); + + chanctx_conf = sdata_dereference(link->conf->chanctx_conf, link->sdata); + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + + /* + * Note: this relies on ieee80211_recalc_chanctx_min_def() having + * the side effect of updating all stations, if they changed; that + * was normally for when the chandef changed but is used here too. + */ + ieee80211_recalc_chanctx_min_def(local, chanctx); +} + static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, @@ -1450,6 +1597,8 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, if (old) kfree_rcu(old, rcu_head); + ieee80211_update_ap_bandwidth(link, params); + *changed |= _changed; return 0; } @@ -1496,7 +1645,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id = params->beacon.link_id; struct ieee80211_link_data *link; struct ieee80211_bss_conf *link_conf; - struct ieee80211_chan_req chanreq = { .oper = params->chandef }; + struct ieee80211_chan_req chanreq = { + .oper = params->chandef, + .require_npca = true, + }; u64 tsf; lockdep_assert_wiphy(local->hw.wiphy); @@ -1541,13 +1693,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); } - if (params->he_cap && params->he_oper) { + if (params->he_cap && params->beacon.he_oper) { link_conf->he_support = true; link_conf->htc_trig_based_pkt_ext = - le32_get_bits(params->he_oper->he_oper_params, + le32_get_bits(params->beacon.he_oper->he_oper_params, IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK); link_conf->frame_time_rts_th = - le32_get_bits(params->he_oper->he_oper_params, + le32_get_bits(params->beacon.he_oper->he_oper_params, IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); changed |= BSS_CHANGED_HE_OBSS_PD; @@ -1596,7 +1748,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ); link_conf->eht_disable_mcs15 = - u8_get_bits(params->eht_oper->params, + u8_get_bits(params->beacon.eht_oper->params, IEEE80211_EHT_OPER_MCS15_DISABLE); } else { link_conf->eht_su_beamformer = false; @@ -1604,11 +1756,43 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, link_conf->eht_mu_beamformer = false; } - if (params->uhr_oper) { + if (params->beacon.uhr_oper) { + const struct ieee80211_uhr_npca_info *npca; + struct ieee80211_bss_npca_params npca_params = {}; + if (!link_conf->eht_support) return -EOPNOTSUPP; link_conf->uhr_support = true; + + npca = ieee80211_uhr_npca_info(params->beacon.uhr_oper); + if (!npca) { + chanreq.oper.npca_chan = NULL; + chanreq.oper.npca_punctured = 0; + } else { + npca_params.min_dur_thresh = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH); + npca_params.switch_delay = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY); + npca_params.switch_back_delay = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY); + npca_params.init_qsrc = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC); + npca_params.moplen = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_MOPLEN); + npca_params.enabled = true; + } + + if (memcmp(&npca_params, &link->conf->npca, + sizeof(npca_params))) { + link->conf->npca = npca_params; + changed |= BSS_CHANGED_NPCA; + } } if (sdata->vif.type == NL80211_IFTYPE_AP && @@ -1838,6 +2022,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_link_data *link = sdata_dereference(sdata->link[link_id], sdata); struct ieee80211_bss_conf *link_conf = link->conf; + u64 changes = BSS_CHANGED_BEACON_ENABLED; LIST_HEAD(keys); lockdep_assert_wiphy(local->hw.wiphy); @@ -1887,6 +2072,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, if (old_s1g_short_beacon) kfree_rcu(old_s1g_short_beacon, rcu_head); + if (link_conf->ftm_responder) { + link_conf->ftm_responder = false; + changes |= BSS_CHANGED_FTM_RESPONDER; + } + kfree(link_conf->ftmr_params); link_conf->ftmr_params = NULL; @@ -1908,8 +2098,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.cfg.ssid_len = 0; sdata->vif.cfg.s1g = false; clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); - ieee80211_link_info_change_notify(sdata, link, - BSS_CHANGED_BEACON_ENABLED); + ieee80211_link_info_change_notify(sdata, link, changes); ieee80211_remove_link_keys(link, &keys); if (!list_empty(&keys)) { @@ -2221,7 +2410,15 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, ieee80211_s1g_cap_to_sta_s1g_cap(sdata, params->s1g_capa, link_sta); - ieee80211_sta_init_nss(link_sta); + switch (sdata->vif.type) { + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + /* not applicable - they don't use NSS/BW as capability */ + break; + default: + ieee80211_sta_init_nss_bw_capa(link_sta, &link->conf->chanreq.oper); + break; + } if (params->opmode_notif_used) { enum nl80211_chan_width width = link->conf->chanreq.oper.width; @@ -3336,6 +3533,7 @@ static int ieee80211_scan(struct wiphy *wiphy, } break; case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_PD: default: return -EOPNOTSUPP; } @@ -4303,6 +4501,9 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) ieee80211_link_info_change_notify(sdata, link_data, changed); + if (sdata->vif.type == NL80211_IFTYPE_AP) + ieee80211_uhr_disable_dbe_all_stas(link_data); + ieee80211_vif_unblock_queues_csa(sdata); err = drv_post_channel_switch(link_data); @@ -4425,8 +4626,6 @@ static int ieee80211_set_csa_beacon(struct ieee80211_link_data *link_data, cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) return -EINVAL; break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: break; @@ -4502,7 +4701,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_chan_req chanreq = { .oper = params->chandef }; + struct ieee80211_chan_req chanreq = { + .oper = params->chandef, + .require_npca = true, + }; struct ieee80211_local *local = sdata->local; struct ieee80211_channel_switch ch_switch = { .link_id = params->link_id, @@ -4944,7 +5146,10 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_link_data *link; - struct ieee80211_chan_req chanreq = { .oper = *chandef }; + struct ieee80211_chan_req chanreq = { + .oper = *chandef, + .require_npca = true, + }; int ret; u64 changed = 0; @@ -5092,6 +5297,25 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif, } EXPORT_SYMBOL(ieee80211_nan_func_match); +void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif, + const u8 *cluster_id, bool new_cluster, + gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (WARN_ON(vif->type != NL80211_IFTYPE_NAN)) + return; + + if (WARN_ON(!sdata->u.nan.started)) + return; + + ether_addr_copy(sdata->u.nan.conf.cluster_id, cluster_id); + + cfg80211_nan_cluster_joined(ieee80211_vif_to_wdev(vif), cluster_id, + new_cluster, gfp); +} +EXPORT_SYMBOL(ieee80211_nan_cluster_joined); + static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy, struct net_device *dev, const bool enabled) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index fda692316f08..5152b84a3357 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -285,19 +285,41 @@ ieee80211_chanreq_compatible(const struct ieee80211_chan_req *a, const struct ieee80211_chan_req *b, struct ieee80211_chan_req *tmp) { + struct ieee80211_chan_req _a = *a, _b = *b; const struct cfg80211_chan_def *compat; if (a->ap.chan && b->ap.chan && !cfg80211_chandef_identical(&a->ap, &b->ap)) return NULL; - compat = cfg80211_chandef_compatible(&a->oper, &b->oper); + /* + * Remove NPCA if it's not required, so that interfaces + * sharing a channel context will not use NPCA while the + * channel context is shared. + * If both sides are AP interfaces requiring NPC, there's + * an assumption that userspace will set them up with + * identical configurations and the same BSS color + * (if the config is not identical, sharing will fail due + * to cfg80211_chandef_compatible() failing below.) + */ + if (!_a.require_npca) { + _a.oper.npca_chan = NULL; + _a.oper.npca_punctured = 0; + } + + if (!_b.require_npca) { + _b.oper.npca_chan = NULL; + _b.oper.npca_punctured = 0; + } + + compat = cfg80211_chandef_compatible(&_a.oper, &_b.oper); if (!compat) return NULL; /* Note: later code assumes this always fills & returns tmp if compat */ tmp->oper = *compat; tmp->ap = a->ap.chan ? a->ap : b->ap; + tmp->require_npca = a->require_npca && b->require_npca; return tmp; } @@ -438,11 +460,12 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local, return NULL; } -static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta, - unsigned int link_id) +static enum nl80211_chan_width +ieee80211_get_sta_bw(struct sta_info *sta, struct ieee80211_link_data *link) { enum ieee80211_sta_rx_bandwidth width; struct link_sta_info *link_sta; + int link_id = link->link_id; link_sta = wiphy_dereference(sta->local->hw.wiphy, sta->link[link_id]); @@ -454,45 +477,28 @@ static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta, * We assume that TX/RX might be asymmetric (so e.g. VHT operating * mode notification changes what a STA wants to receive, but not * necessarily what it will transmit to us), and therefore use the - * capabilities here. Calling it RX bandwidth capability is a bit - * wrong though, since capabilities are in fact symmetric. + * "from station" bandwidth here. */ - width = ieee80211_sta_cap_rx_bw(link_sta); + width = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper, + IEEE80211_STA_BW_RX_FROM_STA); - switch (width) { - case IEEE80211_STA_RX_BW_20: - if (link_sta->pub->ht_cap.ht_supported) - return NL80211_CHAN_WIDTH_20; - else - return NL80211_CHAN_WIDTH_20_NOHT; - case IEEE80211_STA_RX_BW_40: - return NL80211_CHAN_WIDTH_40; - case IEEE80211_STA_RX_BW_80: - return NL80211_CHAN_WIDTH_80; - case IEEE80211_STA_RX_BW_160: - /* - * This applied for both 160 and 80+80. since we use - * the returned value to consider degradation of - * ctx->conf.min_def, we have to make sure to take - * the bigger one (NL80211_CHAN_WIDTH_160). - * Otherwise we might try degrading even when not - * needed, as the max required sta_bw returned (80+80) - * 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; - } + if (width == IEEE80211_STA_RX_BW_20 && + !link_sta->pub->ht_cap.ht_supported && + !link_sta->pub->he_cap.has_he) + return NL80211_CHAN_WIDTH_20_NOHT; + + /* + * This returns 160 for both 160 and 80+80. Since we use + * the returned value to consider narrowing for + * ctx->conf.min_def, that's correct and necessary. + */ + return ieee80211_sta_rx_bw_to_chan_width(width); } static enum nl80211_chan_width ieee80211_get_max_required_bw(struct ieee80211_link_data *link) { struct ieee80211_sub_if_data *sdata = link->sdata; - unsigned int link_id = link->link_id; enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; struct sta_info *sta; @@ -503,7 +509,7 @@ ieee80211_get_max_required_bw(struct ieee80211_link_data *link) !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) continue; - max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link_id)); + max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link)); } return max_bw; @@ -551,6 +557,7 @@ ieee80211_get_width_of_link(struct ieee80211_link_data *link) case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_NAN_DATA: + case NL80211_IFTYPE_PD: WARN_ON_ONCE(1); break; } @@ -635,9 +642,7 @@ __ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, lockdep_assert_wiphy(local->hw.wiphy); /* don't optimize non-20MHz based and radar_enabled confs */ - if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || - ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || - ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || + if (ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || ctx->conf.def.width == NL80211_CHAN_WIDTH_2 || ctx->conf.def.width == NL80211_CHAN_WIDTH_4 || ctx->conf.def.width == NL80211_CHAN_WIDTH_8 || @@ -709,8 +714,9 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local, else new_chandef = &link_conf->chanreq.oper; - new_sta_bw = _ieee80211_sta_cur_vht_bw(link_sta, - new_chandef); + new_sta_bw = ieee80211_sta_current_bw(link_sta, + new_chandef, + IEEE80211_STA_BW_TX_TO_STA); /* nothing change */ if (new_sta_bw == link_sta->pub->bandwidth) @@ -733,6 +739,9 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local, * recalc the min required chan width of the channel context, which is * the max of min required widths of all the interfaces bound to this * channel context. + * + * Note: ieee80211_update_ap_bandwidth() relies on this iterating all + * affected stations, even if min_def didn't change. */ static void _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, @@ -740,16 +749,20 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, struct ieee80211_link_data *rsvd_for, bool check_reserved) { - u32 changed = __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for, - check_reserved); + u32 changed; - if (!changed) + /* No recalc for S1G chan ctx's */ + if (cfg80211_chandef_is_s1g(&ctx->conf.def)) return; + changed = __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for, + check_reserved); + /* check is BW narrowed */ ieee80211_chan_bw_change(local, ctx, false, true); - drv_change_chanctx(local, ctx, changed); + if (changed) + drv_change_chanctx(local, ctx, changed); /* check is BW wider */ ieee80211_chan_bw_change(local, ctx, false, false); @@ -761,13 +774,44 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, _ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false); } +static void +ieee80211_chanctx_update_npca_links(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + bool enable) +{ + struct ieee80211_chanctx_user_iter iter; + + if (!!ctx->conf.def.npca_chan != enable) + return; + + for_each_chanctx_user_assigned(local, ctx, &iter) { + if (!iter.link) + continue; + if (!iter.sdata->vif.cfg.assoc) + continue; + + if (enable) { + if (!iter.link->conf->chanreq.oper.npca_chan) + continue; + } else { + if (!iter.link->conf->npca.enabled) + continue; + } + + iter.link->conf->npca.enabled = enable; + drv_link_info_changed(local, iter.sdata, + iter.link->conf, + iter.link->link_id, + BSS_CHANGED_NPCA); + } +} + static void _ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, struct ieee80211_chanctx *old_ctx, const struct ieee80211_chan_req *chanreq, struct ieee80211_link_data *rsvd_for) { - const struct cfg80211_chan_def *chandef = &chanreq->oper; struct ieee80211_chan_req ctx_req = { .oper = ctx->conf.def, .ap = ctx->conf.ap, @@ -775,7 +819,7 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, u32 changed = 0; /* 5/10 MHz not handled here */ - switch (chandef->width) { + switch (chanreq->oper.width) { case NL80211_CHAN_WIDTH_1: case NL80211_CHAN_WIDTH_2: case NL80211_CHAN_WIDTH_4: @@ -820,10 +864,15 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; if (ctx->conf.def.punctured != chanreq->oper.punctured) changed |= IEEE80211_CHANCTX_CHANGE_PUNCTURING; + if (ctx->conf.def.npca_chan != chanreq->oper.npca_chan) + changed |= IEEE80211_CHANCTX_CHANGE_NPCA; + if (chanreq->oper.npca_chan && + ctx->conf.def.npca_punctured != chanreq->oper.npca_punctured) + changed |= IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT; } if (!cfg80211_chandef_identical(&ctx->conf.ap, &chanreq->ap)) changed |= IEEE80211_CHANCTX_CHANGE_AP; - ctx->conf.def = *chandef; + ctx->conf.def = chanreq->oper; ctx->conf.ap = chanreq->ap; /* check if min chanctx also changed */ @@ -832,10 +881,16 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local, ieee80211_add_wbrf(local, &ctx->conf.def); + /* disable NPCA on the link using it */ + ieee80211_chanctx_update_npca_links(local, ctx, false); + drv_change_chanctx(local, ctx, changed); /* check if BW is wider */ ieee80211_chan_bw_change(local, old_ctx, false, false); + + /* enable NPCA on the link that requested it */ + ieee80211_chanctx_update_npca_links(local, ctx, true); } static void ieee80211_change_chanctx(struct ieee80211_local *local, @@ -1546,6 +1601,7 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_NAN_DATA: + case NL80211_IFTYPE_PD: case NUM_NL80211_IFTYPES: WARN_ON(1); break; @@ -1875,16 +1931,21 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) } if (n_assigned != n_reserved) { - if (n_ready == n_reserved) { - wiphy_info(local->hw.wiphy, - "channel context reservation cannot be finalized because some interfaces aren't switching\n"); - err = -EBUSY; - goto err; - } + if (n_ready != n_reserved) + return -EAGAIN; - return -EAGAIN; + if (n_assigned == n_reserved + 1 && + ieee80211_nan_try_evacuate(&local->hw, + &ctx->replace_ctx->conf)) + goto use_reserved; + + wiphy_info(local->hw.wiphy, + "channel context reservation cannot be finalized because some interfaces aren't switching\n"); + err = -EBUSY; + goto err; } +use_reserved: ctx->conf.radar_enabled = false; for_each_chanctx_user_reserved(local, ctx, &iter) { if (ieee80211_link_has_in_place_reservation(iter.link) && @@ -2153,6 +2214,64 @@ ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata, assign_on_failure, radio_idx); } +static bool +ieee80211_nan_evac_chanctx_filter(struct ieee80211_chanctx *ctx, + void *filter_data) +{ + return ctx == filter_data; +} + +static int +ieee80211_try_nan_chan_evacuation(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + enum ieee80211_chanctx_mode mode, + u8 radar_detect_width) +{ + struct ieee80211_sub_if_data *nan_sdata = ieee80211_find_nan_sdata(local); + struct ieee80211_check_combinations_data comb_data = { + .chandef = chandef, + .chanmode = mode, + .radar_detect = radar_detect_width, + .radio_idx = -1, + .chanctx_filter = ieee80211_nan_evac_chanctx_filter, + }; + struct ieee80211_nan_channel *evac_chan; + struct ieee80211_chanctx *evac_ctx; + int ret; + + if (!nan_sdata) + return -ENOENT; + + /* Find an evacuation candidate... */ + evac_chan = ieee80211_nan_find_evac_chan(local, nan_sdata, NULL); + if (!evac_chan || WARN_ON(!evac_chan->chanctx_conf)) + return -ENOENT; + + evac_ctx = container_of(evac_chan->chanctx_conf, + struct ieee80211_chanctx, conf); + + /* + * ... check combinations assuming to-be-evacuated ctx is already + * released + */ + comb_data.filter_data = evac_ctx; + ret = ieee80211_check_combinations_ext(sdata, &comb_data); + if (ret < 0) + return ret; + + /* That helped! Let's evacuate the channel */ + ieee80211_nan_evacuate_channel(nan_sdata, evac_chan); + + /* Re-check, just to be on the safe-side */ + ret = ieee80211_check_combinations(sdata, chandef, mode, + radar_detect_width, -1); + + /* That shouldn't happen, we checked before! */ + WARN_ON(ret); + return ret; +} + int _ieee80211_link_use_channel(struct ieee80211_link_data *link, const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode, @@ -2184,8 +2303,15 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link, ret = ieee80211_check_combinations(sdata, &chanreq->oper, mode, radar_detect_width, -1); - if (ret < 0) - goto out; + if (ret < 0) { + /* Let's check if evacuating a NAN channel will help */ + ret = ieee80211_try_nan_chan_evacuation(local, sdata, + &chanreq->oper, + mode, + radar_detect_width); + if (ret < 0) + goto out; + } if (!local->in_reconfig) __ieee80211_link_release_channel(link, false); diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 5a1831b08677..a4d5461f6480 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -320,26 +320,17 @@ static ssize_t aql_enable_read(struct file *file, char __user *user_buf, static ssize_t aql_enable_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - char buf[3]; - size_t len; + bool val; + int ret; - if (count > sizeof(buf)) - return -EINVAL; + ret = kstrtobool_from_user(user_buf, count, &val); + if (unlikely(ret)) + return ret; - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - buf[sizeof(buf) - 1] = '\0'; - len = strlen(buf); - if (len > 0 && buf[len - 1] == '\n') - buf[len - 1] = 0; - - if (buf[0] == '0' && buf[1] == '\0') - static_branch_enable(&aql_disable); - else if (buf[0] == '1' && buf[1] == '\0') + if (val) static_branch_disable(&aql_disable); else - return -EINVAL; + static_branch_enable(&aql_disable); return count; } @@ -371,26 +362,14 @@ static ssize_t force_tx_status_write(struct file *file, loff_t *ppos) { struct ieee80211_local *local = file->private_data; - char buf[3]; + bool val; + int ret; - if (count >= sizeof(buf)) - return -EINVAL; - - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; - - if (buf[0] == '0' && buf[1] == '\0') - local->force_tx_status = 0; - else if (buf[0] == '1' && buf[1] == '\0') - local->force_tx_status = 1; - else - return -EINVAL; + ret = kstrtobool_from_user(user_buf, count, &val); + if (unlikely(ret)) + return ret; + local->force_tx_status = val; return count; } diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c index 768bfc4e737d..23fe82df6abf 100644 --- a/net/mac80211/eht.c +++ b/net/mac80211/eht.c @@ -2,7 +2,7 @@ /* * EHT handling * - * Copyright(c) 2021-2025 Intel Corporation + * Copyright(c) 2021-2026 Intel Corporation */ #include "driver-ops.h" @@ -74,9 +74,6 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, eht_cap->has_eht = true; - link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta); - link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); - /* * The MPDU length bits are reserved on all but 2.4 GHz and get set via * VHT (5 GHz) or HE (6 GHz) capabilities. @@ -103,177 +100,3 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, ieee80211_sta_recalc_aggregates(&link_sta->sta->sta); } - -static void -ieee80211_send_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *req, int opt_len) -{ - int len = IEEE80211_MIN_ACTION_SIZE(eml_omn); - struct ieee80211_local *local = sdata->local; - struct ieee80211_mgmt *mgmt; - struct sk_buff *skb; - - len += opt_len; /* optional len */ - skb = dev_alloc_skb(local->tx_headroom + len); - if (!skb) - return; - - skb_reserve(skb, local->tx_headroom); - mgmt = skb_put_zero(skb, len); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - memcpy(mgmt->da, req->sa, ETH_ALEN); - memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); - - mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT; - mgmt->u.action.action_code = WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF; - mgmt->u.action.eml_omn.dialog_token = - req->u.action.eml_omn.dialog_token; - mgmt->u.action.eml_omn.control = req->u.action.eml_omn.control & - ~(IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE | - IEEE80211_EML_CTRL_INDEV_COEX_ACT); - /* Copy optional fields from the received notification frame */ - memcpy(mgmt->u.action.eml_omn.variable, - req->u.action.eml_omn.variable, opt_len); - - ieee80211_tx_skb(sdata, skb); -} - -void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) -{ - int len = IEEE80211_MIN_ACTION_SIZE(eml_omn); - enum nl80211_iftype type = ieee80211_vif_type_p2p(&sdata->vif); - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - const struct wiphy_iftype_ext_capab *ift_ext_capa; - struct ieee80211_mgmt *mgmt = (void *)skb->data; - struct ieee80211_local *local = sdata->local; - u8 control = mgmt->u.action.eml_omn.control; - u8 *ptr = mgmt->u.action.eml_omn.variable; - struct ieee80211_eml_params eml_params = { - .link_id = status->link_id, - .control = control, - }; - struct sta_info *sta; - int opt_len = 0; - - if (!ieee80211_vif_is_mld(&sdata->vif)) - return; - - /* eMLSR and eMLMR can't be enabled at the same time */ - if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) && - (control & IEEE80211_EML_CTRL_EMLMR_MODE)) - return; - - if ((control & IEEE80211_EML_CTRL_EMLMR_MODE) && - (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE)) - return; - - ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, type); - if (!ift_ext_capa) - return; - - if (!status->link_valid) - return; - - sta = sta_info_get_bss(sdata, mgmt->sa); - if (!sta) - return; - - if (control & IEEE80211_EML_CTRL_EMLSR_MODE) { - u8 emlsr_param_update_len; - - if (!(ift_ext_capa->eml_capabilities & - IEEE80211_EML_CAP_EMLSR_SUPP)) - return; - - opt_len += sizeof(__le16); /* eMLSR link_bitmap */ - /* eMLSR param update field is not part of Notfication frame - * sent by the AP to client so account it separately. - */ - emlsr_param_update_len = - !!(control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE); - - if (skb->len < len + opt_len + emlsr_param_update_len) - return; - - if (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE) { - u8 pad_delay, trans_delay; - - pad_delay = u8_get_bits(ptr[2], - IEEE80211_EML_EMLSR_PAD_DELAY); - if (pad_delay > - IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US) - return; - - trans_delay = u8_get_bits(ptr[2], - IEEE80211_EML_EMLSR_TRANS_DELAY); - if (trans_delay > - IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US) - return; - - /* Update sta padding and transition delay */ - sta->sta.eml_cap = - u8_replace_bits(sta->sta.eml_cap, - pad_delay, - IEEE80211_EML_CAP_EMLSR_PADDING_DELAY); - sta->sta.eml_cap = - u8_replace_bits(sta->sta.eml_cap, - trans_delay, - IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY); - } - } - - if (control & IEEE80211_EML_CTRL_EMLMR_MODE) { - u8 mcs_map_size; - int i; - - if (!(ift_ext_capa->eml_capabilities & - IEEE80211_EML_CAP_EMLMR_SUPPORT)) - return; - - opt_len += sizeof(__le16); /* eMLMR link_bitmap */ - opt_len++; /* eMLMR mcs_map_count */ - if (skb->len < len + opt_len) - return; - - eml_params.emlmr_mcs_map_count = ptr[2]; - if (eml_params.emlmr_mcs_map_count > 2) - return; - - mcs_map_size = 3 * (1 + eml_params.emlmr_mcs_map_count); - opt_len += mcs_map_size; - if (skb->len < len + opt_len) - return; - - for (i = 0; i < mcs_map_size; i++) { - u8 rx_mcs, tx_mcs; - - rx_mcs = u8_get_bits(ptr[3 + i], - IEEE80211_EML_EMLMR_RX_MCS_MAP); - if (rx_mcs > 8) - return; - - tx_mcs = u8_get_bits(ptr[3 + i], - IEEE80211_EML_EMLMR_TX_MCS_MAP); - if (tx_mcs > 8) - return; - } - - memcpy(eml_params.emlmr_mcs_map_bw, &ptr[3], mcs_map_size); - } - - if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) || - (control & IEEE80211_EML_CTRL_EMLMR_MODE)) { - eml_params.link_bitmap = get_unaligned_le16(ptr); - if ((eml_params.link_bitmap & sdata->vif.active_links) != - eml_params.link_bitmap) - return; - } - - if (drv_set_eml_op_mode(sdata, &sta->sta, &eml_params)) - return; - - ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len); -} diff --git a/net/mac80211/he.c b/net/mac80211/he.c index a3e16a5bec22..5acf482c177a 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -3,7 +3,7 @@ * HE handling * * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright(c) 2019-2025 Intel Corporation + * Copyright(c) 2019-2026 Intel Corporation */ #include "ieee80211_i.h" @@ -159,10 +159,6 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, he_cap->has_he = true; - link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta); - if (sdata->vif.type != NL80211_IFTYPE_NAN) - link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); - if (he_6ghz_capa) ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta); @@ -266,26 +262,6 @@ ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif, } } -static void ieee80211_link_sta_rc_update_omi(struct ieee80211_link_data *link, - struct link_sta_info *link_sta) -{ - struct ieee80211_sub_if_data *sdata = link->sdata; - struct ieee80211_supported_band *sband; - enum ieee80211_sta_rx_bandwidth new_bw; - enum nl80211_band band; - - band = link->conf->chanreq.oper.chan->band; - sband = sdata->local->hw.wiphy->bands[band]; - - new_bw = ieee80211_sta_cur_vht_bw(link_sta); - if (link_sta->pub->bandwidth == new_bw) - return; - - link_sta->pub->bandwidth = new_bw; - rate_control_rate_update(sdata->local, sband, link_sta, - IEEE80211_RC_BW_CHANGED); -} - bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta, enum ieee80211_sta_rx_bandwidth bw) { @@ -326,7 +302,7 @@ bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta, if (bw < link_sta->rx_omi_bw_staging) { link_sta->rx_omi_bw_tx = bw; - ieee80211_link_sta_rc_update_omi(link, link_sta); + ieee80211_link_sta_update_rc_bw(link, link_sta); } else { link_sta->rx_omi_bw_rx = bw; ieee80211_recalc_chanctx_min_def(local, chanctx); @@ -368,7 +344,7 @@ void ieee80211_finalize_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta) /* rate control in finalize only when widening bandwidth */ WARN_ON(link_sta->rx_omi_bw_tx > link_sta->rx_omi_bw_staging); link_sta->rx_omi_bw_tx = link_sta->rx_omi_bw_staging; - ieee80211_link_sta_rc_update_omi(link, link_sta); + ieee80211_link_sta_update_rc_bw(link, link_sta); } if (link_sta->rx_omi_bw_rx != link_sta->rx_omi_bw_staging) { diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 97719298e038..e1e1b7f82f3f 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -140,14 +140,11 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, const struct ieee80211_ht_cap *ht_cap_ie, struct link_sta_info *link_sta) { - struct ieee80211_bss_conf *link_conf; struct sta_info *sta = link_sta->sta; struct ieee80211_sta_ht_cap ht_cap, own_cap; u8 ampdu_info, tx_mcs_set_cap; int i, max_tx_streams; bool changed; - enum ieee80211_sta_rx_bandwidth bw; - enum nl80211_chan_width width; memset(&ht_cap, 0, sizeof(ht_cap)); @@ -256,45 +253,6 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, memcpy(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap)); - rcu_read_lock(); - link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]); - if (WARN_ON(!link_conf)) { - width = NL80211_CHAN_WIDTH_20_NOHT; - } else if (sdata->vif.type == NL80211_IFTYPE_NAN || - sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { - /* In NAN, link_sta->bandwidth is invalid since NAN operates on - * multiple channels. Just take the maximum. - */ - width = NL80211_CHAN_WIDTH_320; - } else { - width = link_conf->chanreq.oper.width; - } - - switch (width) { - default: - WARN_ON_ONCE(1); - fallthrough; - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - bw = IEEE80211_STA_RX_BW_20; - break; - case NL80211_CHAN_WIDTH_40: - case NL80211_CHAN_WIDTH_80: - case NL80211_CHAN_WIDTH_80P80: - case NL80211_CHAN_WIDTH_160: - case NL80211_CHAN_WIDTH_320: - bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? - IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; - break; - } - rcu_read_unlock(); - - link_sta->pub->bandwidth = bw; - - link_sta->cur_max_bandwidth = - ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? - IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; - if (sta->sdata->vif.type == NL80211_IFTYPE_AP || sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sta->sdata->vif.type == NL80211_IFTYPE_NAN || @@ -626,34 +584,31 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local, struct link_sta_info *link_sta, u8 chanwidth, enum nl80211_band band) { - enum ieee80211_sta_rx_bandwidth max_bw, new_bw; - struct ieee80211_supported_band *sband; - struct sta_opmode_info sta_opmode = {}; + enum ieee80211_sta_rx_bandwidth max_bw; + struct sta_opmode_info sta_opmode = { + .changed = STA_OPMODE_MAX_BW_CHANGED, + }; + struct ieee80211_link_data *link; lockdep_assert_wiphy(local->hw.wiphy); + link = sdata_dereference(sdata->link[link_sta->link_id], sdata); + if (WARN_ON(!link)) + return; + if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) max_bw = IEEE80211_STA_RX_BW_20; else - max_bw = ieee80211_sta_cap_rx_bw(link_sta); + max_bw = IEEE80211_STA_RX_BW_MAX; - /* set cur_max_bandwidth and recalc sta bw */ - link_sta->cur_max_bandwidth = max_bw; - new_bw = ieee80211_sta_cur_vht_bw(link_sta); + /* set op_mode_bw and recalc sta bw */ + link_sta->op_mode_bw = max_bw; - if (link_sta->pub->bandwidth == new_bw) + if (!ieee80211_link_sta_update_rc_bw(link, link_sta)) return; - link_sta->pub->bandwidth = new_bw; - sband = local->hw.wiphy->bands[band]; - sta_opmode.bw = - ieee80211_sta_rx_bw_to_chan_width(link_sta); - sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED; + sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(link_sta->pub->bandwidth); - rate_control_rate_update(local, sband, link_sta, - IEEE80211_RC_BW_CHANGED); - cfg80211_sta_opmode_change_notify(sdata->dev, - sta->addr, - &sta_opmode, - GFP_KERNEL); + cfg80211_sta_opmode_change_notify(sdata->dev, sta->addr, + &sta_opmode, GFP_KERNEL); } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 97292ff51475..d0fd6054f182 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -167,8 +167,6 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, /* add HT capability and information IEs */ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT && - chandef->width != NL80211_CHAN_WIDTH_5 && - chandef->width != NL80211_CHAN_WIDTH_10 && sband->ht_cap.ht_supported) { struct ieee80211_sta_ht_cap ht_cap; @@ -259,9 +257,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, chan = chanreq.oper.chan; if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chanreq.oper, NL80211_IFTYPE_ADHOC)) { - if (chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - chanreq.oper.width == NL80211_CHAN_WIDTH_10 || - chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + if (chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || chanreq.oper.width == NL80211_CHAN_WIDTH_20) { sdata_info(sdata, "Failed to join IBSS, beacons forbidden\n"); @@ -405,12 +401,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef); cfg80211_chandef_create(&chandef, cbss->channel, chan_type); break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - cfg80211_chandef_create(&chandef, cbss->channel, - NL80211_CHAN_NO_HT); - chandef.width = sdata->u.ibss.chandef.width; - break; case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_160: @@ -553,6 +543,9 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) memcpy(addr, sta->sta.addr, ETH_ALEN); + ieee80211_sta_init_nss_bw_capa(&sta->deflink, + &sdata->deflink.conf->chanreq.oper); + ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr); sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); @@ -759,8 +752,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, lockdep_assert_wiphy(sdata->local->hw.wiphy); switch (ifibss->chandef.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: conn.mode = IEEE80211_CONN_MODE_LEGACY; fallthrough; @@ -808,19 +799,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, cfg80211_chandef_create(¶ms.chandef, params.chandef.chan, ch_type); break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - if (params.chandef.width != ifibss->chandef.width) { - sdata_info(sdata, - "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", - ifibss->bssid, - params.chandef.chan->center_freq, - params.chandef.width, - params.chandef.center_freq1, - params.chandef.center_freq2); - goto disconnect; - } - break; default: /* should not happen, conn_flags should prevent VHT modes. */ WARN_ON(1); @@ -1002,9 +980,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, } if (sta && elems->ht_operation && elems->ht_cap_elem && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) { + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { /* we both use HT */ struct ieee80211_ht_cap htcap_ie; struct cfg80211_chan_def chandef; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2a693406294b..34a9ea8b6f85 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -99,6 +99,7 @@ enum ieee80211_status_data { IEEE80211_STATUS_TYPE_INVALID = 0, IEEE80211_STATUS_TYPE_SMPS = 1, IEEE80211_STATUS_TYPE_NEG_TTLM = 2, + IEEE80211_STATUS_TYPE_UHR_OMP = 3, IEEE80211_STATUS_SUBDATA_MASK = 0x1ff0, }; @@ -412,6 +413,7 @@ enum ieee80211_conn_bw_limit { struct ieee80211_conn_settings { enum ieee80211_conn_mode mode; enum ieee80211_conn_bw_limit bw_limit; + bool dbe_enabled; }; extern const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited; @@ -639,6 +641,15 @@ struct ieee80211_if_managed { bool enabled; u8 dialog_token; } epcs; + + struct { + struct wiphy_hrtimer_work status_work; + u32 timeout_us; + u16 links; + u16 pending, pending_init; + u8 dialog_token; + bool acked; + } uhr_omp; }; struct ieee80211_if_ibss { @@ -1133,6 +1144,11 @@ struct ieee80211_link_data { struct ieee80211_bss_conf *conf; + struct { + enum ieee80211_sta_rx_bandwidth he_and_lower; + enum ieee80211_sta_rx_bandwidth eht; /* and UHR non-DBE */ + } bss_bw; + #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *debugfs_dir; #endif @@ -1786,6 +1802,8 @@ struct ieee802_11_elems { const u8 *ie_start; size_t total_len; u32 crc; + u8 frame_type; + bool from_ap; /* pointers to IEs */ const struct ieee80211_tdls_lnkie *lnk_id; @@ -1892,6 +1910,15 @@ struct ieee802_11_elems { struct ieee80211_mle_per_sta_profile *prof; size_t sta_prof_len; + /* + * When parsing the beacon with MBSSID (from a transmitted BSS), this + * indicates that the profile the parser was instructed to look for + * (via the bss value in &struct ieee80211_elems_parse_params) couldn't + * be found (due to EMA, or perhaps broken AP) and the result cannot be + * considered complete. + */ + bool mbssid_nontx_profile_missing; + /* whether/which parse error occurred while retrieving these elements */ u8 parse_error; }; @@ -1940,6 +1967,11 @@ ieee80211_vif_get_num_mcast_if(struct ieee80211_sub_if_data *sdata) return -1; } +static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) +{ + return test_bit(SDATA_STATE_RUNNING, &sdata->state); +} + int ieee80211_hw_config(struct ieee80211_local *local, int radio_idx, u32 changed); int ieee80211_hw_conf_chan(struct ieee80211_local *local); @@ -1969,6 +2001,11 @@ void ieee80211_clear_fast_rx(struct sta_info *sta); bool ieee80211_is_our_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr, int *out_link_id); +/* AP code */ +void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link); + /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, @@ -1984,10 +2021,8 @@ void ieee80211_send_pspoll(struct ieee80211_local *local, void ieee80211_recalc_ps(struct ieee80211_local *local); void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata); -void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb); -void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb); +void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata); @@ -2046,6 +2081,29 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, struct cfg80211_nan_peer_sched *sched); void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched); void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata); +struct ieee80211_nan_channel * +ieee80211_nan_find_evac_chan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx); +void ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata, + struct ieee80211_nan_channel *nan_channel); + +static inline struct ieee80211_sub_if_data * +ieee80211_find_nan_sdata(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + + lockdep_assert_wiphy(local->hw.wiphy); + + /* Find the NAN interface - there can only be one */ + list_for_each_entry(sdata, &local->interfaces, list) { + if (ieee80211_sdata_running(sdata) && + sdata->vif.type == NL80211_IFTYPE_NAN) + return sdata; + } + + return NULL; +} /* scan/BSS handling */ void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work); @@ -2093,7 +2151,8 @@ void ieee80211_roc_purge(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie); + unsigned int duration, u64 *cookie, + const u8 *rx_addr); int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, @@ -2146,11 +2205,6 @@ void ieee80211_recalc_txpower(struct ieee80211_link_data *link, bool update_bss); void ieee80211_recalc_offload(struct ieee80211_local *local); -static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) -{ - return test_bit(SDATA_STATE_RUNNING, &sdata->state); -} - /* link handling */ void ieee80211_link_setup(struct ieee80211_link_data *link); void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, @@ -2299,25 +2353,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, const struct ieee80211_vht_cap *vht_cap_ie, const struct ieee80211_vht_cap *vht_cap_ie2, struct link_sta_info *link_sta); -enum ieee80211_sta_rx_bandwidth -_ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, - struct cfg80211_chan_def *chandef); -static inline enum ieee80211_sta_rx_bandwidth -ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta) -{ - return _ieee80211_sta_cap_rx_bw(link_sta, NULL); -} -enum ieee80211_sta_rx_bandwidth -_ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta, - struct cfg80211_chan_def *chandef); -static inline enum ieee80211_sta_rx_bandwidth -ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta) -{ - return _ieee80211_sta_cur_vht_bw(link_sta, NULL); -} -void ieee80211_sta_init_nss(struct link_sta_info *link_sta); -enum nl80211_chan_width -ieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta); void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, struct ieee80211_mgmt *mgmt); @@ -2331,8 +2366,6 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_vht_cap *vht_cap); void ieee80211_get_vht_mask_from_cap(__le16 vht_cap, u16 vht_mask[NL80211_VHT_NSS_MAX]); -enum nl80211_chan_width -ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *sta); /* HE */ void @@ -2718,6 +2751,9 @@ void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata, void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +enum nl80211_chan_width +ieee80211_sta_rx_bw_to_chan_width(enum ieee80211_sta_rx_bandwidth bw); + /* element building in SKBs */ int ieee80211_put_srates_elem(struct sk_buff *skb, const struct ieee80211_supported_band *sband, @@ -2767,6 +2803,7 @@ ieee80211_chanreq_downgrade(struct ieee80211_chan_req *chanreq, return; if (conn->mode < IEEE80211_CONN_MODE_EHT) chanreq->ap.chan = NULL; + conn->dbe_enabled = false; } bool ieee80211_chanreq_identical(const struct ieee80211_chan_req *a, @@ -2827,10 +2864,36 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *csa_settings); void ieee80211_recalc_sb_count(struct ieee80211_sub_if_data *sdata, u64 tsf); void ieee80211_recalc_dtim(struct ieee80211_sub_if_data *sdata, u64 tsf); -int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, - const struct cfg80211_chan_def *chandef, - enum ieee80211_chanctx_mode chanmode, - u8 radar_detect, int radio_idx); + +struct ieee80211_check_combinations_data { + const struct cfg80211_chan_def *chandef; + enum ieee80211_chanctx_mode chanmode; + u8 radar_detect; + int radio_idx; + bool (*chanctx_filter)(struct ieee80211_chanctx *ctx, + void *filter_data); + void *filter_data; +}; + +int ieee80211_check_combinations_ext(struct ieee80211_sub_if_data *sdata, + struct ieee80211_check_combinations_data *data); + +static inline int +ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + enum ieee80211_chanctx_mode chanmode, + u8 radar_detect, int radio_idx) +{ + struct ieee80211_check_combinations_data data = { + .chandef = chandef, + .chanmode = chanmode, + .radar_detect = radar_detect, + .radio_idx = radio_idx, + }; + + return ieee80211_check_combinations_ext(sdata, &data); +} + int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx); u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev); void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, @@ -2891,8 +2954,6 @@ void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata); -void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb); void ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, @@ -2900,28 +2961,17 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, const struct ieee80211_eht_cap_elem *eht_cap_ie_elem, u8 eht_cap_len, struct link_sta_info *link_sta); -void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len); -void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len); int ieee80211_req_neg_ttlm(struct ieee80211_sub_if_data *sdata, struct cfg80211_ttlm_params *params); -void ieee80211_process_ttlm_teardown(struct ieee80211_sub_if_data *sdata); void ieee80211_check_wbrf_support(struct ieee80211_local *local); void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); void ieee80211_remove_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef); int ieee80211_mgd_set_epcs(struct ieee80211_sub_if_data *sdata, bool enable); -void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len); -void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len); int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, struct cfg80211_ml_reconf_req *req); -void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len); void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata); void @@ -2944,10 +2994,15 @@ void ieee80211_rearrange_tpe_psd(struct ieee80211_parsed_tpe_psd *psd, struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, - struct cfg80211_bss *cbss, int link_id, + struct cfg80211_bss *cbss, + struct link_sta_info *link_sta, int link_id, struct ieee80211_chan_req *chanreq, struct cfg80211_chan_def *ap_chandef, unsigned long *userspace_selectors); +int ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_ttlm_elem *ttlm, + struct ieee80211_neg_ttlm *neg_ttlm, + u8 *direction); #else #define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) #define VISIBLE_IF_MAC80211_KUNIT static diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 95b779c4d627..086272c3ec08 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -648,6 +648,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do spin_unlock_bh(&sdata->u.nan.de.func_lock); } + + /* + * Free the remaining keys that might be associated with the + * NAN interface, e.g., IGTK and BIGTK used for Tx. + */ + ieee80211_free_keys(sdata, true); break; case NL80211_IFTYPE_NAN_DATA: RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL); @@ -1400,6 +1406,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_PD: /* no special treatment */ break; case NL80211_IFTYPE_NAN_DATA: @@ -1532,7 +1539,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) FIF_PROBE_REQ); if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && - sdata->vif.type != NL80211_IFTYPE_NAN) + sdata->vif.type != NL80211_IFTYPE_NAN && + sdata->vif.type != NL80211_IFTYPE_PD) changed |= ieee80211_reset_erp_info(sdata); ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed); @@ -1548,6 +1556,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) break; case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_PD: break; default: /* not reached */ @@ -1713,50 +1722,6 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, default: break; } - } else if (ieee80211_is_action(mgmt->frame_control) && - mgmt->u.action.category == WLAN_CATEGORY_PROTECTED_EHT) { - if (sdata->vif.type == NL80211_IFTYPE_AP) { - switch (mgmt->u.action.action_code) { - case WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF: - ieee80211_rx_eml_op_mode_notif(sdata, skb); - break; - default: - break; - } - } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { - switch (mgmt->u.action.action_code) { - case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ: - ieee80211_process_neg_ttlm_req(sdata, mgmt, - skb->len); - break; - case WLAN_PROTECTED_EHT_ACTION_TTLM_RES: - ieee80211_process_neg_ttlm_res(sdata, mgmt, - skb->len); - break; - case WLAN_PROTECTED_EHT_ACTION_TTLM_TEARDOWN: - ieee80211_process_ttlm_teardown(sdata); - break; - case WLAN_PROTECTED_EHT_ACTION_LINK_RECONFIG_RESP: - ieee80211_process_ml_reconf_resp(sdata, mgmt, - skb->len); - break; - case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_RESP: - ieee80211_process_epcs_ena_resp(sdata, mgmt, - skb->len); - break; - case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_TEARDOWN: - ieee80211_process_epcs_teardown(sdata, mgmt, - skb->len); - break; - default: - break; - } - } - } else if (ieee80211_is_ext(mgmt->frame_control)) { - if (sdata->vif.type == NL80211_IFTYPE_STATION) - ieee80211_sta_rx_queued_ext(sdata, skb); - else - WARN_ON(1); } else if (ieee80211_is_data_qos(mgmt->frame_control)) { struct ieee80211_hdr *hdr = (void *)mgmt; struct sta_info *sta; @@ -1788,8 +1753,11 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, true); } } else switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + ieee80211_ap_rx_queued_frame(sdata, skb); + break; case NL80211_IFTYPE_STATION: - ieee80211_sta_rx_queued_mgmt(sdata, skb); + ieee80211_sta_rx_queued_frame(sdata, skb); break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_rx_queued_mgmt(sdata, skb); @@ -1988,6 +1956,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_NAN_DATA: + case NL80211_IFTYPE_PD: break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: @@ -2280,7 +2249,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sdata->dev = NULL; strscpy(sdata->name, name, IFNAMSIZ); - ieee80211_assign_perm_addr(local, wdev->address, type); + + if (is_valid_ether_addr(params->macaddr)) + memcpy(wdev->address, params->macaddr, ETH_ALEN); + else + ieee80211_assign_perm_addr(local, wdev->address, type); + memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 4b8965633df3..f45e792abede 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -6,7 +6,7 @@ * Copyright 2007-2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright 2018-2020, 2022-2025 Intel Corporation + * Copyright 2018-2020, 2022-2026 Intel Corporation */ #include @@ -150,11 +150,14 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) sta = key->sta; /* - * If this is a per-STA GTK, check if it - * is supported; if not, return. + * Allow installation of a per-STA GTK if per-STA GTK is supported + * by the driver or the interface is a NAN Data interface (as + * per-station GTKs are required to be supported if secure NAN is + * supported). */ if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) && - !ieee80211_hw_check(&key->local->hw, SUPPORTS_PER_STA_GTK)) + !(ieee80211_hw_check(&key->local->hw, SUPPORTS_PER_STA_GTK) || + sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) goto out_unsupported; if (sta && !sta->uploaded) @@ -867,11 +870,16 @@ int ieee80211_key_link(struct ieee80211_key *key, alt_key = wiphy_dereference(sdata->local->hw.wiphy, sta->ptk[idx ^ 1]); - /* The rekey code assumes that the old and new key are using + /* + * The rekey code assumes that the old and new key are using * the same cipher. Enforce the assumption for pairwise keys. + * NAN Data interfaces are exempt: Wi-Fi Aware v4.0 section 7.4 + * requires upgrading the ND-TKSA when a new NDP negotiates a + * stronger cipher suite. */ - if ((alt_key && alt_key->conf.cipher != key->conf.cipher) || - (old_key && old_key->conf.cipher != key->conf.cipher)) { + if (sdata->vif.type != NL80211_IFTYPE_NAN_DATA && + ((alt_key && alt_key->conf.cipher != key->conf.cipher) || + (old_key && old_key->conf.cipher != key->conf.cipher))) { ret = -EOPNOTSUPP; goto out; } diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 93e290dd783f..d0535268962c 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -2,7 +2,7 @@ /* * MLO link handling * - * Copyright (C) 2022-2025 Intel Corporation + * Copyright (C) 2022-2026 Intel Corporation */ #include #include @@ -292,6 +292,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, u16 old_active = sdata->vif.active_links; unsigned long add = new_links & ~old_links; unsigned long rem = old_links & ~new_links; + unsigned long sta_rem = rem; unsigned int link_id; int ret; struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link; @@ -299,6 +300,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS]; bool use_deflink = old_links == 0; /* set for error case */ bool non_sta = sdata->vif.type != NL80211_IFTYPE_STATION; + struct sta_info *sta; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -307,6 +309,9 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, if (old_links == new_links && dormant_links == sdata->vif.dormant_links) return 0; + if (!old_links || !new_links) + WARN_ON(sta_info_flush(sdata, -1) > 0); + /* if there were no old links, need to clear the pointers to deflink */ if (!old_links) rem |= BIT(0); @@ -399,6 +404,34 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, goto free; } + /* try to remove links that are now invalid from (MLO) stations */ + list_for_each_entry(sta, &sdata->local->sta_list, list) { + unsigned long rem_links = sta->sta.valid_links & sta_rem; + + if (sta->sdata != sdata) + continue; + + /* + * skip stations that would have no links left, + * those will be removed completely later + */ + if (sta->sta.valid_links == rem_links) + continue; + + for_each_set_bit(link_id, &rem_links, + IEEE80211_MLD_MAX_NUM_LINKS) + ieee80211_sta_remove_link(sta, link_id); + } + + /* + * Remove stations using any removed links. Note that due + * to the above station link removal, this only removes + * stations that were skipped above because they'd have no + * links left after link removal. + */ + for_each_set_bit(link_id, &sta_rem, IEEE80211_MLD_MAX_NUM_LINKS) + sta_info_flush(sdata, link_id); + /* use deflink/bss_conf again if and only if there are no more links */ use_deflink = new_links == 0; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f47dd58770ad..90d295cc364f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -750,6 +750,10 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4), }, + [NL80211_IFTYPE_NAN_DATA] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4), + }, }; static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { @@ -1459,8 +1463,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (supp_uhr) local->scan_ies_len += - 3 + sizeof(struct ieee80211_uhr_cap) + - sizeof(struct ieee80211_uhr_cap_phy); + 3 + sizeof(struct ieee80211_uhr_cap); if (!local->ops->hw_scan) { /* For hw_scan, driver needs to set these up. */ diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 04578447df9b..d4507e4e6ec1 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -439,9 +439,7 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!sband->ht_cap.ht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) @@ -480,9 +478,7 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!ht_cap->ht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) @@ -511,9 +507,7 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!sband->vht_cap.vht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap)) @@ -552,9 +546,7 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!vht_cap->vht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_operation)) @@ -576,9 +568,7 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, if (!sband) return -EINVAL; - if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; return ieee80211_put_he_cap(skb, sdata, sband, NULL); @@ -598,9 +588,7 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata, he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!he_cap || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; len = 2 + 1 + sizeof(struct ieee80211_he_operation); @@ -648,9 +636,7 @@ int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata, if (!sband) return -EINVAL; - if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; return ieee80211_put_eht_cap(skb, sdata, sband, NULL); @@ -669,9 +655,7 @@ int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *sk eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!eht_cap || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) + @@ -729,9 +713,7 @@ ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata, return; if (!ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT) || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return; sdata->vif.bss_conf.he_support = true; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 9d89ebcce1c1..7f962b003579 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -20,52 +20,7 @@ static void mesh_queue_preq(struct mesh_path *, u8); -static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae) -{ - if (ae) - offset += 6; - return get_unaligned_le32(preq_elem + offset); -} - -static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae) -{ - if (ae) - offset += 6; - return get_unaligned_le16(preq_elem + offset); -} - /* HWMP IE processing macros */ -#define AE_F (1<<6) -#define AE_F_SET(x) (*x & AE_F) -#define PREQ_IE_FLAGS(x) (*(x)) -#define PREQ_IE_HOPCOUNT(x) (*(x + 1)) -#define PREQ_IE_TTL(x) (*(x + 2)) -#define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0) -#define PREQ_IE_ORIG_ADDR(x) (x + 7) -#define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0) -#define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x)) -#define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x)) -#define PREQ_IE_TARGET_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26)) -#define PREQ_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27) -#define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x)) - - -#define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x) -#define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x) -#define PREP_IE_TTL(x) PREQ_IE_TTL(x) -#define PREP_IE_ORIG_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) -#define PREP_IE_ORIG_SN(x) u32_field_get(x, 27, AE_F_SET(x)) -#define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) -#define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)) -#define PREP_IE_TARGET_ADDR(x) (x + 3) -#define PREP_IE_TARGET_SN(x) u32_field_get(x, 9, 0) - -#define PERR_IE_TTL(x) (*(x)) -#define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) -#define PERR_IE_TARGET_ADDR(x) (x + 3) -#define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0) -#define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0) - #define MSEC_TO_TU(x) (x*1000/1024) #define SN_GT(x, y) ((s32)(y - x) < 0) #define SN_LT(x, y) ((s32)(x - y) < 0) @@ -414,25 +369,37 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, fresh_info = true; switch (action) { - case MPATH_PREQ: - orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie); - orig_sn = PREQ_IE_ORIG_SN(hwmp_ie); - orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie); - orig_metric = PREQ_IE_METRIC(hwmp_ie); - hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1; + case MPATH_PREQ: { + struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = + (void *)hwmp_ie; + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(hwmp_ie); + + orig_addr = preq_elem_top->orig_addr; + orig_sn = le32_to_cpu(preq_elem_top->orig_sn); + orig_lifetime = le32_to_cpu(preq_elem_bottom->lifetime); + orig_metric = le32_to_cpu(preq_elem_bottom->metric); + hopcount = preq_elem_top->hopcount + 1; break; - case MPATH_PREP: + } + case MPATH_PREP: { /* Originator here refers to the MP that was the target in the * Path Request. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ - orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); - orig_sn = PREP_IE_TARGET_SN(hwmp_ie); - orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); - orig_metric = PREP_IE_METRIC(hwmp_ie); - hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1; + struct ieee80211_mesh_hwmp_prep_top *prep_elem_top = + (void *)hwmp_ie; + struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom = + ieee80211_mesh_hwmp_prep_get_bottom(hwmp_ie); + + orig_addr = prep_elem_top->target_addr; + orig_sn = le32_to_cpu(prep_elem_top->target_sn); + orig_lifetime = le32_to_cpu(prep_elem_bottom->lifetime); + orig_metric = le32_to_cpu(prep_elem_bottom->metric); + hopcount = prep_elem_top->hopcount + 1; break; + } default: rcu_read_unlock(); return 0; @@ -579,6 +546,11 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *preq_elem, u32 orig_metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = (void *)preq_elem; + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(preq_elem); + struct ieee80211_mesh_hwmp_preq_target *target = + preq_elem_bottom->targets; struct mesh_path *mpath = NULL; const u8 *target_addr, *orig_addr; const u8 *da; @@ -589,13 +561,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, bool root_is_gate; /* Update target SN, if present */ - target_addr = PREQ_IE_TARGET_ADDR(preq_elem); - orig_addr = PREQ_IE_ORIG_ADDR(preq_elem); - target_sn = PREQ_IE_TARGET_SN(preq_elem); - orig_sn = PREQ_IE_ORIG_SN(preq_elem); - target_flags = PREQ_IE_TARGET_F(preq_elem); + target_addr = target[0].addr; + orig_addr = preq_elem_top->orig_addr; + target_sn = le32_to_cpu(target[0].sn); + orig_sn = le32_to_cpu(preq_elem_top->orig_sn); + target_flags = target[0].flags; /* Proactive PREQ gate announcements */ - flags = PREQ_IE_FLAGS(preq_elem); + flags = preq_elem_top->flags; root_is_gate = !!(flags & RANN_FLAG_IS_GATE); mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr); @@ -655,7 +627,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, } if (reply) { - lifetime = PREQ_IE_LIFETIME(preq_elem); + lifetime = le32_to_cpu(preq_elem_bottom->lifetime); ttl = ifmsh->mshcfg.element_ttl; if (ttl != 0) { mhwmp_dbg(sdata, "replying to the PREQ\n"); @@ -673,22 +645,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, u32 preq_id; u8 hopcount; - ttl = PREQ_IE_TTL(preq_elem); - lifetime = PREQ_IE_LIFETIME(preq_elem); + ttl = preq_elem_top->ttl; + lifetime = le32_to_cpu(preq_elem_bottom->lifetime); if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; return; } mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr); --ttl; - preq_id = PREQ_IE_PREQ_ID(preq_elem); - hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; + preq_id = le32_to_cpu(preq_elem_top->preq_id); + hopcount = preq_elem_top->hopcount + 1; da = (mpath && mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { - target_addr = PREQ_IE_TARGET_ADDR(preq_elem); - target_sn = PREQ_IE_TARGET_SN(preq_elem); + target_addr = target[0].addr; + target_sn = le32_to_cpu(target[0].sn); } mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, @@ -717,6 +689,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *prep_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_hwmp_prep_top *prep_elem_top = (void *)prep_elem; + struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom = + ieee80211_mesh_hwmp_prep_get_bottom(prep_elem); struct mesh_path *mpath; const u8 *target_addr, *orig_addr; u8 ttl, hopcount, flags; @@ -724,9 +699,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, u32 target_sn, orig_sn, lifetime; mhwmp_dbg(sdata, "received PREP from %pM\n", - PREP_IE_TARGET_ADDR(prep_elem)); + prep_elem_top->target_addr); - orig_addr = PREP_IE_ORIG_ADDR(prep_elem); + orig_addr = prep_elem_bottom->orig_addr; if (ether_addr_equal(orig_addr, sdata->vif.addr)) /* destination, no forwarding required */ return; @@ -734,7 +709,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, if (!ifmsh->mshcfg.dot11MeshForwarding) return; - ttl = PREP_IE_TTL(prep_elem); + ttl = prep_elem_top->ttl; if (ttl <= 1) { sdata->u.mesh.mshstats.dropped_frames_ttl++; return; @@ -753,12 +728,12 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN); spin_unlock_bh(&mpath->state_lock); --ttl; - flags = PREP_IE_FLAGS(prep_elem); - lifetime = PREP_IE_LIFETIME(prep_elem); - hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1; - target_addr = PREP_IE_TARGET_ADDR(prep_elem); - target_sn = PREP_IE_TARGET_SN(prep_elem); - orig_sn = PREP_IE_ORIG_SN(prep_elem); + flags = prep_elem_top->flags; + lifetime = le32_to_cpu(prep_elem_bottom->lifetime); + hopcount = prep_elem_top->hopcount + 1; + target_addr = prep_elem_top->target_addr; + target_sn = le32_to_cpu(prep_elem_top->target_sn); + orig_sn = le32_to_cpu(prep_elem_bottom->orig_sn); mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0, target_addr, target_sn, next_hop, hopcount, @@ -779,6 +754,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *perr_elem) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_hwmp_perr *perr_elem_s = (void *)perr_elem; struct mesh_path *mpath; u8 ttl; const u8 *ta, *target_addr; @@ -786,15 +762,15 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, u16 target_rcode; ta = mgmt->sa; - ttl = PERR_IE_TTL(perr_elem); + ttl = perr_elem_s->ttl; if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; return; } ttl--; - target_addr = PERR_IE_TARGET_ADDR(perr_elem); - target_sn = PERR_IE_TARGET_SN(perr_elem); - target_rcode = PERR_IE_TARGET_RCODE(perr_elem); + target_addr = ieee80211_mesh_hwmp_perr_get_addr(perr_elem, 0); + target_sn = ieee80211_mesh_hwmp_perr_get_sn(perr_elem, 0); + target_rcode = ieee80211_mesh_hwmp_perr_get_rcode(perr_elem, 0); rcu_read_lock(); mpath = mesh_path_lookup(sdata, target_addr); @@ -955,9 +931,17 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, return; if (elems->preq) { - if (elems->preq_len != 37) - /* Right now we support just 1 destination and no AE */ + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(elems->preq); + + /* Right now we do not support AE (Address Extension) */ + if (ieee80211_mesh_preq_prep_ae_enabled(elems->preq)) goto free; + + /* Right now we only support 1 target */ + if (preq_elem_bottom->target_count != 1) + goto free; + path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq, MPATH_PREQ); if (path_metric) @@ -965,8 +949,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, path_metric); } if (elems->prep) { - if (elems->prep_len != 31) - /* Right now we support no AE */ + /* Right now we do not support AE (Address Extension) */ + if (ieee80211_mesh_preq_prep_ae_enabled(elems->prep)) goto free; path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep, MPATH_PREP); @@ -975,9 +959,23 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, path_metric); } if (elems->perr) { - if (elems->perr_len != 15) - /* Right now we support only one destination per PERR */ + struct ieee80211_mesh_hwmp_perr *perr_elem = + (struct ieee80211_mesh_hwmp_perr *)elems->perr; + int i; + + /* Right now we support only one destination per PERR */ + if (perr_elem->number_of_dst != 1) goto free; + + /* Right now we do not support AE (Address Extension) */ + for (i = 0; i < perr_elem->number_of_dst; i++) { + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(elems->perr, i); + + if (dst->flags & AE_F) + goto free; + } + hwmp_perr_frame_process(sdata, mgmt, elems->perr); } if (elems->rann) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7cbab90c8784..268857467f29 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -165,8 +165,6 @@ static u64 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) switch (sdata->vif.bss_conf.chanreq.oper.width) { case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: return 0; default: break; @@ -470,6 +468,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, elems->eht_cap, elems->eht_cap_len, &sta->deflink); + ieee80211_sta_init_nss_bw_capa(&sta->deflink, + &sdata->deflink.conf->chanreq.oper); + if (bw != sta->sta.deflink.bandwidth) changed |= IEEE80211_RC_BW_CHANGED; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b98ddfa3003e..9e92337bb6f9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -149,26 +149,65 @@ static int ecw2cw(int ecw) return (1 << ecw) - 1; } +static bool ieee80211_chandef_usable(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + u32 prohibited_flags) +{ + if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, + chandef, prohibited_flags)) + return false; + + if (chandef->punctured && + ieee80211_hw_check(&sdata->local->hw, DISALLOW_PUNCTURING)) + return false; + + return true; +} + +struct ieee80211_determine_ap_chan_data { + /* input data */ + struct ieee80211_channel *channel; + const struct ieee802_11_elems *elems; + const struct ieee80211_conn_settings *conn; + u32 vht_cap_info; + bool ignore_ht_channel_mismatch; + const struct cfg80211_chan_def *cur_chandef; + bool cur_dbe_used; + + /* target chandef is filled in */ + struct cfg80211_chan_def *chandef; +}; + +struct ieee80211_determine_ap_chan_output { + /* filled to indicate UHR DBE was used */ + bool dbe_used; + /* and need to know non-DBE width */ + enum nl80211_chan_width non_dbe_width; +}; + static enum ieee80211_conn_mode ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel *channel, - u32 vht_cap_info, - const struct ieee802_11_elems *elems, - bool ignore_ht_channel_mismatch, - const struct ieee80211_conn_settings *conn, - struct cfg80211_chan_def *chandef) + const struct ieee80211_determine_ap_chan_data *data, + struct ieee80211_determine_ap_chan_output *out) { + bool ignore_ht_channel_mismatch = data->ignore_ht_channel_mismatch; + const struct ieee802_11_elems *elems = data->elems; const struct ieee80211_ht_operation *ht_oper = elems->ht_operation; const struct ieee80211_vht_operation *vht_oper = elems->vht_operation; const struct ieee80211_he_operation *he_oper = elems->he_operation; const struct ieee80211_eht_operation *eht_oper = elems->eht_operation; const struct ieee80211_uhr_operation *uhr_oper = elems->uhr_operation; + const struct ieee80211_conn_settings *conn = data->conn; + struct ieee80211_channel *channel = data->channel; + struct cfg80211_chan_def *chandef = data->chandef; struct ieee80211_supported_band *sband = sdata->local->hw.wiphy->bands[channel->band]; struct cfg80211_chan_def vht_chandef; bool no_vht = false; u32 ht_cfreq; + memset(out, 0, sizeof(*out)); + if (ieee80211_hw_check(&sdata->local->hw, STRICT)) ignore_ht_channel_mismatch = false; @@ -288,7 +327,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, memcpy(&he_oper_vht_cap, he_oper->optional, 3); he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0); - if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, + if (!ieee80211_chandef_vht_oper(&sdata->local->hw, data->vht_cap_info, &he_oper_vht_cap, ht_oper, &vht_chandef)) { sdata_info(sdata, @@ -303,7 +342,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, } else if (sband->band == NL80211_BAND_2GHZ) { no_vht = true; } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, - vht_cap_info, + data->vht_cap_info, vht_oper, ht_oper, &vht_chandef)) { sdata_info(sdata, @@ -362,59 +401,136 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, } check_uhr: - if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper) + if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper || !elems->ml_basic) return IEEE80211_CONN_MODE_EHT; - /* - * In beacons we don't have all the data - but we know the size was OK, - * so if the size is valid as a non-beacon case, we have more data and - * can validate the NPCA parameters. - */ - if (ieee80211_uhr_oper_size_ok((const void *)uhr_oper, - elems->uhr_operation_len, - false)) { + if (elems->frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON)) { struct cfg80211_chan_def npca_chandef = *chandef; + const struct ieee80211_sta_uhr_cap *uhr_cap; const struct ieee80211_uhr_npca_info *npca; - const __le16 *dis_subch_bmap; - u16 punct = chandef->punctured, npca_punct; + const struct ieee80211_uhr_dbe_info *dbe; + struct cfg80211_chan_def dbe_chandef; + + /* frames other than beacons carry UHR capability too */ + if (!elems->uhr_cap) + return IEEE80211_CONN_MODE_EHT; npca = ieee80211_uhr_npca_info(uhr_oper); - if (npca) { - int width = cfg80211_chandef_get_width(chandef); - u8 offs = le32_get_bits(npca->params, - IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS); - u32 cf1 = chandef->center_freq1; - bool pri_upper, npca_upper; - pri_upper = chandef->chan->center_freq > cf1; - npca_upper = 20 * offs >= width / 2; + if (npca && !(elems->uhr_cap->mac.mac_cap[0] & + IEEE80211_UHR_MAC_CAP0_NPCA_SUPP)) { + sdata_info(sdata, + "AP without UHR NPCA capability uses it, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } - if (20 * offs >= cfg80211_chandef_get_width(chandef) || - pri_upper == npca_upper) { + /* DBE is not considered yet, so this works */ + if (!cfg80211_chandef_npca_valid(sdata->local->hw.wiphy, + &npca_chandef, npca) || + cfg80211_chandef_add_npca(sdata->local->hw.wiphy, + &npca_chandef, npca)) { + sdata_info(sdata, + "AP UHR NPCA settings invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif); + /* can't happen since we must have UHR to parse the elems */ + if (WARN_ON(!uhr_cap)) + return IEEE80211_CONN_MODE_EHT; + + if (uhr_cap->mac.mac_cap[0] & IEEE80211_UHR_MAC_CAP0_NPCA_SUPP) + *chandef = npca_chandef; + + dbe = ieee80211_uhr_oper_dbe_info(uhr_oper); + if (dbe) { + const struct ieee80211_uhr_cap_dbe *dbe_cap; + u8 dbe_bw_oper; + u8 dbe_bw_cap; + + dbe_cap = ieee80211_uhr_dbe_cap(elems->uhr_cap); + + if (!dbe_cap) { sdata_info(sdata, - "AP UHR NPCA primary channel invalid, disabling UHR\n"); + "AP without UHR DBE capability uses it, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + dbe_bw_oper = u8_get_bits(dbe->params, + IEEE80211_UHR_DBE_OPER_BANDWIDTH); + + if (le16_get_bits(uhr_oper->params, + IEEE80211_UHR_OPER_PARAMS_DBE_BW) != dbe_bw_oper) { + sdata_info(sdata, + "AP UHR DBE settings mismatch, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + if (ieee80211_uhr_dbe_bw_mhz(dbe_bw_oper) < 0) { + sdata_info(sdata, + "AP UHR DBE bandwidth invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + dbe_bw_cap = u8_get_bits(dbe_cap->cap, + IEEE80211_UHR_MAC_CAP_DBE_MAX_BW); + + switch (dbe_bw_cap) { + case IEEE80211_UHR_DBE_MAX_BW_40: + case IEEE80211_UHR_DBE_MAX_BW_80: + case IEEE80211_UHR_DBE_MAX_BW_160: + case IEEE80211_UHR_DBE_MAX_BW_320: + break; + default: + sdata_info(sdata, + "AP UHR DBE capability invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + /* 1-4 are same in DBE capabilities, map 320-2 to 320 */ + if (dbe_bw_oper == IEEE80211_UHR_DBE_OPER_BW_320_2) + dbe_bw_oper = IEEE80211_UHR_DBE_MAX_BW_320; + if (dbe_bw_oper > dbe_bw_cap) { + sdata_info(sdata, + "AP UHR DBE wider than capability, disabling UHR\n"); return IEEE80211_CONN_MODE_EHT; } } - dis_subch_bmap = ieee80211_uhr_npca_dis_subch_bitmap(uhr_oper); + dbe_chandef = *chandef; - if (dis_subch_bmap) { - npca_punct = get_unaligned_le16(dis_subch_bmap); - npca_chandef.punctured = npca_punct; + if (cfg80211_chandef_add_dbe(&dbe_chandef, dbe)) { + sdata_info(sdata, + "AP UHR DBE settings invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; } - /* - * must be a valid puncturing pattern for this channel as - * well as puncturing all subchannels that are already in - * the disabled subchannel bitmap on the primary channel - */ - if (!cfg80211_chandef_valid(&npca_chandef) || - ((punct & npca_punct) != punct)) { + if (dbe && + /* maybe driver would like to never use DBE */ + uhr_cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP && + ieee80211_chandef_usable(sdata, &dbe_chandef, + IEEE80211_CHAN_DISABLED)) { + out->non_dbe_width = chandef->width; + *chandef = dbe_chandef; + out->dbe_used = true; + } + } else if (data->cur_chandef && data->cur_dbe_used && + cfg80211_chandef_compatible(chandef, data->cur_chandef)) { + u8 dbe_bw = le16_get_bits(uhr_oper->params, + IEEE80211_UHR_OPER_PARAMS_DBE_BW); + int dbe_bw_mhz; + + dbe_bw_mhz = ieee80211_uhr_dbe_bw_mhz(dbe_bw); + if (dbe_bw_mhz < 0) { sdata_info(sdata, - "AP UHR NPCA disabled subchannel bitmap invalid, disabling UHR\n"); + "AP UHR DBE bandwidth invalid, drop UHR\n"); return IEEE80211_CONN_MODE_EHT; } + + if (cfg80211_chandef_get_width(data->cur_chandef) == dbe_bw_mhz) { + *chandef = *data->cur_chandef; + out->dbe_used = true; + } } return IEEE80211_CONN_MODE_UHR; @@ -848,21 +964,6 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, } } -static bool ieee80211_chandef_usable(struct ieee80211_sub_if_data *sdata, - const struct cfg80211_chan_def *chandef, - u32 prohibited_flags) -{ - if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, - chandef, prohibited_flags)) - return false; - - if (chandef->punctured && - ieee80211_hw_check(&sdata->local->hw, DISALLOW_PUNCTURING)) - return false; - - return true; -} - static int ieee80211_chandef_num_subchans(const struct cfg80211_chan_def *c) { if (c->width == NL80211_CHAN_WIDTH_80P80) @@ -1072,7 +1173,8 @@ static void ieee80211_set_chanreq_ap(struct ieee80211_sub_if_data *sdata, VISIBLE_IF_MAC80211_KUNIT struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, - struct cfg80211_bss *cbss, int link_id, + struct cfg80211_bss *cbss, + struct link_sta_info *link_sta, int link_id, struct ieee80211_chan_req *chanreq, struct cfg80211_chan_def *ap_chandef, unsigned long *userspace_selectors) @@ -1080,6 +1182,14 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, const struct cfg80211_bss_ies *ies = rcu_dereference(cbss->ies); struct ieee80211_bss *bss = (void *)cbss->priv; struct ieee80211_channel *channel = cbss->channel; + /* + * This is for parsing a beacon or probe response here, but it's + * using the *BSS* elements which are synthetic for multi-BSSID, + * created by cfg80211 based on multi-BSSID inheritance etc. As + * a result, this sets neither .bss (since multi-BSSID is parsed + * already) nor a valid .link_id (since it doesn't want to see + * the data from another link.) + */ struct ieee80211_elems_parse_params parse_params = { .link_id = -1, .from_ap = true, @@ -1094,6 +1204,14 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, enum ieee80211_conn_mode ap_mode; unsigned long unknown_rates_selectors[BITS_TO_LONGS(128)] = {}; unsigned long sta_selectors[BITS_TO_LONGS(128)] = {}; + struct ieee80211_determine_ap_chan_output ap_chan_out; + struct ieee80211_determine_ap_chan_data ap_chan_data = { + .channel = channel, + .vht_cap_info = bss->vht_cap_info, + .ignore_ht_channel_mismatch = false, + .chandef = ap_chandef, + .conn = conn, + }; int ret; again: @@ -1102,8 +1220,10 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, if (!elems) return ERR_PTR(-ENOMEM); - ap_mode = ieee80211_determine_ap_chan(sdata, channel, bss->vht_cap_info, - elems, false, conn, ap_chandef); + ap_chan_data.elems = elems; + ap_mode = ieee80211_determine_ap_chan(sdata, &ap_chan_data, + &ap_chan_out); + conn->dbe_enabled = ap_chan_out.dbe_used; /* this should be impossible since parsing depends on our mode */ if (WARN_ON(ap_mode > conn->mode)) { @@ -1319,6 +1439,10 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, goto free; } + if (conn->dbe_enabled && link_sta) + link_sta->uhr_usable_tx_width = + ieee80211_chan_width_to_rx_bw(ap_chan_out.non_dbe_width); + return elems; free: kfree(elems); @@ -1326,17 +1450,167 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, } EXPORT_SYMBOL_IF_MAC80211_KUNIT(ieee80211_determine_chan_mode); +static void ieee80211_send_uhr_omp_req_dbe(struct ieee80211_sub_if_data *sdata, + u16 link_mask, bool initial) +{ + struct ieee80211_mle_basic_common_info *common; + struct ieee80211_mle_per_sta_profile *per_sta; + struct ieee80211_uhr_mode_change_tuple *tuple; + struct ieee80211_local *local = sdata->local; + struct ieee80211_multi_link_elem *mle; + struct ieee80211_link_data *link; + struct ieee80211_tx_info *info; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + u8 *ml_elem_len; + size_t size; + + if (initial) { + bool enabled = false; + + for_each_link_data(sdata, link) { + if (!(link_mask & BIT(link->link_id))) + continue; + if (link->u.mgd.conn.dbe_enabled) { + enabled = true; + break; + } + } + + if (!enabled) + return; + } + + if (sdata->u.mgd.uhr_omp.links) { + if (initial) + sdata->u.mgd.uhr_omp.pending_init |= link_mask; + else + sdata->u.mgd.uhr_omp.pending |= link_mask; + return; + } + + size = local->hw.extra_tx_headroom + + IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req) + + 3 + sizeof(*mle) + sizeof(*common) + + IEEE80211_MLD_MAX_NUM_LINKS * + (2 + sizeof(*per_sta) + + 3 + sizeof(*tuple) /* single tuple for each link */); + + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req)); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_UHR; + mgmt->u.action.action_code = + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST; + + sdata->u.mgd.dialog_token_alloc++; + /* + * NOTE: + * Driver and FW might both send these frames, and iwlwifi + * decided that the driver uses odd numbers, FW uses even + * numbers. For now, hardcode that here, until it matters + * to some other driver. + * + * Note also that there's currently no real synchronisation + * in this case, but it's not valid that both send such a + * frame at the same time, i.e. while waiting for a response + * there can't be another frame sent. This needs addressing + * in the future. + */ + if (sdata->u.mgd.dialog_token_alloc % 2 == 0) + sdata->u.mgd.dialog_token_alloc++; + + sdata->u.mgd.uhr_omp.dialog_token = sdata->u.mgd.dialog_token_alloc; + mgmt->u.action.uhr_link_reconf_req.dialog_token = + sdata->u.mgd.uhr_omp.dialog_token; + mgmt->u.action.uhr_link_reconf_req.type = + IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST; + + skb_put_u8(skb, WLAN_EID_EXTENSION); + ml_elem_len = skb_put(skb, 1); + skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK); + mle = skb_put_zero(skb, sizeof(*mle)); + mle->control = cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_RECONF | + IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR); + + common = skb_put(skb, sizeof(*common)); + common->len = sizeof(*common); + memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN); + + for_each_link_data(sdata, link) { + u8 *subelem_len; + + if (!(link_mask & BIT(link->link_id))) + continue; + + sdata->u.mgd.uhr_omp.links |= BIT(link->link_id); + + skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); + subelem_len = skb_put(skb, 1); + per_sta = skb_put_zero(skb, sizeof(*per_sta)); + per_sta->control = + le16_encode_bits(link->link_id, + IEEE80211_MLE_STA_CONTROL_LINK_ID) | + le16_encode_bits(IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD, + IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE); + per_sta->sta_info_len = 1; /* includes itself */ + + skb_put_u8(skb, WLAN_EID_EXTENSION); + skb_put_u8(skb, 1 + sizeof(*tuple)); + skb_put_u8(skb, WLAN_EID_EXT_UHR_MODE_CHG); + tuple = skb_put_zero(skb, sizeof(*tuple)); + tuple->control = + le16_encode_bits(IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID); + + if (link->u.mgd.conn.dbe_enabled) + tuple->control |= + cpu_to_le16(IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE); + + ieee80211_fragment_element(skb, subelem_len, + IEEE80211_MLE_SUBELEM_FRAGMENT); + } + + ieee80211_fragment_element(skb, ml_elem_len, WLAN_EID_FRAGMENT); + + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + info->status_data = IEEE80211_STATUS_TYPE_UHR_OMP; + ieee80211_tx_skb(sdata, skb); +} + static int ieee80211_config_bw(struct ieee80211_link_data *link, struct ieee802_11_elems *elems, bool update, u64 *changed, u16 stype) { struct ieee80211_channel *channel = link->conf->chanreq.oper.chan; - struct ieee80211_sub_if_data *sdata = link->sdata; - struct ieee80211_chan_req chanreq = {}; struct cfg80211_chan_def ap_chandef; + struct ieee80211_determine_ap_chan_output ap_chan_out; + struct ieee80211_determine_ap_chan_data ap_chan_data = { + .channel = channel, + .vht_cap_info = 0, + .ignore_ht_channel_mismatch = true, + .chandef = &ap_chandef, + .elems = elems, + .conn = &link->u.mgd.conn, + .cur_chandef = &link->conf->chanreq.oper, + .cur_dbe_used = link->u.mgd.conn.dbe_enabled, + }; + struct ieee80211_sub_if_data *sdata = link->sdata; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chan_req chanreq = {}; enum ieee80211_conn_mode ap_mode; const char *frame; - u32 vht_cap_info = 0; u16 ht_opmode; int ret; @@ -1364,11 +1638,12 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, return 0; if (elems->vht_cap_elem) - vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info); + ap_chan_data.vht_cap_info = + le32_to_cpu(elems->vht_cap_elem->vht_cap_info); - ap_mode = ieee80211_determine_ap_chan(sdata, channel, vht_cap_info, - elems, true, &link->u.mgd.conn, - &ap_chandef); + ap_mode = ieee80211_determine_ap_chan(sdata, &ap_chan_data, + &ap_chan_out); + link->u.mgd.conn.dbe_enabled = ap_chan_out.dbe_used; if (ap_mode != link->u.mgd.conn.mode) { link_info(link, @@ -1418,8 +1693,55 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, } } - if (ieee80211_chanreq_identical(&chanreq, &link->conf->chanreq)) + /* + * Beacons don't have the full information - we need to track + * critical updates for NPCA parameters etc. For now only handle + * association and link reconfiguration response. + */ + if (stype != IEEE80211_STYPE_BEACON && + chanreq.oper.npca_chan && elems->uhr_operation && + ieee80211_uhr_oper_size_ok((const void *)elems->uhr_operation, + elems->uhr_operation_len, + false)) { + const struct ieee80211_uhr_npca_info *npca; + struct ieee80211_bss_npca_params params = {}; + + npca = ieee80211_uhr_npca_info(elems->uhr_operation); + if (!npca) { + chanreq.oper.npca_chan = NULL; + chanreq.oper.npca_punctured = 0; + } else { + params.min_dur_thresh = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH); + params.switch_delay = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY); + params.switch_back_delay = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY); + params.init_qsrc = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC); + params.moplen = + le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_MOPLEN); + /* don't change the enabled bit yet */ + params.enabled = link->conf->npca.enabled; + } + + if (memcmp(¶ms, &link->conf->npca, sizeof(params)) || + !update) { + link->conf->npca = params; + *changed |= BSS_CHANGED_NPCA; + } + } + + if (ieee80211_chanreq_identical(&chanreq, &link->conf->chanreq)) { + if (update) + goto update_npca; return 0; + } link_info(link, "AP %pM changed bandwidth in %s, new used config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n", @@ -1466,6 +1788,24 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, } cfg80211_schedule_channels_check(&sdata->wdev); + +update_npca: + chanctx_conf = sdata_dereference(link->conf->chanctx_conf, sdata); + /* must be non-NULL when update is true */ + if (WARN_ON(!chanctx_conf)) + return -EINVAL; + + /* + * If we're not associated yet (i.e. in the process associating) + * then the chanctx code won't have enabled NPCA in the link, so + * if the channel context was set up with NPCA for us, enable it. + */ + if (chanreq.oper.npca_chan && chanctx_conf->def.npca_chan && + !link->conf->npca.enabled && !sdata->vif.cfg.assoc) { + link->conf->npca.enabled = true; + *changed |= BSS_CHANGED_NPCA; + } + return 0; } @@ -2234,8 +2574,7 @@ ieee80211_link_common_elems_size(struct ieee80211_sub_if_data *sdata, sizeof(struct ieee80211_eht_mcs_nss_supp) + IEEE80211_EHT_PPE_THRES_MAX_LEN; - size += 2 + 1 + sizeof(struct ieee80211_uhr_cap) + - sizeof(struct ieee80211_uhr_cap_phy); + size += 2 + 1 + sizeof(struct ieee80211_uhr_cap); return size; } @@ -2569,6 +2908,7 @@ static void ieee80211_csa_switch_work(struct wiphy *wiphy, cfg80211_ch_switch_notify(sdata->dev, &link->csa.chanreq.oper, link->link_id); link->conf->csa_active = false; + link->u.mgd.conn.dbe_enabled = false; ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); if (WARN_ON(!ap_sta)) @@ -2579,9 +2919,16 @@ static void ieee80211_csa_switch_work(struct wiphy *wiphy, if (WARN_ON(!link_sta)) return; + /* + * If the link was somehow deactivated in the middle of enabling + * DBE while waiting for a response, this could be stuck, reset. + */ + link_sta->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + link_sta->pub->bandwidth = - _ieee80211_sta_cur_vht_bw(link_sta, - &link->csa.chanreq.oper); + ieee80211_sta_current_bw(link_sta, + &link->csa.chanreq.oper, + IEEE80211_STA_BW_TX_TO_STA); return; } @@ -2670,6 +3017,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct link_sta_info *link_sta; + struct sta_info *ap_sta; int ret; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -2679,6 +3028,7 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) ieee80211_vif_unblock_queues_csa(sdata); link->conf->csa_active = false; + link->u.mgd.conn.dbe_enabled = false; link->u.mgd.csa.blocked_tx = false; link->u.mgd.csa.waiting_bcn = false; @@ -2691,6 +3041,20 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) return; } + ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); + if (WARN_ON(!ap_sta)) + return; + + link_sta = sdata_dereference(ap_sta->link[link->link_id], sdata); + if (WARN_ON(!link_sta)) + return; + + /* + * If DBE was being activated and CSA happened, this could be + * on a wrong value. Reset it. + */ + link_sta->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + cfg80211_ch_switch_notify(sdata->dev, &link->conf->chanreq.oper, link->link_id); } @@ -4395,6 +4759,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->userspace_selectors, 0, sizeof(ifmgd->userspace_selectors)); + + ifmgd->uhr_omp.pending = 0; + ifmgd->uhr_omp.pending_init = 0; } static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) @@ -4715,6 +5082,101 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) ifmgd->reconnect = false; } +static void ieee80211_uhr_omp_req_status(struct ieee80211_sub_if_data *sdata) +{ + bool acked = sdata->u.mgd.uhr_omp.acked; + u16 links = sdata->u.mgd.uhr_omp.links; + struct ieee80211_link_data *link; + struct sta_info *ap; + + /* timer and queued RX could overlap */ + if (!links) + return; + + sdata->u.mgd.uhr_omp.links = 0; + sdata->u.mgd.uhr_omp.acked = false; + + if (!acked) { + sdata_dbg(sdata, "UHR OMP frame not ACKed - disconnect\n"); + __ieee80211_disconnect(sdata); + return; + } + + ap = sta_info_get(sdata, sdata->vif.cfg.ap_addr); + if (!ap) + return; + + for_each_link_data(sdata, link) { + struct link_sta_info *link_sta; + + if (!(links & BIT(link->link_id))) + continue; + + /* only handle transition to enabled for now */ + if (!link->u.mgd.conn.dbe_enabled) + continue; + + link_sta = sdata_dereference(ap->link[link->link_id], sdata); + if (WARN_ON(!link_sta)) + continue; + + link_sta->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + ieee80211_link_sta_update_rc_bw(link, link_sta); + } + + /* next round - send pending frames if needed */ + + if (sdata->u.mgd.uhr_omp.pending_init) { + links = sdata->u.mgd.uhr_omp.pending_init; + + sdata->u.mgd.uhr_omp.pending_init = 0; + ieee80211_send_uhr_omp_req_dbe(sdata, links, true); + return; + } + + if (sdata->u.mgd.uhr_omp.pending) { + links = sdata->u.mgd.uhr_omp.pending; + + sdata->u.mgd.uhr_omp.pending = 0; + ieee80211_send_uhr_omp_req_dbe(sdata, links, false); + return; + } +} + +static void ieee80211_uhr_omp_req_status_wk(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.uhr_omp.status_work.work); + + ieee80211_uhr_omp_req_status(sdata); +} + +static void ieee80211_process_uhr_omp_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt) +{ + if (mgmt->u.action.uhr_link_reconf_notif.dialog_token != + sdata->u.mgd.uhr_omp.dialog_token) + return; + + wiphy_hrtimer_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.uhr_omp.status_work); + ieee80211_uhr_omp_req_status(sdata); +} + +static void +ieee80211_process_uhr_link_reconf_notif(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + switch (mgmt->u.action.uhr_link_reconf_notif.type) { + case IEEE80211_UHR_LINK_RECONFIG_NOTIFY_OMP_RESPONSE: + ieee80211_process_uhr_omp_resp(sdata, mgmt); + break; + } +} + static void ieee80211_beacon_connection_loss_work(struct wiphy *wiphy, struct wiphy_work *work) { @@ -5693,12 +6155,27 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, if (elems->uhr_operation && elems->uhr_cap && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) { + int omp_to_us; + ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband, elems->uhr_cap, elems->uhr_cap_len, link_sta); bss_conf->uhr_support = link_sta->pub->uhr_cap.has_uhr; + + /* + * This assumes that the timeout is the same across all links, + * maybe we should actually validate that. + */ + omp_to_us = ieee80211_uhr_capa_get_om_pu_to_us(elems->uhr_cap); + if (omp_to_us < 0) { + ret = false; + link_info(link, "Invalid UHR OMP timeout\n"); + goto out; + } + + sdata->u.mgd.uhr_omp.timeout_us = omp_to_us; } else { bss_conf->uhr_support = false; } @@ -5757,20 +6234,13 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, * next beacon and update then. */ - /* - * If an operating mode notification IE is present, override the - * NSS calculation (that would be done in rate_control_rate_init()) - * and use the # of streams from that element. - */ - if (elems->opmode_notif && - !(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) { - u8 nss; + ieee80211_sta_init_nss_bw_capa(link_sta, &bss_conf->chanreq.oper); - nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; - nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; - nss += 1; - link_sta->pub->rx_nss = nss; - } + /* If an operating mode notification element is present, use it. */ + if (elems->opmode_notif) + __ieee80211_vht_handle_opmode(sdata, link_sta, + *elems->opmode_notif, + sband->band); /* * Always handle WMM once after association regardless @@ -6235,6 +6705,7 @@ ieee80211_determine_our_sta_mode_assoc(struct ieee80211_sub_if_data *sdata, static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, + struct link_sta_info *link_sta, int link_id, struct cfg80211_bss *cbss, bool mlo, struct ieee80211_conn_settings *conn, @@ -6250,7 +6721,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); - elems = ieee80211_determine_chan_mode(sdata, conn, cbss, link_id, + elems = ieee80211_determine_chan_mode(sdata, conn, cbss, + link_sta, link_id, &chanreq, &ap_chandef, userspace_selectors); @@ -6307,10 +6779,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ret = ieee80211_link_use_channel(link, &chanreq, IEEE80211_CHANCTX_SHARED); - /* don't downgrade for 5/10/S1G MHz channels, though. */ - if (chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - chanreq.oper.width == NL80211_CHAN_WIDTH_10 || - cfg80211_chandef_is_s1g(&chanreq.oper)) + /* don't downgrade for S1G channels, though. */ + if (cfg80211_chandef_is_s1g(&chanreq.oper)) return ret; while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) { @@ -6477,6 +6947,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sta->sta.spp_amsdu = assoc_data->spp_amsdu; if (ieee80211_vif_is_mld(&sdata->vif)) { + if (!elems->ml_basic) + goto out_err; + + sta->sta.ext_mld_capa_ops = + ieee80211_mle_get_ext_mld_capa_op((const void *)elems->ml_basic); + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { if (!assoc_data->link[link_id].bss) continue; @@ -6566,7 +7042,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (link_id != assoc_data->assoc_link_id) { link->u.mgd.conn = assoc_data->link[link_id].conn; - err = ieee80211_prep_channel(sdata, link, link_id, cbss, + err = ieee80211_prep_channel(sdata, link, link_sta, + link_id, cbss, true, &link->u.mgd.conn, sdata->u.mgd.userspace_selectors); if (err) { @@ -6647,6 +7124,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); + ieee80211_send_uhr_omp_req_dbe(sdata, ~0, true); + return true; out_err: eth_zero_addr(sdata->vif.cfg.ap_addr); @@ -6667,6 +7146,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, .type = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_TYPE, }; struct ieee802_11_elems *elems; + struct sta_info *sta; int ac; const u8 *elem_start; unsigned int elem_len; @@ -6862,6 +7342,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, resp.ap_mld_addr = ap_mld_addr; } + /* + * If epp_peer set, unprotected (Re)Association Request/Response frames + * are dropped, which ensures that the (re)association exchange is + * encrypted over the air. + */ + sta = sta_info_get_bss(sdata, sdata->vif.cfg.ap_addr); + resp.assoc_encrypted = sta && sta->sta.epp_peer; + ieee80211_destroy_assoc_data(sdata, status_code == WLAN_STATUS_SUCCESS ? ASSOC_SUCCESS : @@ -7087,7 +7575,6 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, container_of(work, struct ieee80211_sub_if_data, u.mgd.ml_reconf_work.work); u16 new_valid_links, new_active_links, new_dormant_links; - struct sta_info *sta; int ret; if (!sdata->u.mgd.removed_links) @@ -7123,16 +7610,6 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, } } - sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); - if (sta) { - unsigned long removed_links = sdata->u.mgd.removed_links; - unsigned int link_id; - - for_each_set_bit(link_id, &removed_links, - IEEE80211_MLD_MAX_NUM_LINKS) - ieee80211_sta_remove_link(sta, link_id); - } - new_dormant_links = sdata->vif.dormant_links & ~sdata->u.mgd.removed_links; ret = ieee80211_vif_set_links(sdata, new_valid_links, @@ -7595,8 +8072,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, struct link_sta_info *link_sta; struct sta_info *sta; u64 changed = 0; - bool erp_valid; - u8 erp_value = 0; u32 ncrc = 0; u8 *bssid, *variable = mgmt->u.beacon.variable; u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -7717,6 +8192,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, if (!elems) return; + /* + * Note: with MBSSID and an EMA (or broken) AP, we could fail to find + * the correct multi-BSSID profile for the non-transmitting AP we're + * connected to. The result's elems->mbssid_nontx_profile_missing is + * indicating that, but some things must happen regardless. + */ + if (rx_status->flag & RX_FLAG_DECRYPTED && ieee80211_mgd_ssid_mismatch(sdata, elems)) { sdata_info(sdata, "SSID mismatch for AP %pM, disconnect\n", @@ -7752,6 +8234,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, } } + /* + * P2P will almost certainly not have MBSSID, but this just + * assumes that it would at least always inherit NoA anyway + * since it's absent from the channel. + */ if (sdata->vif.p2p || sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) { struct ieee80211_p2p_noa_attr noa = {}; @@ -7809,23 +8296,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_rx_bss_info(link, mgmt, len, rx_status); + /* + * This assumes that all members of a multiple BSS set must be + * switching together, so we can parse channel switch elements + * from the transmitted BSS even if our non-transmitted one is + * not present in this beacon (due to EMA.) + */ ieee80211_sta_process_chanswitch(link, rx_status->mactime, rx_status->device_timestamp, elems, elems, IEEE80211_CSA_SOURCE_BEACON); - /* note that after this elems->ml_basic can no longer be used fully */ - ieee80211_mgd_check_cross_link_csa(sdata, rx_status->link_id, elems); - - ieee80211_mgd_update_bss_param_ch_cnt(sdata, bss_conf, elems); - - if (!sdata->u.mgd.epcs.enabled && - !link->u.mgd.disable_wmm_tracking && - ieee80211_sta_wmm_params(local, link, elems->wmm_param, - elems->wmm_param_len, - elems->mu_edca_param_set)) - changed |= BSS_CHANGED_QOS; - /* * If we haven't had a beacon before, tell the driver about the * DTIM period (and beacon timing if desired) now. @@ -7842,17 +8323,53 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_recalc_ps_vif(sdata); } - if (elems->erp_info) { - erp_valid = true; - erp_value = elems->erp_info[0]; - } else { - erp_valid = false; - } + /* RNR isn't inside an MBSSID profile */ + ieee80211_mgd_update_bss_param_ch_cnt(sdata, bss_conf, elems); + + /* assume ERP would be inherited anyway */ + if (!ieee80211_is_s1g_beacon(hdr->frame_control)) { + u8 erp_value = 0; + bool erp_valid; + + if (elems->erp_info) { + erp_valid = true; + erp_value = elems->erp_info[0]; + } else { + erp_valid = false; + } - if (!ieee80211_is_s1g_beacon(hdr->frame_control)) changed |= ieee80211_handle_bss_capability(link, le16_to_cpu(mgmt->u.beacon.capab_info), erp_valid, erp_value); + } + + /* + * There are some other things that we can only do when the + * real non-transmitted profile was actually parsed, so exit + * here before doing those. + */ + if (elems->mbssid_nontx_profile_missing) + goto apply; + + /* + * This requires multi-link element, which is from the MBSSID profile. + * Note that after this elems->ml_basic can no longer be used fully. + * + * Note also that currently the parsing is incorrect, so this will + * never actually do anything. + */ + ieee80211_mgd_check_cross_link_csa(sdata, rx_status->link_id, elems); + + /* + * EDCA parameters should be the same, but perhaps ACM can differ + * between BSSes in an MBSSID set. + */ + if (!sdata->u.mgd.epcs.enabled && + !link->u.mgd.disable_wmm_tracking && + ieee80211_sta_wmm_params(local, link, elems->wmm_param, + elems->wmm_param_len, + elems->mu_edca_param_set)) + changed |= BSS_CHANGED_QOS; sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); if (WARN_ON(!sta)) { @@ -7898,6 +8415,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_process_adv_ttlm(sdata, elems, le64_to_cpu(mgmt->u.beacon.timestamp)); +apply: ieee80211_link_info_change_notify(sdata, link, changed); free: kfree(elems); @@ -8105,7 +8623,7 @@ ieee80211_send_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb(sdata, skb); } -static int +VISIBLE_IF_MAC80211_KUNIT int ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata, const struct ieee80211_ttlm_elem *ttlm, struct ieee80211_neg_ttlm *neg_ttlm, @@ -8186,9 +8704,11 @@ ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata, } return 0; } +EXPORT_SYMBOL_IF_MAC80211_KUNIT(ieee80211_parse_neg_ttlm); -void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) { u8 dialog_token, direction[IEEE80211_TTLM_MAX_CNT] = {}, i; size_t ies_len; @@ -8250,8 +8770,9 @@ void ieee80211_process_neg_ttlm_req(struct ieee80211_sub_if_data *sdata, ieee80211_send_neg_ttlm_res(sdata, ttlm_res, dialog_token, &neg_ttlm); } -void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) { if (!ieee80211_vif_is_mld(&sdata->vif) || mgmt->u.action.ttlm_res.dialog_token != sdata->u.mgd.dialog_token_alloc) @@ -8272,7 +8793,7 @@ void ieee80211_process_neg_ttlm_res(struct ieee80211_sub_if_data *sdata, __ieee80211_disconnect(sdata); } -void ieee80211_process_ttlm_teardown(struct ieee80211_sub_if_data *sdata) +static void ieee80211_process_ttlm_teardown(struct ieee80211_sub_if_data *sdata) { u16 new_dormant_links; @@ -8330,8 +8851,8 @@ void ieee80211_send_teardown_neg_ttlm(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_send_teardown_neg_ttlm); -void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) +static void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_link_data *link = &sdata->deflink; struct ieee80211_rx_status *rx_status; @@ -8351,129 +8872,6 @@ void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, } } -void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) -{ - struct ieee80211_link_data *link = &sdata->deflink; - struct ieee80211_rx_status *rx_status; - struct ieee802_11_elems *elems; - struct ieee80211_mgmt *mgmt; - u16 fc; - int ies_len; - - lockdep_assert_wiphy(sdata->local->hw.wiphy); - - rx_status = (struct ieee80211_rx_status *) skb->cb; - mgmt = (struct ieee80211_mgmt *) skb->data; - fc = le16_to_cpu(mgmt->frame_control); - - if (rx_status->link_valid) { - link = sdata_dereference(sdata->link[rx_status->link_id], - sdata); - if (!link) - return; - } - - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(link, (void *)mgmt, - skb->len, rx_status); - break; - case IEEE80211_STYPE_PROBE_RESP: - ieee80211_rx_mgmt_probe_resp(link, skb); - break; - case IEEE80211_STYPE_AUTH: - ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_DEAUTH: - ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_DISASSOC: - ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_ASSOC_RESP: - case IEEE80211_STYPE_REASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_ACTION: - if (!sdata->u.mgd.associated || - !ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) - break; - - switch (mgmt->u.action.category) { - case WLAN_CATEGORY_SPECTRUM_MGMT: - ies_len = skb->len - - offsetof(struct ieee80211_mgmt, - u.action.chan_switch.variable); - - if (ies_len < 0) - break; - - /* CSA IE cannot be overridden, no need for BSSID */ - elems = ieee802_11_parse_elems(mgmt->u.action.chan_switch.variable, - ies_len, - IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION, - NULL); - - if (elems && !elems->parse_error) { - enum ieee80211_csa_source src = - IEEE80211_CSA_SOURCE_PROT_ACTION; - - ieee80211_sta_process_chanswitch(link, - rx_status->mactime, - rx_status->device_timestamp, - elems, elems, - src); - } - kfree(elems); - break; - case WLAN_CATEGORY_PUBLIC: - case WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION: - ies_len = skb->len - - offsetof(struct ieee80211_mgmt, - u.action.ext_chan_switch.variable); - - if (ies_len < 0) - break; - - /* - * extended CSA IE can't be overridden, no need for - * BSSID - */ - elems = ieee802_11_parse_elems(mgmt->u.action.ext_chan_switch.variable, - ies_len, - IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION, - NULL); - - if (elems && !elems->parse_error) { - enum ieee80211_csa_source src; - - if (mgmt->u.action.category == - WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION) - src = IEEE80211_CSA_SOURCE_PROT_ACTION; - else - src = IEEE80211_CSA_SOURCE_UNPROT_ACTION; - - /* for the handling code pretend it was an IE */ - elems->ext_chansw_ie = - &mgmt->u.action.ext_chan_switch.data; - - ieee80211_sta_process_chanswitch(link, - rx_status->mactime, - rx_status->device_timestamp, - elems, elems, - src); - } - - kfree(elems); - break; - } - break; - } -} - static void ieee80211_sta_timer(struct timer_list *t) { struct ieee80211_sub_if_data *sdata = @@ -9067,6 +9465,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ieee80211_neg_ttlm_timeout_work); wiphy_work_init(&ifmgd->teardown_ttlm_work, ieee80211_teardown_ttlm_work); + wiphy_hrtimer_work_init(&ifmgd->uhr_omp.status_work, + ieee80211_uhr_omp_req_status_wk); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; @@ -9281,6 +9681,9 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, } if (new_sta || override) { + struct link_sta_info *link_sta; + struct sta_info *ap; + /* * Only set this if we're also going to calculate the AP * settings etc., otherwise this was set before in a @@ -9288,7 +9691,18 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, * if the settings were changed. */ link->u.mgd.conn = *conn; - err = ieee80211_prep_channel(sdata, link, link->link_id, cbss, + + ap = new_sta ?: have_sta; + link_sta = sdata_dereference(ap->link[link->link_id], sdata); + if (!link_sta) { + err = -EINVAL; + if (new_sta) + sta_info_free(local, new_sta); + goto out_err; + } + + err = ieee80211_prep_channel(sdata, link, link_sta, + link->link_id, cbss, mlo, &link->u.mgd.conn, userspace_selectors); if (err) { @@ -9857,7 +10271,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgd_assoc_data *assoc_data; const struct element *ssid_elem; struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; + const struct wiphy_iftype_ext_capab *ift_ext_capa; struct ieee80211_link_data *link; + u16 driver_ext_mld_capa_ops = 0; struct cfg80211_bss *cbss; bool override, uapsd_supported; bool match_auth; @@ -9896,17 +10312,26 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, else memcpy(assoc_data->ap_addr, cbss->bssid, ETH_ALEN); + ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, + ieee80211_vif_type_p2p(&sdata->vif)); + if (ift_ext_capa) + driver_ext_mld_capa_ops = ift_ext_capa->ext_mld_capa_and_ops; + /* * Many APs have broken parsing of the extended MLD capa/ops field, * dropping (re-)association request frames or replying with association * response with a failure status if it's present. * Set our value from the userspace request only in strict mode or if * the AP also had that field present. + * For UHR we may want to advertise ML-PM (per driver_ext_mld_capa_ops) + * but if the AP doesn't have it then it's pointless, and if it does + * then it has to have the extended MLD capa/ops field. */ if (ieee80211_hw_check(&local->hw, STRICT) || ieee80211_mgd_assoc_bss_has_mld_ext_capa_ops(req)) assoc_data->ext_mld_capa_ops = - cpu_to_le16(req->ext_mld_capa_ops); + cpu_to_le16(req->ext_mld_capa_ops | + driver_ext_mld_capa_ops); if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -10172,8 +10597,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, continue; if (i == assoc_data->assoc_link_id) continue; - /* only calculate the mode, hence link == NULL */ - err = ieee80211_prep_channel(sdata, NULL, i, + /* only calculate the mode, hence link/link_sta == NULL */ + err = ieee80211_prep_channel(sdata, NULL, NULL, i, assoc_data->link[i].bss, true, &assoc_data->link[i].conn, sdata->u.mgd.userspace_selectors); @@ -10356,6 +10781,8 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) &ifmgd->csa_connection_drop_work); wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->tdls_peer_del_work); + wiphy_hrtimer_work_cancel(sdata->local->hw.wiphy, + &ifmgd->uhr_omp.status_work); if (ifmgd->assoc_data) ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); @@ -10437,8 +10864,9 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); -void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void +ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -10601,7 +11029,7 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; link->u.mgd.conn = add_links_data->link[link_id].conn; - if (ieee80211_prep_channel(sdata, link, link_id, cbss, + if (ieee80211_prep_channel(sdata, link, link_sta, link_id, cbss, true, &link->u.mgd.conn, sdata->u.mgd.userspace_selectors)) { link_info(link, "mlo: reconf: prep_channel failed\n"); @@ -10625,7 +11053,6 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, if (add_links_data->link[link_id].status != WLAN_STATUS_SUCCESS) goto disconnect; - ieee80211_sta_init_nss(link_sta); if (ieee80211_sta_activate_link(sta, link_id)) goto disconnect; @@ -10647,6 +11074,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_ps(local); ieee80211_recalc_ps_vif(sdata); + ieee80211_send_uhr_omp_req_dbe(sdata, link_mask, true); + done_data.buf = (const u8 *)mgmt; done_data.len = orig_len; done_data.added_links = link_mask; @@ -10901,11 +11330,13 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, struct cfg80211_ml_reconf_req *req) { + const struct wiphy_iftype_ext_capab *ift_ext_capa; struct ieee80211_local *local = sdata->local; struct ieee80211_mgd_assoc_data *data = NULL; struct sta_info *sta; struct sk_buff *skb; u16 added_links, new_valid_links; + u16 driver_ext_mld_capa_ops = 0; int link_id, err; if (!ieee80211_vif_is_mld(&sdata->vif) || @@ -11015,7 +11446,7 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, continue; /* only used to verify the mode, nothing is allocated */ - err = ieee80211_prep_channel(sdata, NULL, link_id, + err = ieee80211_prep_channel(sdata, NULL, NULL, link_id, data->link[link_id].bss, true, &data->link[link_id].conn, @@ -11063,6 +11494,11 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, } } + ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, + ieee80211_vif_type_p2p(&sdata->vif)); + if (ift_ext_capa) + driver_ext_mld_capa_ops = ift_ext_capa->ext_mld_capa_and_ops; + /* Build the SKB before the link removal as the construction of the * station info for removed links requires the local address. * Invalidate the removed links, so that the transmission of the ML @@ -11071,7 +11507,8 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, * on which the request was received. */ skb = ieee80211_build_ml_reconf_req(sdata, data, req->rem_links, - cpu_to_le16(req->ext_mld_capa_ops)); + cpu_to_le16(req->ext_mld_capa_ops | + driver_ext_mld_capa_ops)); if (!skb) { err = -ENOMEM; goto err_free; @@ -11090,14 +11527,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, goto err_free; } - for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; - link_id++) { - if (!(req->rem_links & BIT(link_id))) - continue; - - ieee80211_sta_remove_link(sta, link_id); - } - /* notify the driver and upper layers */ ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_VALID_LINKS); @@ -11265,8 +11694,9 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, } } -void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) { struct ieee802_11_elems *elems __free(kfree) = NULL; size_t ies_len; @@ -11313,8 +11743,9 @@ void ieee80211_process_epcs_ena_resp(struct ieee80211_sub_if_data *sdata, ieee80211_epcs_changed(sdata, true); } -void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) { if (!ieee80211_vif_is_mld(&sdata->vif) || !sdata->u.mgd.epcs.enabled) @@ -11323,3 +11754,170 @@ void ieee80211_process_epcs_teardown(struct ieee80211_sub_if_data *sdata, ieee80211_epcs_teardown(sdata); ieee80211_epcs_changed(sdata, false); } + +void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_link_data *link = &sdata->deflink; + struct ieee80211_rx_status *rx_status; + struct ieee802_11_elems *elems; + struct ieee80211_mgmt *mgmt; + u16 fc; + int ies_len; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + mgmt = (struct ieee80211_mgmt *) skb->data; + + if (ieee80211_is_ext(mgmt->frame_control)) { + ieee80211_sta_rx_queued_ext(sdata, skb); + return; + } + + rx_status = (struct ieee80211_rx_status *) skb->cb; + fc = le16_to_cpu(mgmt->frame_control); + + if (rx_status->link_valid) { + link = sdata_dereference(sdata->link[rx_status->link_id], + sdata); + if (!link) + return; + } + + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(link, (void *)mgmt, + skb->len, rx_status); + break; + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(link, skb); + break; + case IEEE80211_STYPE_AUTH: + ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_DEAUTH: + ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_DISASSOC: + ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_ACTION: + if (!sdata->u.mgd.associated || + !ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) + break; + + switch (mgmt->u.action.category) { + case WLAN_CATEGORY_SPECTRUM_MGMT: + ies_len = skb->len - + offsetof(struct ieee80211_mgmt, + u.action.chan_switch.variable); + + if (ies_len < 0) + break; + + /* CSA IE cannot be overridden, no need for BSSID */ + elems = ieee802_11_parse_elems(mgmt->u.action.chan_switch.variable, + ies_len, + IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION, + NULL); + + if (elems && !elems->parse_error) { + enum ieee80211_csa_source src = + IEEE80211_CSA_SOURCE_PROT_ACTION; + + ieee80211_sta_process_chanswitch(link, + rx_status->mactime, + rx_status->device_timestamp, + elems, elems, + src); + } + kfree(elems); + break; + case WLAN_CATEGORY_PUBLIC: + case WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION: + ies_len = skb->len - + offsetof(struct ieee80211_mgmt, + u.action.ext_chan_switch.variable); + + if (ies_len < 0) + break; + + /* + * extended CSA IE can't be overridden, no need for + * BSSID + */ + elems = ieee802_11_parse_elems(mgmt->u.action.ext_chan_switch.variable, + ies_len, + IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION, + NULL); + + if (elems && !elems->parse_error) { + enum ieee80211_csa_source src; + + if (mgmt->u.action.category == + WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION) + src = IEEE80211_CSA_SOURCE_PROT_ACTION; + else + src = IEEE80211_CSA_SOURCE_UNPROT_ACTION; + + /* for the handling code pretend it was an IE */ + elems->ext_chansw_ie = + &mgmt->u.action.ext_chan_switch.data; + + ieee80211_sta_process_chanswitch(link, + rx_status->mactime, + rx_status->device_timestamp, + elems, elems, + src); + } + + kfree(elems); + break; + case WLAN_CATEGORY_PROTECTED_EHT: + switch (mgmt->u.action.action_code) { + case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ: + ieee80211_process_neg_ttlm_req(sdata, mgmt, + skb->len); + break; + case WLAN_PROTECTED_EHT_ACTION_TTLM_RES: + ieee80211_process_neg_ttlm_res(sdata, mgmt, + skb->len); + break; + case WLAN_PROTECTED_EHT_ACTION_TTLM_TEARDOWN: + ieee80211_process_ttlm_teardown(sdata); + break; + case WLAN_PROTECTED_EHT_ACTION_LINK_RECONFIG_RESP: + ieee80211_process_ml_reconf_resp(sdata, mgmt, + skb->len); + break; + case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_RESP: + ieee80211_process_epcs_ena_resp(sdata, mgmt, + skb->len); + break; + case WLAN_PROTECTED_EHT_ACTION_EPCS_ENABLE_TEARDOWN: + ieee80211_process_epcs_teardown(sdata, mgmt, + skb->len); + break; + default: + break; + } + break; + case WLAN_CATEGORY_PROTECTED_UHR: + switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY: + ieee80211_process_uhr_link_reconf_notif(sdata, + mgmt, + skb->len); + break; + } + break; + } + break; + } +} diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c index 4e262b624521..1800bb96dd29 100644 --- a/net/mac80211/nan.c +++ b/net/mac80211/nan.c @@ -334,7 +334,10 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, sched_idx_to_chan[i] = chan; ieee80211_nan_init_channel(chan, &sched->nan_channels[i]); + } + /* Also a pre-existing channel might have been ULWed, so no chanctx */ + if (!chan->chanctx_conf) { ret = ieee80211_nan_use_chanctx(sdata, chan, false); if (ret) { memset(chan, 0, sizeof(*chan)); @@ -708,3 +711,137 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, ieee80211_nan_free_peer_sched(to_free); return ret; } + +void +ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata, + struct ieee80211_nan_channel *nan_channel) +{ + struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx *ctx; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (WARN_ON(!nan_channel || !nan_channel->chanreq.oper.chan)) + return; + + conf = nan_channel->chanctx_conf; + if (WARN_ON(!conf)) + return; + + nan_channel->chanctx_conf = NULL; + + /* Update all peer channels that reference this chanctx */ + ieee80211_nan_update_peer_channels(sdata, conf); + + drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); + + cfg80211_nan_channel_evac(&sdata->wdev, &nan_channel->chanreq.oper, + GFP_KERNEL); + + /* Update NDI carrier states */ + ieee80211_nan_update_all_ndi_carriers(sdata->local); + + /* Clean up the channel context if no longer used */ + ctx = container_of(conf, struct ieee80211_chanctx, conf); + + if (ieee80211_chanctx_num_assigned(sdata->local, ctx) > 0) { + ieee80211_recalc_chanctx_chantype(sdata->local, ctx); + ieee80211_recalc_smps_chanctx(sdata->local, ctx); + ieee80211_recalc_chanctx_min_def(sdata->local, ctx); + } + + if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) + ieee80211_free_chanctx(sdata->local, ctx, false); +} + +struct ieee80211_nan_channel * +ieee80211_nan_find_evac_chan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx *ctx) +{ + struct ieee80211_nan_sched_cfg *sched_cfg; + struct ieee80211_nan_channel *evac_chan = NULL; + int min_slot_count = INT_MAX; + int usable_channels = 0; + + lockdep_assert_wiphy(local->hw.wiphy); + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_NAN)) + return NULL; + + sched_cfg = &sdata->vif.cfg.nan_sched; + + /* Find the channel to evacuate and count usable channels */ + for (int i = 0; i < IEEE80211_NAN_MAX_CHANNELS; i++) { + struct ieee80211_nan_channel *chan = + &sched_cfg->channels[i]; + struct ieee80211_chanctx *chan_ctx; + int slot_count = 0; + + if (!chan->chanreq.oper.chan || !chan->chanctx_conf) + continue; + + usable_channels++; + + chan_ctx = container_of(chan->chanctx_conf, + struct ieee80211_chanctx, conf); + + /* If ctx specified, only consider that specific chanctx */ + if (ctx) { + if (chan_ctx == ctx) + evac_chan = chan; + continue; + } + + /* Can only evacuate channels whose chanctx is NAN-only */ + if (ieee80211_chanctx_refcount(local, chan_ctx) > 1) + continue; + + /* Count how many time slots use this channel */ + for (int s = 0; s < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; s++) + if (sched_cfg->schedule[s] == chan) + slot_count++; + + if (slot_count < min_slot_count) { + min_slot_count = slot_count; + evac_chan = chan; + } + } + + /* No suitable NAN channel found */ + if (!evac_chan) + return NULL; + + /* NAN needs at least one remaining usable channel after evacuation */ + if (usable_channels < 2) + return NULL; + + return evac_chan; +} + +bool ieee80211_nan_try_evacuate(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata = + ieee80211_find_nan_sdata(local); + struct ieee80211_nan_channel *evac_chan; + struct ieee80211_chanctx *ctx = NULL; + + lockdep_assert_wiphy(local->hw.wiphy); + + if (!sdata) + return false; + + if (conf) + ctx = container_of(conf, struct ieee80211_chanctx, conf); + + evac_chan = ieee80211_nan_find_evac_chan(local, sdata, ctx); + if (!evac_chan) + return false; + + ieee80211_nan_evacuate_channel(sdata, evac_chan); + + return true; +} +EXPORT_SYMBOL(ieee80211_nan_try_evacuate); diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index ebb4f4d88c23..e9bf4ba3e60b 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -4,7 +4,7 @@ * * Copyright: (c) 2014 Czech Technical University in Prague * (c) 2014 Volkswagen Group Research - * Copyright (C) 2022 - 2024 Intel Corporation + * Copyright (C) 2022 - 2024, 2026 Intel Corporation * Author: Rostislav Lisovy * Funded by: Volkswagen Group Research */ @@ -92,6 +92,9 @@ static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) memcpy(addr, sta->sta.addr, ETH_ALEN); + ieee80211_sta_init_nss_bw_capa(&sta->deflink, + &sdata->deflink.conf->chanreq.oper); + ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", addr, sdata->name); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index f60f6a58948b..2bceb73717c6 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -8,7 +8,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2009 Johannes Berg - * Copyright (C) 2019, 2022-2025 Intel Corporation + * Copyright (C) 2019, 2022-2026 Intel Corporation */ #include #include @@ -355,9 +355,7 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) * Note: scan can't run, tmp_channel is what we use, so this * must be the currently active channel. */ - roc->on_channel = roc->chan == local->hw.conf.chandef.chan && - local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_5 && - local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_10; + roc->on_channel = roc->chan == local->hw.conf.chandef.chan; /* start this ROC */ ieee80211_recalc_idle(local); @@ -706,7 +704,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie) + unsigned int duration, u64 *cookie, + const u8 *rx_addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; @@ -894,10 +893,15 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } break; case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_PD: need_offchan = true; break; case NL80211_IFTYPE_NAN: break; + case NL80211_IFTYPE_NAN_DATA: + if (is_multicast_ether_addr(mgmt->da)) + return -EOPNOTSUPP; + break; default: return -EOPNOTSUPP; } @@ -911,7 +915,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* Check if the operating channel is the requested channel */ if (!params->chan && mlo_sta) { need_offchan = false; - } else if (sdata->vif.type == NL80211_IFTYPE_NAN) { + } else if (sdata->vif.type == NL80211_IFTYPE_NAN || + sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { /* Frames can be sent during NAN schedule */ } else if (!need_offchan) { struct ieee80211_chanctx_conf *chanctx_conf = NULL; diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 77894d997113..c2f2f78f2b4f 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -54,12 +54,9 @@ struct ieee80211_elems_parse { /* must be first for kfree to work */ struct ieee802_11_elems elems; - /* The basic Multi-Link element in the original elements */ - const struct element *ml_basic_elem; + struct ieee80211_elem_defrag ml_reconf, ml_epcs, ml_basic; - struct ieee80211_elem_defrag ml_reconf, ml_epcs; - - bool multi_link_inner; + bool inside_multilink; bool skip_vendor; /* @@ -167,11 +164,14 @@ ieee80211_parse_extension_element(u32 *crc, switch (le16_get_bits(mle->control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: - if (elems_parse->multi_link_inner) { + if (elems_parse->inside_multilink) { elems->parse_error |= IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC; break; } + elems_parse->ml_basic.elem = elem; + elems_parse->ml_basic.start = params->start; + elems_parse->ml_basic.len = params->len; break; case IEEE80211_ML_CONTROL_TYPE_RECONF: elems_parse->ml_reconf.elem = elem; @@ -563,16 +563,31 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->awake_window = (void *)pos; break; case WLAN_EID_PREQ: - elems->preq = pos; - elems->preq_len = elen; + if (ieee80211_mesh_preq_size_ok(pos, elen)) { + elems->preq = pos; + elems->preq_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_PREP: - elems->prep = pos; - elems->prep_len = elen; + if (ieee80211_mesh_prep_size_ok(pos, elen)) { + elems->prep = pos; + elems->prep_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; + if (ieee80211_mesh_perr_size_ok(pos, elen)) { + elems->perr = pos; + elems->perr_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_RANN: if (elen >= sizeof(struct ieee80211_rann_ie)) @@ -813,10 +828,9 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, u8 *nontransmitted_profile) { const struct element *elem, *sub; - size_t profile_len = 0; if (!bss || !bss->transmitted_bss) - return profile_len; + return 0; for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { if (elem->datalen < 2) @@ -826,6 +840,7 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, for_each_element(sub, elem->data + 1, elem->datalen - 1) { u8 new_bssid[ETH_ALEN]; + size_t profile_len; const u8 *index; if (sub->id != 0 || sub->datalen < 4) { @@ -932,6 +947,7 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, { struct ieee802_11_elems *elems = &elems_parse->elems; struct ieee80211_mle_per_sta_profile *prof; + const struct element *ml_basic_elem = NULL; const struct element *tmp, *ret; ssize_t ml_len; const u8 *end; @@ -951,12 +967,11 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, IEEE80211_ML_CONTROL_TYPE_BASIC) continue; - elems_parse->ml_basic_elem = tmp; + ml_basic_elem = tmp; break; } - ml_len = cfg80211_defragment_element(elems_parse->ml_basic_elem, - elems->ie_start, + ml_len = cfg80211_defragment_element(ml_basic_elem, elems->ie_start, elems->total_len, elems_parse->scratch_pos, elems_parse->scratch + @@ -1046,7 +1061,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) const struct element *non_inherit = NULL; struct ieee802_11_elems *elems; size_t scratch_len = 3 * params->len; - bool multi_link_inner = false; + bool inside_multilink = false; BUILD_BUG_ON(sizeof(empty_non_inheritance) != empty_non_inheritance[1] + 2); BUILD_BUG_ON(offsetof(typeof(*elems_parse), elems) != 0); @@ -1060,6 +1075,9 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) if (!elems_parse) return NULL; + elems_parse->elems.frame_type = params->type; + elems_parse->elems.from_ap = params->from_ap; + elems_parse->scratch_len = scratch_len; elems_parse->scratch_pos = elems_parse->scratch; @@ -1091,6 +1109,10 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) sub.type = params->type; sub.link_id = params->link_id; + /* indicate to consumer whether or not profile was found */ + if (params->bss->transmitted_bss && !nontx_len) + elems->mbssid_nontx_profile_missing = true; + /* consume the space used for non-transmitted profile */ elems_parse->scratch_pos += nontx_len; @@ -1108,10 +1130,13 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) if (params->bss->transmitted_bss && !non_inherit) non_inherit = (const void *)empty_non_inheritance; } else { - /* must always parse to get elems_parse->ml_basic_elem */ + /* + * Find the multi-link element and the non-inherit element inside + * the applicable profile, if requested by params->link_id >= 0. + */ non_inherit = ieee80211_prep_mle_link_parse(elems_parse, params, &sub); - multi_link_inner = true; + inside_multilink = true; } elems_parse->skip_vendor = @@ -1122,7 +1147,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) /* Override with nontransmitted/per-STA profile if found */ if (sub.len) { - elems_parse->multi_link_inner = multi_link_inner; + elems_parse->inside_multilink = inside_multilink; elems_parse->skip_vendor = false; _ieee802_11_parse_elems_full(&sub, elems_parse, NULL); } @@ -1133,6 +1158,10 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) elems->ml_epcs = ieee80211_mle_defrag(elems_parse, &elems_parse->ml_epcs, &elems->ml_epcs_len); + if (!elems->ml_basic) + elems->ml_basic = ieee80211_mle_defrag(elems_parse, + &elems_parse->ml_basic, + &elems->ml_basic_len); if (elems->tim && !elems->parse_error) { const struct ieee80211_tim_ie *tim_ie = elems->tim; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 31af7dd6aedc..64768abb0a5f 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -4,7 +4,7 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2019, 2022-2025 Intel Corporation + * Copyright (C) 2019, 2022-2026 Intel Corporation */ #include @@ -38,8 +38,6 @@ void rate_control_rate_init(struct link_sta_info *link_sta) struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *chanctx_conf; - ieee80211_sta_init_nss(link_sta); - if (!ref) return; @@ -603,14 +601,8 @@ static void rate_idx_match_mask(s8 *rate_idx, u16 *rate_flags, return; /* if HT BSS, and we handle a data frame, also try HT rates */ - switch (chan_width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: + if (chan_width == NL80211_CHAN_WIDTH_20_NOHT) return; - default: - break; - } *rate_idx = 0; /* keep protection flags */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3fb40449c6c5..fb9a3574afe9 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1188,7 +1188,7 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx, int index) { - struct sk_buff_head *frames = &tid_agg_rx->reorder_buf[index]; + struct sk_buff_head *frames = &tid_agg_rx->reorder[index].buf; struct sk_buff *tail = skb_peek_tail(frames); struct ieee80211_rx_status *status; @@ -1211,7 +1211,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, int index, struct sk_buff_head *frames) { - struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index]; + struct sk_buff_head *skb_list = &tid_agg_rx->reorder[index].buf; struct sk_buff *skb; struct ieee80211_rx_status *status; @@ -1290,14 +1290,14 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, continue; } if (skipped && - !time_after(jiffies, tid_agg_rx->reorder_time[j] + + !time_after(jiffies, tid_agg_rx->reorder[j].time + HT_RX_REORDER_BUF_TIMEOUT)) goto set_release_timer; /* don't leave incomplete A-MSDUs around */ for (i = (index + 1) % tid_agg_rx->buf_size; i != j; i = (i + 1) % tid_agg_rx->buf_size) - __skb_queue_purge(&tid_agg_rx->reorder_buf[i]); + __skb_queue_purge(&tid_agg_rx->reorder[i].buf); ht_dbg_ratelimited(sdata, "release an RX reorder frame due to timeout on earlier frames\n"); @@ -1331,7 +1331,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, if (!tid_agg_rx->removed) mod_timer(&tid_agg_rx->reorder_timer, - tid_agg_rx->reorder_time[j] + 1 + + tid_agg_rx->reorder[j].time + 1 + HT_RX_REORDER_BUF_TIMEOUT); } else { timer_delete(&tid_agg_rx->reorder_timer); @@ -1426,9 +1426,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata } /* put the frame in the reordering buffer */ - __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb); + __skb_queue_tail(&tid_agg_rx->reorder[index].buf, skb); if (!(status->flag & RX_FLAG_AMSDU_MORE)) { - tid_agg_rx->reorder_time[index] = jiffies; + tid_agg_rx->reorder[index].time = jiffies; tid_agg_rx->stored_mpdu_num++; ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); } @@ -3947,6 +3947,29 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) break; } break; + case WLAN_CATEGORY_PROTECTED_UHR: + if (len < IEEE80211_MIN_ACTION_SIZE(action_code)) + break; + + switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST: + if (sdata->vif.type != NL80211_IFTYPE_AP) + break; + if (len < IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req)) + goto invalid; + if (mgmt->u.action.uhr_link_reconf_req.type != + IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST) + break; + goto queue; + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_notif)) + goto invalid; + goto queue; + } + break; } return RX_CONTINUE; @@ -4624,16 +4647,24 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) ieee80211_has_fromds(hdr->frame_control)) return false; - /* Accept only frames that are addressed to the NAN cluster + /* + * Accept only frames that are addressed to the NAN cluster * (based on the Cluster ID). From these frames, accept only - * action frames or authentication frames that are addressed to - * the local NAN interface. + * - public action frames, + * - authentication frames to the local address, and + * - robust management frames except disassoc. */ - return memcmp(sdata->wdev.u.nan.cluster_id, - hdr->addr3, ETH_ALEN) == 0 && - (ieee80211_is_public_action(hdr, skb->len) || - (ieee80211_is_auth(hdr->frame_control) && - ether_addr_equal(sdata->vif.addr, hdr->addr1))); + if (!ether_addr_equal(sdata->u.nan.conf.cluster_id, hdr->addr3)) + return false; + if (ieee80211_is_public_action(hdr, skb->len)) + return true; + if (ieee80211_is_auth(hdr->frame_control) && + ether_addr_equal(sdata->vif.addr, hdr->addr1)) + return true; + if (!ieee80211_is_disassoc(hdr->frame_control) && + ieee80211_is_robust_mgmt_frame(skb)) + return true; + return false; case NL80211_IFTYPE_NAN_DATA: if (ieee80211_has_tods(hdr->frame_control) || ieee80211_has_fromds(hdr->frame_control)) @@ -4646,7 +4677,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) if (!nmi) return false; - if (!ether_addr_equal(nmi->wdev.u.nan.cluster_id, + if (!ether_addr_equal(nmi->u.nan.conf.cluster_id, hdr->addr3)) return false; @@ -4664,6 +4695,13 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) /* Unicast secure management frames */ return ether_addr_equal(sdata->vif.addr, hdr->addr1) && ieee80211_is_unicast_robust_mgmt_frame(skb); + case NL80211_IFTYPE_PD: + /* + * Accept only authentication frames (PASN) addressed to + * this interface. + */ + return ieee80211_is_auth(hdr->frame_control) && + ether_addr_equal(sdata->vif.addr, hdr->addr1); default: break; } @@ -5606,6 +5644,14 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, status->rate_idx, status->nss)) goto drop; break; + case RX_ENC_S1G: + if (WARN_ONCE(status->rate_idx > 12 || + !status->nss || + status->nss > 4, + "Rate marked as an S1G rate but data is invalid: MCS: %d, NSS: %d\n", + status->rate_idx, status->nss)) + goto drop; + break; default: WARN_ON_ONCE(1); fallthrough; diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index e2eaf8d8d7ff..ec622750e1c9 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -329,16 +329,6 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, case -1: cfg80211_chandef_create(&csa_ie->chanreq.oper, new_chan, NL80211_CHAN_NO_HT); - /* keep width for 5/10 MHz channels */ - switch (sdata->vif.bss_conf.chanreq.oper.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - csa_ie->chanreq.oper.width = - sdata->vif.bss_conf.chanreq.oper.width; - break; - default: - break; - } break; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4c31ef8817ce..02b587ff8504 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -572,11 +572,27 @@ static int sta_info_alloc_link(struct ieee80211_local *local, link_info->rx_omi_bw_tx = IEEE80211_STA_RX_BW_MAX; link_info->rx_omi_bw_staging = IEEE80211_STA_RX_BW_MAX; + /* + * This will always be taken into account, so set to MAX. + * When mac80211 is the client on a UHR AP, it'll be used + * for the TX side, to limit the bandwidth to TX to the AP + * with, to limit to the BSS width during DBE enablement. + * + * This is needed since the chanreq, which normally has + * maximum bandwidth to use with the AP, will already be + * set to the DBE width during enablement to prepare for + * RX (and not be racy), but the TX can only use higher + * bandwidth after enablement finishes. + */ + link_info->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + + link_info->op_mode_bw = IEEE80211_STA_RX_BW_MAX; + /* * Cause (a) warning(s) if IEEE80211_STA_RX_BW_MAX != 320 * or if new values are added to the enum. */ - switch (link_info->cur_max_bandwidth) { + switch (link_info->op_mode_bw) { case IEEE80211_STA_RX_BW_20: case IEEE80211_STA_RX_BW_40: case IEEE80211_STA_RX_BW_80: @@ -2603,6 +2619,13 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate, if (STA_STATS_GET(UHR_IM, rate)) rinfo->flags |= RATE_INFO_FLAGS_UHR_IM; break; + case STA_STATS_RATE_TYPE_S1G: + rinfo->flags = RATE_INFO_FLAGS_S1G_MCS; + rinfo->mcs = STA_STATS_GET(S1G_MCS, rate); + rinfo->nss = STA_STATS_GET(S1G_NSS, rate); + if (STA_STATS_GET(SGI, rate)) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + break; } } @@ -3256,7 +3279,10 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, struct link_sta_info *link_sta; int link_id; - ether_addr_copy(sinfo->mld_addr, sta->addr); + sinfo->mlo_params_valid = true; + sinfo->assoc_link_id = sta->deflink.link_id; + if (sta->sta.mlo) + ether_addr_copy(sinfo->mld_addr, sta->addr); /* assign valid links first for iteration */ sinfo->valid_links = sta->sta.valid_links; @@ -3424,6 +3450,121 @@ void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id) sta_remove_link(sta, link_id, true); } +static u8 ieee80211_sta_nss_capability(struct link_sta_info *link_sta) +{ + u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; + bool support_160; + + if (link_sta->pub->eht_cap.has_eht) { + int i; + const u8 *rx_nss_mcs = (void *)&link_sta->pub->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 (link_sta->pub->he_cap.has_he) { + int i; + u8 rx_mcs_80 = 0, rx_mcs_160 = 0; + const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; + u16 mcs_160_map = + le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); + + for (i = 7; i >= 0; i--) { + u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; + + if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { + rx_mcs_160 = i + 1; + break; + } + } + for (i = 7; i >= 0; i--) { + u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; + + if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { + rx_mcs_80 = i + 1; + break; + } + } + + support_160 = he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + if (support_160) + he_rx_nss = min(rx_mcs_80, rx_mcs_160); + else + he_rx_nss = rx_mcs_80; + } + + if (link_sta->pub->ht_cap.ht_supported) { + if (link_sta->pub->ht_cap.mcs.rx_mask[0]) + ht_rx_nss++; + if (link_sta->pub->ht_cap.mcs.rx_mask[1]) + ht_rx_nss++; + if (link_sta->pub->ht_cap.mcs.rx_mask[2]) + ht_rx_nss++; + if (link_sta->pub->ht_cap.mcs.rx_mask[3]) + ht_rx_nss++; + /* FIXME: consider rx_highest? */ + } + + if (link_sta->pub->vht_cap.vht_supported) { + int i; + u16 rx_mcs_map; + + rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map); + + for (i = 7; i >= 0; i--) { + u8 mcs = (rx_mcs_map >> (2 * i)) & 3; + + if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + vht_rx_nss = i + 1; + break; + } + } + /* FIXME: consider rx_highest? */ + } + + 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); + rx_nss = max_t(u8, 1, rx_nss); + + return rx_nss; +} + +void ieee80211_sta_init_nss_bw_capa(struct link_sta_info *link_sta, + struct cfg80211_chan_def *chandef) +{ + /* + * TODO: The entirety of the STA Tx/Rx bandwidth handling + * assumes 20MHz based widths, so for now don't initialise + * pubsta->bandwidth for S1G bands. Since enum + * ieee80211_sta_rx_bandwidth is ordered, we will probably + * need to introduce ieee80211_s1g_sta_rx_bandwidth with + * S1G widths and associated S1G specific code. Additionally, + * existing S1G hardware is all 1SS, in the future if hardware + * starts supporting >1SS this should be implemented in + * ieee80211_sta_nss_capability(). + */ + if (cfg80211_chandef_is_s1g(chandef)) { + link_sta->capa_nss = 1; + link_sta->pub->rx_nss = 1; + return; + } + + link_sta->capa_nss = ieee80211_sta_nss_capability(link_sta); + link_sta->pub->rx_nss = link_sta->capa_nss; + + link_sta->pub->bandwidth = + ieee80211_sta_current_bw(link_sta, chandef, + IEEE80211_STA_BW_TX_TO_STA); +} + void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta, const u8 *ext_capab, unsigned int ext_capab_len) @@ -3456,3 +3597,224 @@ bool lockdep_sta_mutex_held(struct ieee80211_sta *pubsta) } EXPORT_SYMBOL(lockdep_sta_mutex_held); #endif + +/** + * ieee80211_sta_bw_capability - get STA's bandwidth capability + * @link_sta: the (link) STA to get the capability for + * @band: the band to get the capability on + * + * Return: the maximum bandwidth supported by the STA + */ +static enum ieee80211_sta_rx_bandwidth +ieee80211_sta_bw_capability(struct link_sta_info *link_sta, + enum nl80211_band band) +{ + struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; + struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; + struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap; + u32 cap_width; + + if (he_cap->has_he) { + u8 info; + + if (eht_cap->has_eht && 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 (band == NL80211_BAND_2GHZ) { + if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) + return IEEE80211_STA_RX_BW_40; + return IEEE80211_STA_RX_BW_20; + } + + if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || + info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + return IEEE80211_STA_RX_BW_160; + + if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) + return IEEE80211_STA_RX_BW_80; + + return IEEE80211_STA_RX_BW_20; + } + + if (!vht_cap->vht_supported) + return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_STA_RX_BW_40 : + IEEE80211_STA_RX_BW_20; + + cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + + if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || + cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) + return IEEE80211_STA_RX_BW_160; + + /* + * If this is non-zero, then it does support 160 MHz after all, + * in one form or the other. We don't distinguish here (or even + * above) between 160 and 80+80 yet. + */ + if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) + return IEEE80211_STA_RX_BW_160; + + return IEEE80211_STA_RX_BW_80; +} + +/** + * ieee80211_sta_usable_bw - get STA's usable bandwidth capability + * @link_sta: the (link) STA to get the capability for + * @band: the band to get the capability on + * + * If the STA is on an AP interface, take into account the AP's + * bandwidth corresponding to this station's PHY capability + * + * Return: the maximum bandwidth supported by the STA on the + * connection to the interface it's connected to + */ +static enum ieee80211_sta_rx_bandwidth +ieee80211_sta_usable_bw(struct link_sta_info *link_sta, + enum nl80211_band band) +{ + struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata; + enum ieee80211_sta_rx_bandwidth bw; + struct ieee80211_link_data *link; + + bw = ieee80211_sta_bw_capability(link_sta, band); + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + sdata = get_bss_sdata(sdata); + + /* for a STA to exist on VLAN, it must have AP */ + if (WARN_ON(!sdata)) + return IEEE80211_STA_RX_BW_20; + } + + if (sdata->vif.type != NL80211_IFTYPE_AP) + return bw; + + /* for a link STA to exist, vif must have the link */ + link = sdata_dereference(sdata->link[link_sta->link_id], sdata); + if (WARN_ON(!link)) + return IEEE80211_STA_RX_BW_20; + + if (!link_sta->pub->eht_cap.has_eht) + return min(bw, link->bss_bw.he_and_lower); + + if (!link_sta->pub->uhr_cap.has_uhr || + !link_sta->uhr_dbe_enabled) + return min(bw, link->bss_bw.eht); + + return bw; +} + +static enum ieee80211_sta_rx_bandwidth +ieee80211_sta_current_bw_rx_from_sta(struct link_sta_info *link_sta, + struct cfg80211_chan_def *chandef) +{ + /* + * Take RX OMI into account. The value "rx_omi_bw_rx" is what + * we've indicated to the STA we can currently receive. + * + * This is needed since the RX OMI is done by us to save power, + * requiring changing both our TX (rate control) and RX (chanctx), + * which in turn needs to be done in the right order (stop TX + * at a higher bandwidth first while reducing bandwidth, and + * change the chanctx only after the peer accepts, etc.) + */ + return min(ieee80211_sta_usable_bw(link_sta, chandef->chan->band), + link_sta->rx_omi_bw_rx); +} + +static enum ieee80211_sta_rx_bandwidth +ieee80211_sta_current_bw_tx_to_sta(struct link_sta_info *link_sta, + struct cfg80211_chan_def *chandef) +{ + struct sta_info *sta = link_sta->sta; + enum nl80211_chan_width bss_width; + enum ieee80211_sta_rx_bandwidth bw; + enum nl80211_band band; + + bss_width = chandef->width; + band = chandef->chan->band; + + bw = ieee80211_sta_usable_bw(link_sta, band); + bw = min(bw, link_sta->op_mode_bw); + /* also limit to RX OMI bandwidth we TX to the STA */ + bw = min(bw, link_sta->rx_omi_bw_tx); + /* and UHR DBE transition limits */ + bw = min(bw, link_sta->uhr_usable_tx_width); + + /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of + * IEEE80211-2016 specification makes higher bandwidth operation + * possible on the TDLS link if the peers have wider bandwidth + * capability. + * + * However, in this case, and only if the TDLS peer is authorized, + * limit to the tdls_chandef so that the configuration here isn't + * wider than what's actually requested on the channel context. + */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && + test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) && + test_sta_flag(sta, WLAN_STA_AUTHORIZED) && + sta->tdls_chandef.chan) + bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); + else + bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); + + return bw; +} + +/** + * ieee80211_sta_current_bw - get STA's current usable bandwidth + * @link_sta: the (link) STA to get the bandwidth for + * @chandef: the chandef for the channel the STA is on + * @direction: the direction (to or from STA) + * + * Return: the maximum bandwidth that the station can/may + * (currently) use in the given direction + */ +enum ieee80211_sta_rx_bandwidth +ieee80211_sta_current_bw(struct link_sta_info *link_sta, + struct cfg80211_chan_def *chandef, + enum ieee80211_sta_bw_direction direction) +{ + if (WARN_ON(!chandef)) + return IEEE80211_STA_RX_BW_20; + + switch (direction) { + case IEEE80211_STA_BW_RX_FROM_STA: + return ieee80211_sta_current_bw_rx_from_sta(link_sta, chandef); + case IEEE80211_STA_BW_TX_TO_STA: + return ieee80211_sta_current_bw_tx_to_sta(link_sta, chandef); + } + + /* unreachable */ + return IEEE80211_STA_RX_BW_20; +} + +bool ieee80211_link_sta_update_rc_bw(struct ieee80211_link_data *link, + struct link_sta_info *link_sta) +{ + struct ieee80211_sub_if_data *sdata = link->sdata; + struct ieee80211_supported_band *sband; + enum ieee80211_sta_rx_bandwidth new_bw; + enum nl80211_band band; + + band = link->conf->chanreq.oper.chan->band; + sband = sdata->local->hw.wiphy->bands[band]; + + new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper, + IEEE80211_STA_BW_TX_TO_STA); + if (link_sta->pub->bandwidth == new_bw) + return false; + + link_sta->pub->bandwidth = new_bw; + rate_control_rate_update(sdata->local, sband, link_sta, + IEEE80211_RC_BW_CHANGED); + + return true; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 3e5d003bd31f..5da3142d8516 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -207,11 +207,8 @@ struct tid_ampdu_tx { /** * struct tid_ampdu_rx - TID aggregation information (Rx). * - * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an - * A-MSDU with individually reported subframes. * @reorder_buf_filtered: bitmap indicating where there are filtered frames in * the reorder buffer that should be ignored when releasing frames - * @reorder_time: jiffies when skb was added * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) * @reorder_timer: releases expired frames from the reorder buffer. * @sta: station we are attached to @@ -228,6 +225,10 @@ struct tid_ampdu_tx { * and ssn. * @removed: this session is removed (but might have been found due to RCU) * @started: this session has started (head ssn or higher was received) + * @reorder: reorder buffer entries + * @reorder.buf: &struct sk_buff_head for the frames, since there could be + * multiple at each entry from an A-MSDU reported as individual subframes + * @reorder.time: time when this entry was filled (jiffies) * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. @@ -241,8 +242,6 @@ struct tid_ampdu_rx { struct rcu_head rcu_head; spinlock_t reorder_lock; u64 reorder_buf_filtered; - struct sk_buff_head *reorder_buf; - unsigned long *reorder_time; struct sta_info *sta; struct timer_list session_timer; struct timer_list reorder_timer; @@ -256,6 +255,10 @@ struct tid_ampdu_rx { u8 auto_seq:1, removed:1, started:1; + struct { + struct sk_buff_head buf; + unsigned long time; + } reorder[]; }; /** @@ -504,13 +507,18 @@ struct ieee80211_fragment_cache { * @status_stats.last_ack_signal: last ACK signal * @status_stats.ack_signal_filled: last ACK signal validity * @status_stats.avg_ack_signal: average ACK signal - * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, + * @op_mode_bw: dynamic bandwidth limit to transmit to the STA, * taken from HT/VHT capabilities or VHT operating mode notification. * Invalid for NAN since that is operating on multiple bands. * @rx_omi_bw_rx: RX OMI bandwidth restriction to apply for RX * @rx_omi_bw_tx: RX OMI bandwidth restriction to apply for TX * @rx_omi_bw_staging: RX OMI bandwidth restriction to apply later * during finalize + * @uhr_usable_tx_width: bandwidth restriction for UHR for TX, only when + * the link_sta is an AP, to restrict TX to BSS width during DBE + * enablement + * @uhr_dbe_enabled: for STAs as clients to an AP interface indicates + * DBE is enabled by the STA * @debugfs_dir: debug filesystem directory dentry * @pub: public (driver visible) link STA data */ @@ -558,10 +566,13 @@ struct link_sta_info { u64 msdu[IEEE80211_NUM_TIDS + 1]; } tx_stats; - enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; + enum ieee80211_sta_rx_bandwidth op_mode_bw; enum ieee80211_sta_rx_bandwidth rx_omi_bw_rx, rx_omi_bw_tx, rx_omi_bw_staging; + enum ieee80211_sta_rx_bandwidth uhr_usable_tx_width; + + bool uhr_dbe_enabled; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *debugfs_dir; @@ -997,12 +1008,27 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); unsigned long ieee80211_sta_last_active(struct sta_info *sta, int link_id); +void ieee80211_sta_init_nss_bw_capa(struct link_sta_info *link_sta, + struct cfg80211_chan_def *chandef); void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta, const u8 *ext_capab, unsigned int ext_capab_len); void __ieee80211_sta_recalc_aggregates(struct sta_info *sta, u16 active_links); +enum ieee80211_sta_bw_direction { + IEEE80211_STA_BW_RX_FROM_STA, + IEEE80211_STA_BW_TX_TO_STA, +}; + +enum ieee80211_sta_rx_bandwidth +ieee80211_sta_current_bw(struct link_sta_info *link_sta, + struct cfg80211_chan_def *chandef, + enum ieee80211_sta_bw_direction direction); + +bool ieee80211_link_sta_update_rc_bw(struct ieee80211_link_data *link, + struct link_sta_info *link_sta); + enum sta_stats_type { STA_STATS_RATE_TYPE_INVALID = 0, STA_STATS_RATE_TYPE_LEGACY, @@ -1030,7 +1056,7 @@ enum sta_stats_type { #define STA_STATS_FIELD_VHT_MCS 0x0000F000 #define STA_STATS_FIELD_VHT_NSS 0x000F0000 -/* HT & VHT */ +/* HT, VHT & S1G */ #define STA_STATS_FIELD_SGI 0x00100000 /* STA_STATS_RATE_TYPE_HE */ @@ -1054,6 +1080,9 @@ enum sta_stats_type { #define STA_STATS_FIELD_UHR_ELR 0x08000000 #define STA_STATS_FIELD_UHR_IM 0x10000000 +/* STA_STATS_RATE_TYPE_S1G */ +#define STA_STATS_FIELD_S1G_MCS 0x0000F000 +#define STA_STATS_FIELD_S1G_NSS 0x000F0000 #define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v) #define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v) @@ -1069,6 +1098,7 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) switch (s->encoding) { case RX_ENC_HT: case RX_ENC_VHT: + case RX_ENC_S1G: if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) r |= STA_STATS_FIELD(SGI, 1); break; @@ -1115,6 +1145,11 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) r |= STA_STATS_FIELD(UHR_ELR, s->uhr.elr); r |= STA_STATS_FIELD(UHR_IM, s->uhr.im); break; + case RX_ENC_S1G: + r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G); + r |= STA_STATS_FIELD(S1G_NSS, s->nss); + r |= STA_STATS_FIELD(S1G_MCS, s->rate_idx); + break; default: WARN_ON(1); return STA_STATS_RATE_INVALID; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 4b38aa0e902a..dd1dbba06838 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2008-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright 2021-2025 Intel Corporation + * Copyright 2021-2026 Intel Corporation */ #include @@ -295,9 +295,10 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local, RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_HE_MCS))) legacy_rate = status_rate->rate_idx.legacy; - } else if (info->status.rates[0].idx >= 0 && - !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | - IEEE80211_TX_RC_VHT_MCS))) { + } else if (info->band < NUM_NL80211_BANDS && + info->status.rates[0].idx >= 0 && + !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | + IEEE80211_TX_RC_VHT_MCS))) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[info->band]; @@ -730,6 +731,28 @@ ieee80211_handle_teardown_ttlm_status(struct ieee80211_sub_if_data *sdata, &sdata->u.mgd.teardown_ttlm_work); } +static void +ieee80211_handle_uhr_omp_status(struct ieee80211_sub_if_data *sdata, bool acked) +{ + if (!sdata || !ieee80211_sdata_running(sdata)) + return; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return; + + sdata->u.mgd.uhr_omp.acked = acked; + + if (!acked) { + wiphy_hrtimer_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.uhr_omp.status_work, 0); + return; + } + + wiphy_hrtimer_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.uhr_omp.status_work, + us_to_ktime(sdata->u.mgd.uhr_omp.timeout_us)); +} + static void ieee80211_report_used_skb(struct ieee80211_local *local, struct sk_buff *skb, bool dropped, ktime_t ack_hwtstamp) @@ -810,6 +833,9 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, case IEEE80211_STATUS_TYPE_NEG_TTLM: ieee80211_handle_teardown_ttlm_status(sdata, acked); break; + case IEEE80211_STATUS_TYPE_UHR_OMP: + ieee80211_handle_uhr_omp_status(sdata, acked); + break; } rcu_read_unlock(); } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 44bba7c3e603..ffd575a8d188 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -311,17 +311,21 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, /* IEEE802.11ac-2013 Table E-4 */ static const u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 }; struct cfg80211_chan_def uc = sta->tdls_chandef; - enum nl80211_chan_width max_width = - ieee80211_sta_cap_chan_bw(&sta->deflink); + enum nl80211_chan_width max_width; int i; - /* only support upgrading non-narrow channels up to 80Mhz */ - if (max_width == NL80211_CHAN_WIDTH_5 || - max_width == NL80211_CHAN_WIDTH_10) - return; - - if (max_width > NL80211_CHAN_WIDTH_80) + switch (ieee80211_sta_current_bw(&sta->deflink, &uc, + IEEE80211_STA_BW_RX_FROM_STA)) { + case IEEE80211_STA_RX_BW_20: + max_width = NL80211_CHAN_WIDTH_20; + break; + case IEEE80211_STA_RX_BW_40: + max_width = NL80211_CHAN_WIDTH_40; + break; + default: /* 80 or higher, only support upgrade to 80 */ max_width = NL80211_CHAN_WIDTH_80; + break; + } if (uc.width >= max_width) return; @@ -1334,7 +1338,9 @@ static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata, enum ieee80211_sta_rx_bandwidth bw; bw = ieee80211_chan_width_to_rx_bw(conf->def.width); - bw = min(bw, ieee80211_sta_cap_rx_bw(&sta->deflink)); + bw = min(bw, ieee80211_sta_current_bw(&sta->deflink, + &conf->def, + IEEE80211_STA_BW_RX_FROM_STA)); if (bw != sta->sta.deflink.bandwidth) { sta->sta.deflink.bandwidth = bw; rate_control_rate_update(local, sband, diff --git a/net/mac80211/tests/.kunitconfig b/net/mac80211/tests/.kunitconfig new file mode 100644 index 000000000000..ab2cc5cfc1f5 --- /dev/null +++ b/net/mac80211/tests/.kunitconfig @@ -0,0 +1,4 @@ +CONFIG_KUNIT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_MAC80211_KUNIT_TEST=y diff --git a/net/mac80211/tests/Makefile b/net/mac80211/tests/Makefile index 3c7f874e5c41..2e9ade90f7b6 100644 --- a/net/mac80211/tests/Makefile +++ b/net/mac80211/tests/Makefile @@ -1,3 +1,3 @@ -mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o s1g_tim.o +mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o s1g_tim.o ttlm.o obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o diff --git a/net/mac80211/tests/chan-mode.c b/net/mac80211/tests/chan-mode.c index fa370831d617..ab7d38ef6a3a 100644 --- a/net/mac80211/tests/chan-mode.c +++ b/net/mac80211/tests/chan-mode.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel mode functions * - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include @@ -243,7 +243,7 @@ static void test_determine_chan_mode(struct kunit *test) rcu_read_lock(); elems = ieee80211_determine_chan_mode(t_sdata->sdata, &conn, &cbss, - 0, &chanreq, &ap_chandef, + NULL, 0, &chanreq, &ap_chandef, userspace_selectors); rcu_read_unlock(); diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c index 1039794a0183..4b13a5aad875 100644 --- a/net/mac80211/tests/elems.c +++ b/net/mac80211/tests/elems.c @@ -9,6 +9,237 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +static const struct mesh_preq_parse_test_case { + const char *desc; + u8 len; + bool ae_enabled; + u8 target_count; + bool result; +} mesh_preq_parse_cases[] = { + { + .desc = "shorter than header", + .len = 16, + .ae_enabled = false, + .target_count = 1, + .result = false, + }, + { + .desc = "too short non AE, target count is not included", + .len = 29, + .ae_enabled = false, + .target_count = 1, + .result = false, + }, + { + .desc = "too short non AE, target count is 1", + .len = 36, + .ae_enabled = false, + .target_count = 1, + .result = false, + }, + { + .desc = "too short AE, target count is not included", + .len = 35, + .ae_enabled = true, + .target_count = 1, + .result = false, + }, + { + .desc = "too short AE, target count is 1", + .len = 42, + .ae_enabled = true, + .target_count = 1, + .result = false, + }, + { + .desc = "target count is zero", + .len = 26, + .ae_enabled = false, + .target_count = 0, + .result = false, + }, + { + .desc = "target count is 21", + .len = 255, + .ae_enabled = false, + .target_count = 21, + .result = false, + }, + { + .desc = "non AE, target count is 1", + .len = 37, + .ae_enabled = false, + .target_count = 1, + .result = true, + }, + { + .desc = "non AE, target count is 20", + .len = 246, + .ae_enabled = false, + .target_count = 20, + .result = true, + }, + { + .desc = "AE, target count is 1", + .len = 43, + .ae_enabled = true, + .target_count = 1, + .result = true, + }, + { + .desc = "AE, target count is 20", + .len = 252, + .ae_enabled = true, + .target_count = 20, + .result = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc); + +static const struct mesh_prep_parse_test_case { + const char *desc; + u8 len; + bool ae_enabled; + bool result; +} mesh_prep_parse_cases[] = { + { + .desc = "shorter than header", + .len = 12, + .ae_enabled = false, + .result = false, + }, + { + .desc = "non AE short", + .len = 30, + .ae_enabled = false, + .result = false, + }, + { + .desc = "non AE", + .len = 31, + .ae_enabled = false, + .result = true, + }, + { + .desc = "AE short", + .len = 36, + .ae_enabled = true, + .result = false, + }, + { + .desc = "AE", + .len = 37, + .ae_enabled = true, + .result = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc); + +static const struct mesh_perr_parse_test_case { + const char *desc; + u8 len; + u8 number_of_dst; + int ae_enabled_idx; + bool result; +} mesh_perr_parse_cases[] = { + { + .desc = "shorter than header", + .len = 1, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 0", + .len = 2, + .number_of_dst = 0, + .ae_enabled_idx = -1, + .result = true, + }, + { + .desc = "number_of_dst is 20", + .len = 255, + .number_of_dst = 20, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, non AE, short", + .len = 14, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, non AE", + .len = 15, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = true, + }, + { + .desc = "number_of_dst is 1, non AE, extra short dst header", + .len = 25, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, non AE, extra dst header", + .len = 26, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, AE, short", + .len = 20, + .number_of_dst = 1, + .ae_enabled_idx = 0, + .result = false, + }, + { + .desc = "number_of_dst is 1, AE", + .len = 21, + .number_of_dst = 1, + .ae_enabled_idx = 0, + .result = true, + }, + { + .desc = "number_of_dst is 19, non AE, short", + .len = 2 + 13 * 19 - 1, + .number_of_dst = 19, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 19, non AE", + .len = 2 + 13 * 19, + .number_of_dst = 19, + .ae_enabled_idx = -1, + .result = true, + }, + { + .desc = "number_of_dst is 19, AE, short", + .len = 2 + 13 * 19 + 6 - 1, + .number_of_dst = 19, + .ae_enabled_idx = 18, + .result = false, + }, + { + .desc = "number_of_dst is 19, AE", + .len = 2 + 13 * 19 + 6, + .number_of_dst = 19, + .ae_enabled_idx = 18, + .result = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(mesh_perr_parse, mesh_perr_parse_cases, desc); + + static void mle_defrag(struct kunit *test) { struct ieee80211_elems_parse_params parse_params = { @@ -91,8 +322,59 @@ static void mle_defrag(struct kunit *test) kfree_skb(skb); } +static void mesh_preq_parse(struct kunit *test) +{ + const struct mesh_preq_parse_test_case *params = test->param_value; + u8 data[64] = {}; + struct ieee80211_mesh_hwmp_preq_top *top = (void *)data; + struct ieee80211_mesh_hwmp_preq_bottom *bottom; + + top->flags = params->ae_enabled ? AE_F : 0; + bottom = ieee80211_mesh_hwmp_preq_get_bottom(data); + bottom->target_count = params->target_count; + + KUNIT_EXPECT_EQ(test, + ieee80211_mesh_preq_size_ok(data, params->len), + params->result); +} + +static void mesh_prep_parse(struct kunit *test) +{ + const struct mesh_prep_parse_test_case *params = test->param_value; + u8 data[64] = {}; + struct ieee80211_mesh_hwmp_prep_top *top = (void *)data; + top->flags = params->ae_enabled ? AE_F : 0; + + KUNIT_EXPECT_EQ(test, + ieee80211_mesh_prep_size_ok(data, params->len), + params->result); +} + +static void mesh_perr_parse(struct kunit *test) +{ + const struct mesh_perr_parse_test_case *params = test->param_value; + u8 data[256] = {}; + struct ieee80211_mesh_hwmp_perr *perr = (void *)data; + + perr->number_of_dst = params->number_of_dst; + if (params->ae_enabled_idx > -1) { + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst( + data, params->ae_enabled_idx); + + dst->flags = AE_F; + } + + KUNIT_EXPECT_EQ(test, + ieee80211_mesh_perr_size_ok(data, params->len), + params->result); +} + static struct kunit_case element_parsing_test_cases[] = { KUNIT_CASE(mle_defrag), + KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params), + KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params), + KUNIT_CASE_PARAM(mesh_perr_parse, mesh_perr_parse_gen_params), {} }; diff --git a/net/mac80211/tests/ttlm.c b/net/mac80211/tests/ttlm.c new file mode 100644 index 000000000000..18d0592b13d9 --- /dev/null +++ b/net/mac80211/tests/ttlm.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for negotiated TTLM (TID-To-Link Mapping) parsing + * + * Copyright (C) 2026 Michael Bommarito + */ +#include +#include +#include "../ieee80211_i.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +/* + * Build a negotiated TTLM element in caller-supplied buffer. + * + * @buf: destination buffer (must be at least elem_size bytes) + * @elem_size: sizeof(ttlm_elem) + 1 (presence byte) + npresent * bm_size + * @presence: link_map_presence bitmask; each set bit => one map follows + * @bm_size: bytes per map (1 or 2); 2 => LINK_MAP_SIZE bit clear + * @maps: array of npresent u16 maps, one per set bit in presence + * + * Control field encodes direction=BOTH; no switch-time, no expected-dur, + * no DEF_LINK_MAP. LINK_MAP_SIZE bit is set iff bm_size==1. + * + * Returns pointer to the ieee80211_ttlm_elem at buf. + */ +static const struct ieee80211_ttlm_elem * +build_neg_ttlm_elem(u8 *buf, size_t elem_size, + u8 presence, u8 bm_size, const u16 *maps) +{ + struct ieee80211_ttlm_elem *t = (void *)buf; + u8 control; + u8 *pos; + int i, tid; + + memset(buf, 0, elem_size); + + control = IEEE80211_TTLM_DIRECTION_BOTH; /* bits [1:0] = 2 */ + if (bm_size == 1) + control |= IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE; + + t->control = control; + + pos = (u8 *)t->optional; + *pos++ = presence; + + i = 0; + for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) { + if (!(presence & BIT(tid))) + continue; + if (bm_size == 1) + *pos = (u8)maps[i]; + else + put_unaligned_le16(maps[i], pos); + pos += bm_size; + i++; + } + + return t; +} + +/* + * sparse_presence_no_oob_read - BIT(0)|BIT(7) presence, bm_size=2 + * + * Only TID 0 and TID 7 have maps; TIDs 1-6 are absent. Element length + * is exactly 6 bytes (1 control + 1 presence + 2 * 2-byte maps). + * + * Pre-fix the parser advanced pos by bm_size AFTER the switch() block + * (i.e. unconditionally for every TID), so when processing TID 7 it + * had already advanced 6 * bm_size = 12 bytes past the presence byte + * for the absent TIDs before reading the TID-7 map - 14 bytes past the + * end of the 2-byte TID-7 map. Under KASAN that is a slab-out-of-bounds. + * + * After the fix pos is advanced only inside the presence-bit branch so + * the cursor lands exactly at end-of-element after processing TID 7. + */ +static void sparse_presence_no_oob_read(struct kunit *test) +{ + /* + * presence = BIT(0)|BIT(7): 2 maps present. + * elem_size = sizeof(ttlm_elem) + 1 (presence) + 2*2 (maps) = 6. + */ + const u8 presence = BIT(0) | BIT(7); + const u8 bm_size = 2; + const int npresent = 2; + const size_t elem_size = sizeof(struct ieee80211_ttlm_elem) + + 1 + npresent * bm_size; + /* + * Allocate exact-size buffer so a pre-fix OOB read walks into the + * KASAN red zone immediately after the allocation. + */ + u8 *buf = kunit_kzalloc(test, elem_size, GFP_KERNEL); + const struct ieee80211_ttlm_elem *ttlm; + struct ieee80211_neg_ttlm neg_ttlm = {}; + /* Non-zero maps so the parser does not reject with -EINVAL. */ + const u16 maps[2] = { 0x0001, 0x0001 }; + u8 direction = 0; + int ret; + + KUNIT_ASSERT_NOT_NULL(test, buf); + + ttlm = build_neg_ttlm_elem(buf, elem_size, presence, bm_size, maps); + + /* + * Pass NULL for sdata: the only sdata dereference in this code path + * is inside mlme_dbg() on error returns, which are guarded by + * MAC80211_MLME_DEBUG == 0 in non-debug builds and by the dead-code + * eliminator in KUnit builds. The success path does not touch sdata. + */ + ret = ieee80211_parse_neg_ttlm(NULL, ttlm, &neg_ttlm, &direction); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)direction, IEEE80211_TTLM_DIRECTION_BOTH); + /* TID 0: map present */ + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[0], 0x0001); + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[0], 0x0001); + /* TID 3: absent => map should be 0 */ + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[3], 0); + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[3], 0); + /* TID 7: map present */ + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[7], 0x0001); + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[7], 0x0001); +} + +/* + * dense_presence_baseline - presence=0xff (all 8 TIDs), bm_size=2 + * + * Every TID has a map; this is the dense layout the parser handled + * correctly even before the fix. Confirms the cursor-advance fix + * does not regress the already-correct path. + */ +static void dense_presence_baseline(struct kunit *test) +{ + const u8 presence = 0xff; + const u8 bm_size = 2; + const int npresent = 8; + const size_t elem_size = sizeof(struct ieee80211_ttlm_elem) + + 1 + npresent * bm_size; + u8 *buf = kunit_kzalloc(test, elem_size, GFP_KERNEL); + const struct ieee80211_ttlm_elem *ttlm; + struct ieee80211_neg_ttlm neg_ttlm = {}; + const u16 maps[8] = { + 0x0003, 0x0003, 0x0003, 0x0003, + 0x0003, 0x0003, 0x0003, 0x0003, + }; + u8 direction = 0; + int ret; + + KUNIT_ASSERT_NOT_NULL(test, buf); + + ttlm = build_neg_ttlm_elem(buf, elem_size, presence, bm_size, maps); + + ret = ieee80211_parse_neg_ttlm(NULL, ttlm, &neg_ttlm, &direction); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)direction, IEEE80211_TTLM_DIRECTION_BOTH); + /* All TIDs present: every downlink/uplink entry must be 0x0003. */ + for (int tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) { + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[tid], 0x0003); + KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[tid], 0x0003); + } +} + +static struct kunit_case mac80211_ttlm_test_cases[] = { + KUNIT_CASE(sparse_presence_no_oob_read), + KUNIT_CASE(dense_presence_baseline), + {} +}; + +static struct kunit_suite mac80211_ttlm = { + .name = "mac80211-ttlm", + .test_cases = mac80211_ttlm_test_cases, +}; + +kunit_test_suite(mac80211_ttlm); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 71cf88039bd4..562a4964afa3 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -44,7 +44,9 @@ __field(u32, n##center_freq1) \ __field(u32, n##freq1_offset) \ __field(u32, n##center_freq2) \ - __field(u16, n##punctured) + __field(u16, n##punctured) \ + __field(u32, n##npca_pri_freq) \ + __field(u16, n##npca_punctured) #define __CHANDEF_ASSIGN(n, c) \ __entry->n##control_freq = (c) && (c)->chan ? \ (c)->chan->center_freq : 0; \ @@ -54,14 +56,18 @@ __entry->n##center_freq1 = (c) ? (c)->center_freq1 : 0; \ __entry->n##freq1_offset = (c) ? (c)->freq1_offset : 0; \ __entry->n##center_freq2 = (c) ? (c)->center_freq2 : 0; \ - __entry->n##punctured = (c) ? (c)->punctured : 0; + __entry->n##punctured = (c) ? (c)->punctured : 0; \ + __entry->n##npca_pri_freq = (c) && (c)->npca_chan ? \ + (c)->npca_chan->center_freq : 0; \ + __entry->n##npca_punctured = (c) ? (c)->npca_punctured : 0; #define __CHANDEF_PR_FMT(n) \ - " " #n "(%d.%03d MHz,width:%d,center: %d.%03d/%d MHz, punct:0x%x)" + " " #n "(%d.%03d MHz,width:%d,center: %d.%03d/%d MHz, punct:0x%x, npca:%u, npca_punct:0x%x)" #define __CHANDEF_PR_ARG(n) \ __entry->n##control_freq, __entry->n##freq_offset, \ __entry->n##chan_width, __entry->n##center_freq1, \ __entry->n##freq1_offset, __entry->n##center_freq2, \ - __entry->n##punctured + __entry->n##punctured, __entry->n##npca_pri_freq, \ + __entry->n##npca_punctured #define CHANDEF_ENTRY __CHANDEF_ENTRY() #define CHANDEF_ASSIGN(c) __CHANDEF_ASSIGN(, c) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ea7f63e1fc17..c13b209fad47 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation * * Transmit and frame generation functions. */ @@ -2409,10 +2409,20 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, rcu_dereference(tmp_sdata->vif.bss_conf.chanctx_conf); } + if (!chanctx_conf) { + struct ieee80211_chanctx *ctx; + bool first = true; + + list_for_each_entry_rcu(ctx, &local->chanctx_list, list) { + if (!first) + goto fail_rcu; + chanctx_conf = &ctx->conf; + first = false; + } + } + if (chanctx_conf) chandef = &chanctx_conf->def; - else if (local->emulate_chanctx) - chandef = &local->hw.conf.chandef; else goto fail_rcu; @@ -2856,7 +2866,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, ret = -ENOTCONN; goto free; } - memcpy(hdr.addr3, nmi->wdev.u.nan.cluster_id, ETH_ALEN); + memcpy(hdr.addr3, nmi->u.nan.conf.cluster_id, ETH_ALEN); hdrlen = 24; break; } @@ -3918,7 +3928,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, * injected frames or EAPOL frames from the local station. */ if (unlikely(!(info->flags & IEEE80211_TX_CTL_INJECTED) && - ieee80211_is_data(hdr->frame_control) && + ieee80211_is_data_present(hdr->frame_control) && !ieee80211_vif_is_mesh(&tx.sdata->vif) && tx.sdata->vif.type != NL80211_IFTYPE_OCB && !is_multicast_ether_addr(hdr->addr1) && @@ -4652,7 +4662,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, { struct ieee80211_tx_info *info; struct ieee80211_local *local = sdata->local; - struct tid_ampdu_tx *tid_tx; + struct tid_ampdu_tx *tid_tx = NULL; struct sk_buff *seg, *next; unsigned int skbs = 0, len = 0; u16 queue; @@ -4672,7 +4682,9 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, ieee80211_aggr_check(sdata, sta, skb); tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + + if (sta) + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (tid_tx) { if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { /* fall back to non-offload slow path */ @@ -4720,8 +4732,11 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, } dev_sw_netstats_tx_add(dev, skbs, len); - sta->deflink.tx_stats.packets[queue] += skbs; - sta->deflink.tx_stats.bytes[queue] += len; + + if (sta) { + sta->deflink.tx_stats.packets[queue] += skbs; + sta->deflink.tx_stats.bytes[queue] += len; + } ieee80211_tpt_led_trig_tx(local, len); @@ -4733,19 +4748,35 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, kfree_skb(skb); } -netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, - struct net_device *dev) +static bool ieee80211_check_mcast_offload(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + + if ((ieee80211_vif_is_mld(&sdata->vif) && + (sdata->vif.type == NL80211_IFTYPE_AP && + !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX))) || + (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + !sdata->wdev.use_4addr)) + return false; + + if (!is_multicast_ether_addr(skb->data) || + !(sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_MCAST) || + sdata->control_port_protocol == ehdr->h_proto) + return false; + + return true; +} + +static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ethhdr *ehdr = (struct ethhdr *)skb->data; + struct ieee80211_link_data *link; struct ieee80211_key *key; struct sta_info *sta; - if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - rcu_read_lock(); if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { @@ -4753,14 +4784,30 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, goto out; } - if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || - !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || - sdata->control_port_protocol == ehdr->h_proto)) + /* + * If STA is invalid, use the multicast offload path when applicable. + * In AP mode, drop the frame if there are no associated stations; + * otherwise use the default link and multicast key for transmission. + */ + if (unlikely(IS_ERR_OR_NULL(sta) && + ieee80211_check_mcast_offload(sdata, skb))) { + sta = NULL; + if (ieee80211_vif_get_num_mcast_if(sdata) <= 0) { + /* No associated STAs - no need to send multicast frames. */ + kfree_skb(skb); + goto out; + } + link = &sdata->deflink; + key = rcu_dereference(link->default_multicast_key); + } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || + !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || + sdata->control_port_protocol == ehdr->h_proto)) { goto skip_offload; - - key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); + } else { + key = rcu_dereference(sta->ptk[sta->ptk_idx]); + if (!key) + key = rcu_dereference(sdata->default_unicast_key); + } if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) @@ -4774,6 +4821,30 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, ieee80211_subif_start_xmit(skb, dev); out: rcu_read_unlock(); +} + +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (unlikely(is_multicast_ether_addr(ehdr->h_dest) && + ieee80211_multicast_to_unicast(skb, dev))) { + struct sk_buff_head queue; + + __skb_queue_head_init(&queue); + ieee80211_convert_to_unicast(skb, dev, &queue); + while ((skb = __skb_dequeue(&queue))) + __ieee80211_subif_start_xmit_8023(skb, dev); + } else { + __ieee80211_subif_start_xmit_8023(skb, dev); + } return NETDEV_TX_OK; } @@ -5357,7 +5428,7 @@ static int ieee80211_beacon_protect(struct sk_buff *skb, int ieee80211_encrypt_tx_skb(struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = NULL; struct sk_buff *check_skb; struct ieee80211_tx_data tx; ieee80211_tx_result res; @@ -5372,7 +5443,14 @@ int ieee80211_encrypt_tx_skb(struct sk_buff *skb) __skb_queue_head_init(&tx.skbs); __skb_queue_tail(&tx.skbs, skb); - sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); + if (skb->dev) + sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); + else if (info->control.vif) + sdata = vif_to_sdata(info->control.vif); + + if (WARN_ON(!sdata)) + return -EINVAL; + tx.sdata = sdata; tx.local = sdata->local; res = ieee80211_tx_h_encrypt(&tx); @@ -6379,7 +6457,8 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, enum nl80211_band band; rcu_read_lock(); - if (sdata->vif.type == NL80211_IFTYPE_NAN) { + if (sdata->vif.type == NL80211_IFTYPE_NAN || + sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { band = NUM_NL80211_BANDS; } else if (!ieee80211_vif_is_mld(&sdata->vif)) { WARN_ON(link_id >= 0); diff --git a/net/mac80211/uhr.c b/net/mac80211/uhr.c index 2d8f5e5480ef..ca3b471b7981 100644 --- a/net/mac80211/uhr.c +++ b/net/mac80211/uhr.c @@ -15,7 +15,6 @@ ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata, struct link_sta_info *link_sta) { struct ieee80211_sta_uhr_cap *sta_uhr_cap = &link_sta->pub->uhr_cap; - bool from_ap; memset(sta_uhr_cap, 0, sizeof(*sta_uhr_cap)); @@ -23,8 +22,6 @@ ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata, return; sta_uhr_cap->has_uhr = true; - sta_uhr_cap->mac = uhr_cap->mac; - from_ap = sdata->vif.type == NL80211_IFTYPE_STATION; - sta_uhr_cap->phy = *ieee80211_uhr_phy_cap(uhr_cap, from_ap); + sta_uhr_cap->phy = uhr_cap->phy; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2529b01e2cd5..f6d4ae4127c8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2147,6 +2147,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) ieee80211_bss_info_change_notify(sdata, changed); } else if (!WARN_ON(!link)) { + if (link->conf->npca.enabled) + changed |= BSS_CHANGED_NPCA; + ieee80211_link_info_change_notify(sdata, link, changed); changed = BSS_CHANGED_ASSOC | @@ -2200,16 +2203,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) } break; case NL80211_IFTYPE_NAN: - res = ieee80211_reconfig_nan(sdata); - if (res < 0) { - ieee80211_handle_reconfig_failure(local); - return res; - } + WARN_ON(ieee80211_reconfig_nan(sdata)); break; case NL80211_IFTYPE_NAN_DATA: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_PD: /* nothing to do */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -3149,7 +3149,9 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, ext_nss_bw_supp = 0; /* - * Cf. IEEE 802.11 Table 9-250 + * Cf. IEEE 802.11-2020 Table 9-272 - Setting of the Supported Channel + * Width Set subfield and Extended NSS BW Support subfield at a STA + * transmitting the VHT Capabilities Information field * * We really just consider that because it's inefficient to connect * at a higher bandwidth than we'll actually be able to use. @@ -3814,13 +3816,6 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, conn->mode = IEEE80211_CONN_MODE_S1G; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - WARN_ON_ONCE(1); - /* keep c->width */ - conn->mode = IEEE80211_CONN_MODE_LEGACY; - conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; - break; } if (new_primary_width != NL80211_CHAN_WIDTH_20_NOHT) { @@ -3829,6 +3824,10 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, c->width = new_primary_width; } + /* whatever we do, downgrading removes NPCA */ + c->npca_chan = NULL; + c->npca_punctured = 0; + /* * With an 80 MHz channel, we might have the puncturing in the primary * 40 Mhz channel, but that's not valid when downgraded to 40 MHz width. @@ -3840,6 +3839,25 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, WARN_ON_ONCE(!cfg80211_chandef_valid(c)); } +enum nl80211_chan_width +ieee80211_sta_rx_bw_to_chan_width(enum ieee80211_sta_rx_bandwidth bw) +{ + switch (bw) { + case IEEE80211_STA_RX_BW_20: + return NL80211_CHAN_WIDTH_20; + case IEEE80211_STA_RX_BW_40: + return NL80211_CHAN_WIDTH_40; + case IEEE80211_STA_RX_BW_80: + return NL80211_CHAN_WIDTH_80; + case IEEE80211_STA_RX_BW_160: + return NL80211_CHAN_WIDTH_160; + case IEEE80211_STA_RX_BW_320: + return NL80211_CHAN_WIDTH_320; + default: + return NL80211_CHAN_WIDTH_20; + } +} + int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *csa_settings) { @@ -4235,7 +4253,10 @@ static int ieee80211_fill_ifcomb_params(struct ieee80211_local *local, struct iface_combination_params *params, const struct cfg80211_chan_def *chandef, - struct ieee80211_sub_if_data *sdata) + struct ieee80211_sub_if_data *sdata, + bool (*chanctx_filter)(struct ieee80211_chanctx *ctx, + void *filter_data), + void *filter_data) { struct ieee80211_sub_if_data *sdata_iter; struct ieee80211_chanctx *ctx; @@ -4256,6 +4277,10 @@ ieee80211_fill_ifcomb_params(struct ieee80211_local *local, cfg80211_chandef_compatible(chandef, &ctx->conf.def)) continue; + if (chanctx_filter && + chanctx_filter(ctx, filter_data)) + continue; + params->num_different_channels++; } @@ -4280,26 +4305,25 @@ ieee80211_fill_ifcomb_params(struct ieee80211_local *local, return total; } -int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, - const struct cfg80211_chan_def *chandef, - enum ieee80211_chanctx_mode chanmode, - u8 radar_detect, int radio_idx) +int ieee80211_check_combinations_ext(struct ieee80211_sub_if_data *sdata, + struct ieee80211_check_combinations_data *data) { - bool shared = chanmode == IEEE80211_CHANCTX_SHARED; + const struct cfg80211_chan_def *chandef = data->chandef; + bool shared = data->chanmode == IEEE80211_CHANCTX_SHARED; struct ieee80211_local *local = sdata->local; enum nl80211_iftype iftype = sdata->wdev.iftype; struct iface_combination_params params = { - .radar_detect = radar_detect, - .radio_idx = radio_idx, + .radar_detect = data->radar_detect, + .radio_idx = data->radio_idx, }; int total; lockdep_assert_wiphy(local->hw.wiphy); - if (WARN_ON(hweight32(radar_detect) > 1)) + if (WARN_ON(hweight32(data->radar_detect) > 1)) return -EINVAL; - if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED && + if (WARN_ON(chandef && data->chanmode == IEEE80211_CHANCTX_SHARED && !chandef->chan)) return -EINVAL; @@ -4318,7 +4342,7 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, /* Always allow software iftypes */ if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) { - if (radar_detect) + if (data->radar_detect) return -EINVAL; return 0; } @@ -4331,7 +4355,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, total = ieee80211_fill_ifcomb_params(local, ¶ms, shared ? chandef : NULL, - sdata); + sdata, + data->chanctx_filter, + data->filter_data); if (total == 1 && !params.radar_detect) return 0; @@ -4358,7 +4384,7 @@ int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx) lockdep_assert_wiphy(local->hw.wiphy); - ieee80211_fill_ifcomb_params(local, ¶ms, NULL, NULL); + ieee80211_fill_ifcomb_params(local, ¶ms, NULL, NULL, NULL, NULL); err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms, ieee80211_iter_max_chans, @@ -4619,8 +4645,7 @@ int ieee80211_put_uhr_cap(struct sk_buff *skb, if (!uhr_cap) return 0; - len = 2 + 1 + sizeof(struct ieee80211_uhr_cap) + - sizeof(struct ieee80211_uhr_cap_phy); + len = 2 + 1 + sizeof(struct ieee80211_uhr_cap); if (skb_tailroom(skb) < len) return -ENOBUFS; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index f3bb5a561a38..92a4e0450593 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -301,32 +301,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, return; } - /* finally set up the bandwidth */ - switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { - case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: - case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; - break; - default: - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; - - if (!(vht_cap->vht_mcs.tx_highest & - cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) - break; - - /* - * If this is non-zero, then it does support 160 MHz after all, - * in one form or the other. We don't distinguish here (or even - * above) between 160 and 80+80 yet. - */ - if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) - link_sta->cur_max_bandwidth = - IEEE80211_STA_RX_BW_160; - } - - if (sdata->vif.type != NL80211_IFTYPE_NAN) - link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); - /* * Work around the Cisco 9115 FW 17.3 bug by taking the min of * both reported MPDU lengths. @@ -357,319 +331,20 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, ieee80211_sta_recalc_aggregates(&link_sta->sta->sta); } -/* FIXME: move this to some better location - parses HE/EHT now */ -static enum ieee80211_sta_rx_bandwidth -__ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, - struct cfg80211_chan_def *chandef) -{ - unsigned int link_id = link_sta->link_id; - struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata; - struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; - struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; - struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap; - u32 cap_width; - - if (he_cap->has_he) { - enum nl80211_band band; - u8 info; - - if (chandef) { - band = chandef->chan->band; - } else { - struct ieee80211_bss_conf *link_conf; - - if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA || - sdata->vif.type == NL80211_IFTYPE_NAN)) - return IEEE80211_STA_RX_BW_20; - - rcu_read_lock(); - link_conf = rcu_dereference(sdata->vif.link_conf[link_id]); - band = link_conf->chanreq.oper.chan->band; - rcu_read_unlock(); - } - - if (eht_cap->has_eht && 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 (band == NL80211_BAND_2GHZ) { - if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) - return IEEE80211_STA_RX_BW_40; - return IEEE80211_STA_RX_BW_20; - } - - if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || - info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) - return IEEE80211_STA_RX_BW_160; - - if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) - return IEEE80211_STA_RX_BW_80; - - return IEEE80211_STA_RX_BW_20; - } - - if (!vht_cap->vht_supported) - return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? - IEEE80211_STA_RX_BW_40 : - IEEE80211_STA_RX_BW_20; - - cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; - - if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || - cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) - return IEEE80211_STA_RX_BW_160; - - /* - * If this is non-zero, then it does support 160 MHz after all, - * in one form or the other. We don't distinguish here (or even - * above) between 160 and 80+80 yet. - */ - if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) - return IEEE80211_STA_RX_BW_160; - - return IEEE80211_STA_RX_BW_80; -} - -enum ieee80211_sta_rx_bandwidth -_ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, - struct cfg80211_chan_def *chandef) -{ - /* - * With RX OMI, also pretend that the STA's capability changed. - * Of course this isn't really true, it didn't change, only our - * RX capability was changed by notifying RX OMI to the STA. - * The purpose, however, is to save power, and that requires - * changing also transmissions to the AP and the chanctx. The - * transmissions depend on link_sta->bandwidth which is set in - * _ieee80211_sta_cur_vht_bw() below, but the chanctx depends - * on the result of this function which is also called by - * _ieee80211_sta_cur_vht_bw(), so we need to do that here as - * well. This is sufficient for the steady state, but during - * the transition we already need to change TX/RX separately, - * so _ieee80211_sta_cur_vht_bw() below applies the _tx one. - */ - return min(__ieee80211_sta_cap_rx_bw(link_sta, chandef), - link_sta->rx_omi_bw_rx); -} - -enum nl80211_chan_width -ieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta) -{ - struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; - u32 cap_width; - - if (!vht_cap->vht_supported) { - if (!link_sta->pub->ht_cap.ht_supported) - return NL80211_CHAN_WIDTH_20_NOHT; - - return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? - NL80211_CHAN_WIDTH_40 : NL80211_CHAN_WIDTH_20; - } - - cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; - - if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) - return NL80211_CHAN_WIDTH_160; - else if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) - return NL80211_CHAN_WIDTH_80P80; - - return NL80211_CHAN_WIDTH_80; -} - -enum nl80211_chan_width -ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *link_sta) -{ - enum ieee80211_sta_rx_bandwidth cur_bw = - link_sta->pub->bandwidth; - struct ieee80211_sta_vht_cap *vht_cap = - &link_sta->pub->vht_cap; - u32 cap_width; - - switch (cur_bw) { - case IEEE80211_STA_RX_BW_20: - if (!link_sta->pub->ht_cap.ht_supported) - return NL80211_CHAN_WIDTH_20_NOHT; - else - return NL80211_CHAN_WIDTH_20; - case IEEE80211_STA_RX_BW_40: - return NL80211_CHAN_WIDTH_40; - case IEEE80211_STA_RX_BW_80: - return NL80211_CHAN_WIDTH_80; - case IEEE80211_STA_RX_BW_160: - cap_width = - vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; - - if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) - return NL80211_CHAN_WIDTH_160; - - return NL80211_CHAN_WIDTH_80P80; - default: - return NL80211_CHAN_WIDTH_20; - } -} - -/* FIXME: rename/move - this deals with everything not just VHT */ -enum ieee80211_sta_rx_bandwidth -_ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta, - struct cfg80211_chan_def *chandef) -{ - struct sta_info *sta = link_sta->sta; - enum nl80211_chan_width bss_width; - enum ieee80211_sta_rx_bandwidth bw; - - if (chandef) { - bss_width = chandef->width; - } else { - struct ieee80211_bss_conf *link_conf; - - /* NAN operates on multiple channels so a chandef must be given */ - if (WARN_ON_ONCE(sta->sdata->vif.type == NL80211_IFTYPE_NAN || - sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) - return IEEE80211_STA_RX_BW_20; - - rcu_read_lock(); - link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]); - if (WARN_ON_ONCE(!link_conf)) { - rcu_read_unlock(); - return IEEE80211_STA_RX_BW_20; - } - bss_width = link_conf->chanreq.oper.width; - rcu_read_unlock(); - } - - /* intentionally do not take rx_bw_omi_rx into account */ - bw = __ieee80211_sta_cap_rx_bw(link_sta, chandef); - bw = min(bw, link_sta->cur_max_bandwidth); - /* but do apply rx_omi_bw_tx */ - bw = min(bw, link_sta->rx_omi_bw_tx); - - /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of - * IEEE80211-2016 specification makes higher bandwidth operation - * possible on the TDLS link if the peers have wider bandwidth - * capability. - * - * However, in this case, and only if the TDLS peer is authorized, - * limit to the tdls_chandef so that the configuration here isn't - * wider than what's actually requested on the channel context. - */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && - test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) && - test_sta_flag(sta, WLAN_STA_AUTHORIZED) && - sta->tdls_chandef.chan) - bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); - else - bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); - - return bw; -} - -void ieee80211_sta_init_nss(struct link_sta_info *link_sta) -{ - u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; - bool support_160; - - if (link_sta->pub->eht_cap.has_eht) { - int i; - const u8 *rx_nss_mcs = (void *)&link_sta->pub->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 (link_sta->pub->he_cap.has_he) { - int i; - u8 rx_mcs_80 = 0, rx_mcs_160 = 0; - const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; - u16 mcs_160_map = - le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); - u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); - - for (i = 7; i >= 0; i--) { - u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; - - if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { - rx_mcs_160 = i + 1; - break; - } - } - for (i = 7; i >= 0; i--) { - u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; - - if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { - rx_mcs_80 = i + 1; - break; - } - } - - support_160 = he_cap->he_cap_elem.phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - - if (support_160) - he_rx_nss = min(rx_mcs_80, rx_mcs_160); - else - he_rx_nss = rx_mcs_80; - } - - if (link_sta->pub->ht_cap.ht_supported) { - if (link_sta->pub->ht_cap.mcs.rx_mask[0]) - ht_rx_nss++; - if (link_sta->pub->ht_cap.mcs.rx_mask[1]) - ht_rx_nss++; - if (link_sta->pub->ht_cap.mcs.rx_mask[2]) - ht_rx_nss++; - if (link_sta->pub->ht_cap.mcs.rx_mask[3]) - ht_rx_nss++; - /* FIXME: consider rx_highest? */ - } - - if (link_sta->pub->vht_cap.vht_supported) { - int i; - u16 rx_mcs_map; - - rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map); - - for (i = 7; i >= 0; i--) { - u8 mcs = (rx_mcs_map >> (2 * i)) & 3; - - if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { - vht_rx_nss = i + 1; - break; - } - } - /* FIXME: consider rx_highest? */ - } - - 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); - rx_nss = max_t(u8, 1, rx_nss); - link_sta->capa_nss = rx_nss; - - /* that shouldn't be set yet, but we can handle it anyway */ - if (link_sta->op_mode_nss) - link_sta->pub->rx_nss = - min_t(u8, rx_nss, link_sta->op_mode_nss); - else - link_sta->pub->rx_nss = rx_nss; -} - u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct link_sta_info *link_sta, u8 opmode, enum nl80211_band band) { enum ieee80211_sta_rx_bandwidth new_bw; struct sta_opmode_info sta_opmode = {}; + struct ieee80211_link_data *link; u32 changed = 0; u8 nss; + link = sdata_dereference(sdata->link[link_sta->link_id], sdata); + if (WARN_ON(!link)) + return 0; + /* ignore - no support for BF yet */ if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) return 0; @@ -698,28 +373,29 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; + link_sta->op_mode_bw = IEEE80211_STA_RX_BW_20; break; case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; + link_sta->op_mode_bw = IEEE80211_STA_RX_BW_40; break; case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80) - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; + link_sta->op_mode_bw = IEEE80211_STA_RX_BW_160; else - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; + link_sta->op_mode_bw = IEEE80211_STA_RX_BW_80; break; case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: /* legacy only, no longer used by newer spec */ - link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; + link_sta->op_mode_bw = IEEE80211_STA_RX_BW_160; break; } - new_bw = ieee80211_sta_cur_vht_bw(link_sta); + new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper, + IEEE80211_STA_BW_TX_TO_STA); if (new_bw != link_sta->pub->bandwidth) { link_sta->pub->bandwidth = new_bw; - sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(link_sta); + sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(new_bw); changed |= IEEE80211_RC_BW_CHANGED; sta_opmode.changed |= STA_OPMODE_MAX_BW_CHANGED; } diff --git a/net/mctp/af_mctp.c b/net/mctp/af_mctp.c index 209a963112e3..8af5e2b3c8d1 100644 --- a/net/mctp/af_mctp.c +++ b/net/mctp/af_mctp.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -405,7 +406,7 @@ static int mctp_setsockopt(struct socket *sock, int level, int optname, } static int mctp_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk); int len, val; @@ -413,14 +414,13 @@ static int mctp_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_MCTP) return -EINVAL; - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; if (optname == MCTP_OPT_ADDR_EXT) { if (len != sizeof(int)) return -EINVAL; val = !!msk->addr_ext; - if (copy_to_user(optval, &val, len)) + if (copy_to_iter(&val, len, &opt->iter_out) != len) return -EFAULT; return 0; } @@ -639,7 +639,7 @@ static const struct proto_ops mctp_dgram_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = mctp_setsockopt, - .getsockopt = mctp_getsockopt, + .getsockopt_iter = mctp_getsockopt, .sendmsg = mctp_sendmsg, .recvmsg = mctp_recvmsg, .mmap = sock_no_mmap, diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c index e4b230ef6099..c92e3abb40d7 100644 --- a/net/mctp/test/route-test.c +++ b/net/mctp/test/route-test.c @@ -859,12 +859,6 @@ static void mctp_test_route_input_cloned_frag(struct kunit *test) skb[0]->data[4] = 0; skb[3]->data[4] = 0; - skb_dump("pkt1 ", skb[0], false); - skb_dump("pkt2 ", skb[1], false); - skb_dump("pkt3 ", skb[2], false); - skb_dump("pkt4 ", skb[3], false); - skb_dump("pkt5 ", skb[4], false); - for (int i = 0; i < 5; i++) { KUNIT_EXPECT_EQ(test, refcount_read(&skb[i]->users), 1); /* Take a reference so we can check refcounts at the end */ diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 26340a7306b5..ca504d9626cf 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -2221,12 +2221,10 @@ static bool mpls_rt_uses_dev(struct mpls_route *rt, static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) { + struct mpls_route __rcu **platform_label; const struct nlmsghdr *nlh = cb->nlh; struct net *net = sock_net(skb->sk); - struct mpls_route __rcu **platform_label; - struct fib_dump_filter filter = { - .rtnl_held = false, - }; + struct fib_dump_filter filter = {}; unsigned int flags = NLM_F_MULTI; size_t platform_labels; unsigned int index; diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c index d96130e49942..63c5747f0f63 100644 --- a/net/mptcp/ctrl.c +++ b/net/mptcp/ctrl.c @@ -32,12 +32,13 @@ struct mptcp_pernet { unsigned int close_timeout; unsigned int stale_loss_cnt; atomic_t active_disable_times; - u8 syn_retrans_before_tcp_fallback; unsigned long active_disable_stamp; + u8 syn_retrans_before_tcp_fallback; u8 mptcp_enabled; u8 checksum_enabled; u8 allow_join_initial_addr_port; u8 pm_type; + u8 add_addr_v6_port_drop_ts; char scheduler[MPTCP_SCHED_NAME_MAX]; char path_manager[MPTCP_PM_NAME_MAX]; }; @@ -94,6 +95,11 @@ const char *mptcp_get_scheduler(const struct net *net) return mptcp_get_pernet(net)->scheduler; } +unsigned int mptcp_add_addr_v6_port_drop_ts(const struct net *net) +{ + return READ_ONCE(mptcp_get_pernet(net)->add_addr_v6_port_drop_ts); +} + static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet) { pernet->mptcp_enabled = 1; @@ -108,6 +114,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet) pernet->pm_type = MPTCP_PM_TYPE_KERNEL; strscpy(pernet->scheduler, "default", sizeof(pernet->scheduler)); strscpy(pernet->path_manager, "kernel", sizeof(pernet->path_manager)); + pernet->add_addr_v6_port_drop_ts = 1; } #ifdef CONFIG_SYSCTL @@ -362,6 +369,14 @@ static struct ctl_table mptcp_sysctl_table[] = { .mode = 0444, .proc_handler = proc_available_path_managers, }, + { + .procname = "add_addr_v6_port_drop_ts", + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE + }, }; static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet) @@ -389,6 +404,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet) table[10].data = &pernet->syn_retrans_before_tcp_fallback; table[11].data = &pernet->path_manager; /* table[12] is for available_path_managers which is read-only info */ + table[13].data = &pernet->add_addr_v6_port_drop_ts; hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table, ARRAY_SIZE(mptcp_sysctl_table)); diff --git a/net/mptcp/options.c b/net/mptcp/options.c index b3ea7854818f..dff3fd5d3b55 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -447,8 +447,7 @@ static void clear_3rdack_retransmission(struct sock *sk) } static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb, - bool snd_data_fin_enable, - unsigned int *size, + bool snd_data_fin_enable, int *size, struct mptcp_out_options *opts) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); @@ -560,8 +559,7 @@ static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow, } static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, - bool snd_data_fin_enable, - unsigned int *size, + bool snd_data_fin_enable, int *size, struct mptcp_out_options *opts) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); @@ -658,17 +656,17 @@ static u64 add_addr_generate_hmac(u64 key1, u64 key2, return get_unaligned_be64(&hmac[SHA256_DIGEST_SIZE - sizeof(u64)]); } -static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *skb, - unsigned int *size, +static bool mptcp_established_options_add_addr(struct sock *sk, + struct sk_buff *skb, int *size, unsigned int remaining, + bool has_ts, struct mptcp_out_options *opts) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_sock *msk = mptcp_sk(subflow->conn); - unsigned int opt_size = *size; struct mptcp_addr_info addr; + bool drop_ts = has_ts; bool echo; - int len; /* add addr will strip the existing options, be sure to avoid breaking * MPC/MPJ handshakes @@ -676,21 +674,14 @@ 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)) || !skb || !skb_is_tcp_pure_ack(skb) || - !mptcp_pm_add_addr_signal(msk, opt_size, remaining, &addr, &echo)) + !mptcp_pm_add_addr_signal(msk, size, remaining, &addr, &echo, + &drop_ts)) return false; - remaining += opt_size; - - len = mptcp_add_addr_len(addr.family, echo, !!addr.port); - if (remaining < len) - return false; - - *size = len; pr_debug("drop other suboptions\n"); - opts->suboptions = 0; - *size -= opt_size; + opts->suboptions = OPTION_MPTCP_ADD_ADDR; + opts->drop_ts = drop_ts; opts->addr = addr; - opts->suboptions |= OPTION_MPTCP_ADD_ADDR; if (!echo) { MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ADDADDRTX); opts->ahmac = add_addr_generate_hmac(READ_ONCE(msk->local_key), @@ -706,27 +697,19 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff * return true; } -static bool mptcp_established_options_rm_addr(struct sock *sk, - unsigned int *size, +static bool mptcp_established_options_rm_addr(struct sock *sk, int *size, unsigned int remaining, struct mptcp_out_options *opts) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_sock *msk = mptcp_sk(subflow->conn); struct mptcp_rm_list rm_list; - int i, len; + int i; if (!mptcp_pm_should_rm_signal(msk) || - !(mptcp_pm_rm_addr_signal(msk, remaining, &rm_list))) + !(mptcp_pm_rm_addr_signal(msk, remaining, &rm_list, size))) return false; - len = mptcp_rm_addr_len(&rm_list); - if (len < 0) - return false; - if (remaining < len) - return false; - - *size = len; opts->suboptions |= OPTION_MPTCP_RM_ADDR; opts->rm_list = rm_list; @@ -736,8 +719,7 @@ static bool mptcp_established_options_rm_addr(struct sock *sk, return true; } -static bool mptcp_established_options_mp_prio(struct sock *sk, - unsigned int *size, +static bool mptcp_established_options_mp_prio(struct sock *sk, int *size, unsigned int remaining, struct mptcp_out_options *opts) { @@ -762,8 +744,8 @@ static bool mptcp_established_options_mp_prio(struct sock *sk, return true; } -static noinline bool mptcp_established_options_rst(struct sock *sk, struct sk_buff *skb, - unsigned int *size, +static noinline bool mptcp_established_options_rst(struct sock *sk, + int *size, unsigned int remaining, struct mptcp_out_options *opts) { @@ -781,8 +763,7 @@ static noinline bool mptcp_established_options_rst(struct sock *sk, struct sk_bu return true; } -static bool mptcp_established_options_fastclose(struct sock *sk, - unsigned int *size, +static bool mptcp_established_options_fastclose(struct sock *sk, int *size, unsigned int remaining, struct mptcp_out_options *opts) { @@ -804,8 +785,7 @@ static bool mptcp_established_options_fastclose(struct sock *sk, return true; } -static bool mptcp_established_options_mp_fail(struct sock *sk, - unsigned int *size, +static bool mptcp_established_options_mp_fail(struct sock *sk, int *size, unsigned int remaining, struct mptcp_out_options *opts) { @@ -827,15 +807,16 @@ static bool mptcp_established_options_mp_fail(struct sock *sk, return true; } -bool mptcp_established_options(struct sock *sk, struct sk_buff *skb, - unsigned int *size, unsigned int remaining, - struct mptcp_out_options *opts) +int mptcp_established_options(struct sock *sk, struct sk_buff *skb, + unsigned int remaining, bool has_ts, + struct mptcp_out_options *opts) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_sock *msk = mptcp_sk(subflow->conn); - unsigned int opt_size = 0; + int total_size = 0; bool snd_data_fin; bool ret = false; + int opt_size = 0; opts->suboptions = 0; @@ -843,34 +824,34 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb, * option space. */ if (unlikely(__mptcp_check_fallback(msk) && !mptcp_check_infinite_map(skb))) - return true; + return 0; if (unlikely(skb && TCP_SKB_CB(skb)->tcp_flags & TCPHDR_RST)) { if (mptcp_established_options_fastclose(sk, &opt_size, remaining, opts) || mptcp_established_options_mp_fail(sk, &opt_size, remaining, opts)) { - *size += opt_size; + total_size += opt_size; remaining -= opt_size; } /* 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; + if (mptcp_established_options_rst(sk, &opt_size, remaining, opts)) { + total_size += opt_size; remaining -= opt_size; } - return true; + return total_size; } snd_data_fin = mptcp_data_fin_enabled(msk); if (mptcp_established_options_mp(sk, skb, snd_data_fin, &opt_size, opts)) ret = true; else if (mptcp_established_options_dss(sk, skb, snd_data_fin, &opt_size, opts)) { - unsigned int mp_fail_size; + int mp_fail_size; ret = true; if (mptcp_established_options_mp_fail(sk, &mp_fail_size, remaining - opt_size, opts)) { - *size += opt_size + mp_fail_size; + total_size += opt_size + mp_fail_size; remaining -= opt_size - mp_fail_size; - return true; + return total_size; } } @@ -878,27 +859,28 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb, * TCP option space would be fatal */ if (WARN_ON_ONCE(opt_size > remaining)) - return false; + return -1; - *size += opt_size; + total_size += opt_size; remaining -= opt_size; - if (mptcp_established_options_add_addr(sk, skb, &opt_size, remaining, opts)) { - *size += opt_size; + if (mptcp_established_options_add_addr(sk, skb, &opt_size, remaining, + has_ts, opts)) { + total_size += opt_size; remaining -= opt_size; ret = true; } else if (mptcp_established_options_rm_addr(sk, &opt_size, remaining, opts)) { - *size += opt_size; + total_size += opt_size; remaining -= opt_size; ret = true; } if (mptcp_established_options_mp_prio(sk, &opt_size, remaining, opts)) { - *size += opt_size; + total_size += opt_size; remaining -= opt_size; ret = true; } - return ret; + return ret ? total_size : -1; } bool mptcp_synack_options(const struct request_sock *req, unsigned int *size, @@ -1201,7 +1183,6 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ADDADDR); } else { mptcp_pm_add_addr_echoed(msk, &mp_opt.addr); - mptcp_pm_del_add_timer(msk, &mp_opt.addr, true); MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ECHOADD); } diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 470501470fe5..6afd39aea110 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -12,12 +12,12 @@ #define ADD_ADDR_RETRANS_MAX 3 -struct mptcp_pm_add_entry { +struct mptcp_pm_add_addr { struct list_head list; struct mptcp_addr_info addr; u8 retrans_times; bool timer_done; - struct timer_list add_timer; + struct timer_list timer; struct mptcp_sock *sock; struct rcu_head rcu; }; @@ -115,14 +115,14 @@ static bool mptcp_pm_is_init_remote_addr(struct mptcp_sock *msk, return mptcp_addresses_equal(&mpc_remote, remote, remote->port); } -bool mptcp_lookup_subflow_by_saddr(const struct list_head *list, - const struct mptcp_addr_info *saddr) +bool mptcp_pm_has_subflow_saddr(const struct mptcp_sock *msk, + const struct mptcp_addr_info *saddr) { struct mptcp_subflow_context *subflow; struct mptcp_addr_info cur; struct sock_common *skc; - list_for_each_entry(subflow, list, node) { + mptcp_for_each_subflow(msk, subflow) { skc = (struct sock_common *)mptcp_subflow_tcp_sock(subflow); mptcp_local_address(skc, &cur); @@ -133,11 +133,11 @@ bool mptcp_lookup_subflow_by_saddr(const struct list_head *list, return false; } -static struct mptcp_pm_add_entry * -mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk, - const struct mptcp_addr_info *addr) +static struct mptcp_pm_add_addr * +mptcp_pm_announced_lookup(const struct mptcp_sock *msk, + const struct mptcp_addr_info *addr) { - struct mptcp_pm_add_entry *entry; + struct mptcp_pm_add_addr *entry; lockdep_assert_held(&msk->pm.lock); @@ -149,26 +149,60 @@ mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk, return NULL; } -bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk, - const struct mptcp_addr_info *addr) +static struct mptcp_pm_add_addr * +mptcp_pm_announced_del_timer(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr, bool check_id) { - struct mptcp_pm_add_entry *entry; + struct sock *sk = (struct sock *)msk; + struct mptcp_pm_add_addr *entry; + bool stop_timer = false; + + rcu_read_lock(); + + spin_lock_bh(&msk->pm.lock); + entry = mptcp_pm_announced_lookup(msk, addr); + if (entry && (!check_id || entry->addr.id == addr->id)) { + entry->retrans_times = ADD_ADDR_RETRANS_MAX; + stop_timer = true; + } + if (!check_id && entry) + list_del(&entry->list); + spin_unlock_bh(&msk->pm.lock); + + /* Note: entry might have been removed by another thread. + * We hold rcu_read_lock() to ensure it is not freed under us. + */ + if (stop_timer) { + if (check_id) + sk_stop_timer(sk, &entry->timer); + else + sk_stop_timer_sync(sk, &entry->timer); + } + + rcu_read_unlock(); + return entry; +} + +bool mptcp_pm_announced_remove(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr) +{ + struct mptcp_pm_add_addr *entry; bool ret; - entry = mptcp_pm_del_add_timer(msk, addr, false); + entry = mptcp_pm_announced_del_timer(msk, addr, false); ret = entry; kfree_rcu(entry, rcu); return ret; } -bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk) +bool mptcp_pm_announced_has_ssk(struct mptcp_sock *msk, const struct sock *ssk) { - struct mptcp_pm_add_entry *entry; + struct mptcp_pm_add_addr *entry; struct mptcp_addr_info saddr; bool ret = false; - mptcp_local_address((struct sock_common *)sk, &saddr); + mptcp_local_address((struct sock_common *)ssk, &saddr); spin_lock_bh(&msk->pm.lock); list_for_each_entry(entry, &msk->pm.anno_list, list) { @@ -226,6 +260,7 @@ static bool subflow_in_rm_list(const struct mptcp_subflow_context *subflow, return false; } +static void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk); static void mptcp_pm_addr_send_ack_avoid_list(struct mptcp_sock *msk, const struct mptcp_rm_list *rm_list) @@ -338,10 +373,10 @@ static unsigned int mptcp_adjust_add_addr_timeout(struct mptcp_sock *msk) return rto; } -static void mptcp_pm_add_timer(struct timer_list *timer) +static void mptcp_pm_add_addr_timer(struct timer_list *timer) { - struct mptcp_pm_add_entry *entry = timer_container_of(entry, timer, - add_timer); + struct mptcp_pm_add_addr *entry = timer_container_of(entry, timer, + timer); struct mptcp_sock *msk = entry->sock; struct sock *sk = (struct sock *)msk; unsigned int timeout = 0; @@ -364,7 +399,7 @@ static void mptcp_pm_add_timer(struct timer_list *timer) spin_lock_bh(&msk->pm.lock); - /* The cancel path (mptcp_pm_del_add_timer()) can race with this + /* The cancel path (mptcp_pm_announced_del_timer()) can race with this * callback. Once cancel updates retrans_times to MAX, suppress further * retransmissions here. If this callback acquires pm.lock first, one * final transmit attempt is still possible. @@ -397,51 +432,16 @@ static void mptcp_pm_add_timer(struct timer_list *timer) sock_put(sk); } -struct mptcp_pm_add_entry * -mptcp_pm_del_add_timer(struct mptcp_sock *msk, - const struct mptcp_addr_info *addr, bool check_id) -{ - struct mptcp_pm_add_entry *entry; - struct sock *sk = (struct sock *)msk; - bool stop_timer = false; - - rcu_read_lock(); - - spin_lock_bh(&msk->pm.lock); - entry = mptcp_lookup_anno_list_by_saddr(msk, addr); - if (entry && (!check_id || entry->addr.id == addr->id)) { - entry->retrans_times = ADD_ADDR_RETRANS_MAX; - stop_timer = true; - } - if (!check_id && entry) - list_del(&entry->list); - spin_unlock_bh(&msk->pm.lock); - - /* Note: entry might have been removed by another thread. - * We hold rcu_read_lock() to ensure it is not freed under us. - */ - if (stop_timer) { - if (check_id) - sk_stop_timer(sk, &entry->add_timer); - else - sk_stop_timer_sync(sk, &entry->add_timer); - } - - rcu_read_unlock(); - return entry; -} - -bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk, +bool mptcp_pm_announced_alloc(struct mptcp_sock *msk, const struct mptcp_addr_info *addr) { - struct mptcp_pm_add_entry *add_entry = NULL; + struct mptcp_pm_add_addr *add_entry = NULL; struct sock *sk = (struct sock *)msk; unsigned int timeout; lockdep_assert_held(&msk->pm.lock); - add_entry = mptcp_lookup_anno_list_by_saddr(msk, addr); - + add_entry = mptcp_pm_announced_lookup(msk, addr); if (add_entry) { if (WARN_ON_ONCE(mptcp_pm_is_kernel(msk))) return false; @@ -459,19 +459,19 @@ bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk, add_entry->sock = msk; add_entry->retrans_times = 0; - timer_setup(&add_entry->add_timer, mptcp_pm_add_timer, 0); + timer_setup(&add_entry->timer, mptcp_pm_add_addr_timer, 0); reset_timer: add_entry->timer_done = false; timeout = mptcp_adjust_add_addr_timeout(msk); if (timeout) - sk_reset_timer(sk, &add_entry->add_timer, jiffies + timeout); + sk_reset_timer(sk, &add_entry->timer, jiffies + timeout); return true; } -static void mptcp_pm_free_anno_list(struct mptcp_sock *msk) +static void mptcp_pm_free_announced_list(struct mptcp_sock *msk) { - struct mptcp_pm_add_entry *entry, *tmp; + struct mptcp_pm_add_addr *entry, *tmp; struct sock *sk = (struct sock *)msk; LIST_HEAD(free_list); @@ -483,7 +483,7 @@ static void mptcp_pm_free_anno_list(struct mptcp_sock *msk) list_for_each_entry_safe(entry, tmp, &free_list, list) { if (!entry->timer_done) - sk_stop_timer_sync(sk, &entry->add_timer); + sk_stop_timer_sync(sk, &entry->timer); kfree_rcu(entry, rcu); } } @@ -730,21 +730,25 @@ void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk, const struct mptcp_addr_info *addr) { struct mptcp_pm_data *pm = &msk->pm; + struct mptcp_pm_add_addr *entry; pr_debug("msk=%p\n", msk); - if (!READ_ONCE(pm->work_pending)) + entry = mptcp_pm_announced_del_timer(msk, addr, true); + + if (!entry || !READ_ONCE(pm->work_pending)) return; spin_lock_bh(&pm->lock); - if (mptcp_lookup_anno_list_by_saddr(msk, addr) && READ_ONCE(pm->work_pending)) + if (READ_ONCE(pm->work_pending)) mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED); spin_unlock_bh(&pm->lock); } -void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk) +/* To be called while pm->lock is held */ +static void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk) { if (!mptcp_pm_should_add_signal(msk)) return; @@ -887,13 +891,29 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq) } } -bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, - unsigned int remaining, - struct mptcp_addr_info *addr, bool *echo) +static int mptcp_add_addr_len(int family, bool echo, bool port) +{ + int len = TCPOLEN_MPTCP_ADD_ADDR_BASE; + + if (family == AF_INET6) + len = TCPOLEN_MPTCP_ADD_ADDR6_BASE; + if (!echo) + len += MPTCPOPT_THMAC_LEN; + /* account for 2 trailing 'nop' options */ + if (port) + len += TCPOLEN_MPTCP_PORT_LEN + TCPOLEN_MPTCP_PORT_ALIGN; + + return len; +} + +bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, int *size, int remaining, + struct mptcp_addr_info *addr, bool *echo, + bool *drop_ts) { bool skip_add_addr = false; - int ret = false; + bool ret = false; u8 add_addr; + int len = 0; u8 family; bool port; @@ -907,7 +927,7 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, * plain dup-ack from TCP perspective. The other MPTCP-relevant info, * if any, will be carried by the 'original' TCP ack */ - remaining += opt_size; + len -= *size; *echo = mptcp_pm_should_add_signal_echo(msk); if (*echo) { @@ -922,9 +942,17 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, family = msk->pm.local.family; } - if (remaining < mptcp_add_addr_len(family, *echo, port)) { + len += mptcp_add_addr_len(family, *echo, port); + if (len > remaining) { struct net *net = sock_net((struct sock *)msk); + if (*drop_ts && mptcp_add_addr_v6_port_drop_ts(net)) { + /* OK without TCP Timestamps? */ + len -= TCPOLEN_TSTAMP_ALIGNED; + if (len <= remaining) + goto enough_space; + } + if (*echo) { MPTCP_INC_STATS(net, MPTCP_MIB_ECHOADDTXDROP); } else { @@ -934,7 +962,11 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, goto drop_signal_mark; } + *drop_ts = false; + +enough_space: ret = true; + *size = len; drop_signal_mark: WRITE_ONCE(msk->pm.addr_signal, add_addr); @@ -947,14 +979,22 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, * let the PM state machine progress. */ if (skip_add_addr) { - mptcp_pm_del_add_timer(msk, addr, true); + mptcp_pm_announced_del_timer(msk, addr, true); mptcp_pm_subflow_established(msk); } return ret; } +static int mptcp_rm_addr_len(const struct mptcp_rm_list *rm_list) +{ + if (rm_list->nr == 0 || rm_list->nr > MPTCP_RM_IDS_MAX) + return -EINVAL; + + return TCPOLEN_MPTCP_RM_ADDR_BASE + roundup(rm_list->nr - 1, 4) + 1; +} + bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, - struct mptcp_rm_list *rm_list) + struct mptcp_rm_list *rm_list, int *size) { int ret = false, len; u8 rm_addr; @@ -974,6 +1014,7 @@ bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, if (remaining < len) goto out_unlock; + *size = len; *rm_list = msk->pm.rm_list_tx; WRITE_ONCE(msk->pm.addr_signal, rm_addr); ret = true; @@ -1102,7 +1143,7 @@ void mptcp_pm_worker(struct mptcp_sock *msk) void mptcp_pm_destroy(struct mptcp_sock *msk) { - mptcp_pm_free_anno_list(msk); + mptcp_pm_free_announced_list(msk); if (mptcp_pm_is_userspace(msk)) mptcp_userspace_pm_free_local_addr_list(msk); diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index fc818b63752e..424f1a7f9248 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -30,6 +30,7 @@ struct pm_nl_pernet { }; #define MPTCP_PM_ADDR_MAX 8 +#define MPTCP_PM_SUBFLOWS_MAX 64 static struct pm_nl_pernet *pm_nl_get_pernet(const struct net *net) { @@ -95,13 +96,13 @@ u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_sock *msk) } EXPORT_SYMBOL_GPL(mptcp_pm_get_limit_extra_subflows); -static bool lookup_subflow_by_daddr(const struct list_head *list, - const struct mptcp_addr_info *daddr) +static bool has_subflow_daddr(const struct mptcp_sock *msk, + const struct mptcp_addr_info *daddr) { struct mptcp_subflow_context *subflow; struct mptcp_addr_info cur; - list_for_each_entry(subflow, list, node) { + mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); if (!((1 << inet_sk_state_load(ssk)) & @@ -201,7 +202,8 @@ fill_remote_addr(struct mptcp_sock *msk, struct mptcp_addr_info *local, static unsigned int fill_remote_addresses_fullmesh(struct mptcp_sock *msk, struct mptcp_addr_info *local, - struct mptcp_addr_info *addrs) + struct mptcp_addr_info *addrs, + int addrs_size) { u8 limit_extra_subflows = mptcp_pm_get_limit_extra_subflows(msk); bool deny_id0 = READ_ONCE(msk->pm.remote_deny_join_id0); @@ -236,7 +238,8 @@ fill_remote_addresses_fullmesh(struct mptcp_sock *msk, msk->pm.extra_subflows++; i++; - if (msk->pm.extra_subflows >= limit_extra_subflows) + if (msk->pm.extra_subflows >= limit_extra_subflows || + i == addrs_size) break; } @@ -248,7 +251,8 @@ fill_remote_addresses_fullmesh(struct mptcp_sock *msk, */ static unsigned int fill_remote_addresses_vec(struct mptcp_sock *msk, struct mptcp_addr_info *local, - bool fullmesh, struct mptcp_addr_info *addrs) + bool fullmesh, struct mptcp_addr_info *addrs, + int addrs_size) { /* Non-fullmesh: fill in the single entry corresponding to the primary * MPC subflow remote address, and return 1, corresponding to 1 entry. @@ -257,7 +261,7 @@ fill_remote_addresses_vec(struct mptcp_sock *msk, struct mptcp_addr_info *local, return fill_remote_addr(msk, local, addrs); /* Fullmesh endpoint: fill all possible remote addresses */ - return fill_remote_addresses_fullmesh(msk, local, addrs); + return fill_remote_addresses_fullmesh(msk, local, addrs, addrs_size); } static struct mptcp_pm_addr_entry * @@ -370,7 +374,7 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk) /* If the alloc fails, we are on memory pressure, not worth * continuing, and trying to create subflows. */ - if (!mptcp_pm_alloc_anno_list(msk, &local.addr)) + if (!mptcp_pm_announced_alloc(msk, &local.addr)) return; __clear_bit(endp_id, msk->pm.id_avail_bitmap); @@ -410,7 +414,8 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk) else /* local_addr_used is not decr for ID 0 */ msk->pm.local_addr_used++; - nr = fill_remote_addresses_vec(msk, &local.addr, fullmesh, addrs); + nr = fill_remote_addresses_vec(msk, &local.addr, fullmesh, + addrs, ARRAY_SIZE(addrs)); if (nr == 0) continue; @@ -447,6 +452,7 @@ static unsigned int fill_local_addresses_vec_fullmesh(struct mptcp_sock *msk, struct mptcp_addr_info *remote, struct mptcp_pm_local *locals, + int locals_size, bool c_flag_case) { u8 limit_extra_subflows = mptcp_pm_get_limit_extra_subflows(msk); @@ -488,7 +494,8 @@ fill_local_addresses_vec_fullmesh(struct mptcp_sock *msk, msk->pm.extra_subflows++; i++; - if (msk->pm.extra_subflows >= limit_extra_subflows) + if (msk->pm.extra_subflows >= limit_extra_subflows || + i == locals_size) break; } rcu_read_unlock(); @@ -559,7 +566,8 @@ fill_local_laminar_endp(struct mptcp_sock *msk, struct mptcp_addr_info *remote, static unsigned int fill_local_addresses_vec_c_flag(struct mptcp_sock *msk, struct mptcp_addr_info *remote, - struct mptcp_pm_local *locals) + struct mptcp_pm_local *locals, + int locals_size) { u8 limit_extra_subflows = mptcp_pm_get_limit_extra_subflows(msk); struct pm_nl_pernet *pernet = pm_nl_get_pernet_from_msk(msk); @@ -586,7 +594,8 @@ fill_local_addresses_vec_c_flag(struct mptcp_sock *msk, msk->pm.extra_subflows++; i++; - if (msk->pm.extra_subflows >= limit_extra_subflows) + if (msk->pm.extra_subflows >= limit_extra_subflows || + i == locals_size) break; } @@ -620,13 +629,14 @@ fill_local_address_any(struct mptcp_sock *msk, struct mptcp_addr_info *remote, */ static unsigned int fill_local_addresses_vec(struct mptcp_sock *msk, struct mptcp_addr_info *remote, - struct mptcp_pm_local *locals) + struct mptcp_pm_local *locals, int locals_size) { bool c_flag_case = remote->id && mptcp_pm_add_addr_c_flag_case(msk); /* If there is at least one MPTCP endpoint with a fullmesh flag */ if (mptcp_pm_get_endp_fullmesh_max(msk)) return fill_local_addresses_vec_fullmesh(msk, remote, locals, + locals_size, c_flag_case); /* If there is at least one MPTCP endpoint with a laminar flag */ @@ -637,7 +647,8 @@ fill_local_addresses_vec(struct mptcp_sock *msk, struct mptcp_addr_info *remote, * limits are used -- accepting no ADD_ADDR -- and use subflow endpoints */ if (c_flag_case) - return fill_local_addresses_vec_c_flag(msk, remote, locals); + return fill_local_addresses_vec_c_flag(msk, remote, locals, + locals_size); /* No special case: fill in the single 'IPADDRANY' local address */ return fill_local_address_any(msk, remote, &locals[0]); @@ -662,7 +673,7 @@ static void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk) mptcp_pm_addr_send_ack(msk); mptcp_mpc_endpoint_setup(msk); - if (lookup_subflow_by_daddr(&msk->conn_list, &remote)) + if (has_subflow_daddr(msk, &remote)) return; /* pick id 0 port, if none is provided the remote address */ @@ -672,7 +683,7 @@ static void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk) /* connect to the specified remote address, using whatever * local address the routing configuration will pick. */ - nr = fill_local_addresses_vec(msk, &remote, locals); + nr = fill_local_addresses_vec(msk, &remote, locals, ARRAY_SIZE(locals)); if (nr == 0) return; @@ -735,7 +746,7 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet, */ if (pernet->next_id == MPTCP_PM_MAX_ADDR_ID) pernet->next_id = 1; - if (pernet->endpoints >= MPTCP_PM_ADDR_MAX) { + if (pernet->endpoints == MPTCP_PM_MAX_ADDR_ID) { ret = -ERANGE; goto out; } @@ -1042,7 +1053,7 @@ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info) return ret; } -static void mptcp_pm_remove_anno_addr(struct mptcp_sock *msk, +static void mptcp_pm_remove_announced(struct mptcp_sock *msk, const struct mptcp_addr_info *addr, bool force) { @@ -1051,7 +1062,7 @@ static void mptcp_pm_remove_anno_addr(struct mptcp_sock *msk, list.ids[list.nr++] = mptcp_endp_get_local_id(msk, addr); - announced = mptcp_remove_anno_list_by_saddr(msk, addr); + announced = mptcp_pm_announced_remove(msk, addr); if (announced || force) { spin_lock_bh(&msk->pm.lock); if (announced) @@ -1087,8 +1098,8 @@ static int mptcp_nl_remove_subflow_and_signal_addr(struct net *net, goto next; lock_sock(sk); - remove_subflow = mptcp_lookup_subflow_by_saddr(&msk->conn_list, addr); - mptcp_pm_remove_anno_addr(msk, addr, remove_subflow && + remove_subflow = mptcp_pm_has_subflow_saddr(msk, addr); + mptcp_pm_remove_announced(msk, addr, remove_subflow && !(entry->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT)); list.ids[0] = mptcp_endp_get_local_id(msk, addr); @@ -1212,19 +1223,30 @@ int mptcp_pm_nl_del_addr_doit(struct sk_buff *skb, struct genl_info *info) } static void mptcp_pm_flush_addrs_and_subflows(struct mptcp_sock *msk, - struct list_head *rm_list) + struct list_head *rm_list, + struct mptcp_pm_addr_entry *entry) { - struct mptcp_rm_list alist = { .nr = 0 }, slist = { .nr = 0 }; - struct mptcp_pm_addr_entry *entry; + struct mptcp_rm_list alist, slist; + bool more; - list_for_each_entry(entry, rm_list, list) { - if (slist.nr < MPTCP_RM_IDS_MAX && - mptcp_lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) +again: + alist.nr = 0; + slist.nr = 0; + more = false; + + entry = list_prepare_entry(entry, rm_list, list); + list_for_each_entry_continue(entry, rm_list, list) { + if (mptcp_pm_has_subflow_saddr(msk, &entry->addr)) slist.ids[slist.nr++] = mptcp_endp_get_local_id(msk, &entry->addr); - if (alist.nr < MPTCP_RM_IDS_MAX && - mptcp_remove_anno_list_by_saddr(msk, &entry->addr)) + if (mptcp_pm_announced_remove(msk, &entry->addr)) alist.ids[alist.nr++] = mptcp_endp_get_local_id(msk, &entry->addr); + + if (slist.nr == MPTCP_RM_IDS_MAX || + alist.nr == MPTCP_RM_IDS_MAX) { + more = !list_is_last(&entry->list, rm_list); + break; + } } spin_lock_bh(&msk->pm.lock); @@ -1235,9 +1257,14 @@ static void mptcp_pm_flush_addrs_and_subflows(struct mptcp_sock *msk, if (slist.nr) mptcp_pm_rm_subflow(msk, &slist); /* Reset counters: maybe some subflows have been removed before */ - bitmap_fill(msk->pm.id_avail_bitmap, MPTCP_PM_MAX_ADDR_ID + 1); - msk->pm.local_addr_used = 0; + if (!more) { + bitmap_fill(msk->pm.id_avail_bitmap, MPTCP_PM_MAX_ADDR_ID + 1); + msk->pm.local_addr_used = 0; + } spin_unlock_bh(&msk->pm.lock); + + if (more) + goto again; } static void mptcp_nl_flush_addrs_list(struct net *net, @@ -1254,7 +1281,7 @@ static void mptcp_nl_flush_addrs_list(struct net *net, if (!mptcp_pm_is_userspace(msk)) { lock_sock(sk); - mptcp_pm_flush_addrs_and_subflows(msk, rm_list); + mptcp_pm_flush_addrs_and_subflows(msk, rm_list, NULL); release_sock(sk); } @@ -1371,10 +1398,10 @@ static int parse_limit(struct genl_info *info, int id, unsigned int *limit) return 0; *limit = nla_get_u32(attr); - if (*limit > MPTCP_PM_ADDR_MAX) { + if (*limit > MPTCP_PM_SUBFLOWS_MAX) { NL_SET_ERR_MSG_ATTR_FMT(info->extack, attr, "limit greater than maximum (%u)", - MPTCP_PM_ADDR_MAX); + MPTCP_PM_SUBFLOWS_MAX); return -EINVAL; } return 0; diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 0d3a95e676f1..d100867e9202 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -228,7 +228,7 @@ int mptcp_pm_nl_announce_doit(struct sk_buff *skb, struct genl_info *info) lock_sock(sk); spin_lock_bh(&msk->pm.lock); - if (mptcp_pm_alloc_anno_list(msk, &addr_val.addr)) { + if (mptcp_pm_announced_alloc(msk, &addr_val.addr)) { msk->pm.add_addr_signaled++; mptcp_pm_announce_addr(msk, &addr_val.addr, false); mptcp_pm_addr_send_ack(msk); @@ -281,9 +281,9 @@ void mptcp_pm_remove_addr_entry(struct mptcp_sock *msk, int anno_nr = 0; /* only delete if either announced or matching a subflow */ - if (mptcp_remove_anno_list_by_saddr(msk, &entry->addr)) + if (mptcp_pm_announced_remove(msk, &entry->addr)) anno_nr++; - else if (!mptcp_lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) + else if (!mptcp_pm_has_subflow_saddr(msk, &entry->addr)) return; alist.ids[alist.nr++] = entry->addr.id; diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index b93b878478d2..4a2d40cd7b13 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -798,6 +798,7 @@ unsigned int mptcp_close_timeout(const struct sock *sk); int mptcp_get_pm_type(const struct net *net); const char *mptcp_get_path_manager(const struct net *net); const char *mptcp_get_scheduler(const struct net *net); +unsigned int mptcp_add_addr_v6_port_drop_ts(const struct net *net); void mptcp_active_disable(struct sock *sk); bool mptcp_active_should_disable(struct sock *ssk); @@ -1113,7 +1114,6 @@ void mptcp_pm_add_addr_received(const struct sock *ssk, const struct mptcp_addr_info *addr); void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk, const struct mptcp_addr_info *addr); -void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk); void mptcp_pm_send_ack(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow, bool prio, bool backup); @@ -1129,16 +1129,13 @@ int mptcp_pm_mp_prio_send_ack(struct mptcp_sock *msk, struct mptcp_addr_info *addr, struct mptcp_addr_info *rem, u8 bkup); -bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk, +bool mptcp_pm_announced_alloc(struct mptcp_sock *msk, const struct mptcp_addr_info *addr); -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, - const struct mptcp_addr_info *addr, bool check_id); -bool mptcp_lookup_subflow_by_saddr(const struct list_head *list, - const struct mptcp_addr_info *saddr); -bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk, - const struct mptcp_addr_info *addr); +bool mptcp_pm_announced_remove(struct mptcp_sock *msk, + const struct mptcp_addr_info *addr); +bool mptcp_pm_announced_has_ssk(struct mptcp_sock *msk, const struct sock *ssk); +bool mptcp_pm_has_subflow_saddr(const struct mptcp_sock *msk, + const struct mptcp_addr_info *saddr); int mptcp_pm_nl_set_flags(struct mptcp_pm_addr_entry *local, struct genl_info *info); int mptcp_userspace_pm_set_flags(struct mptcp_pm_addr_entry *local, @@ -1206,34 +1203,11 @@ static inline bool mptcp_pm_is_kernel(const struct mptcp_sock *msk) return READ_ONCE(msk->pm.pm_type) == MPTCP_PM_TYPE_KERNEL; } -static inline unsigned int mptcp_add_addr_len(int family, bool echo, bool port) -{ - u8 len = TCPOLEN_MPTCP_ADD_ADDR_BASE; - - if (family == AF_INET6) - len = TCPOLEN_MPTCP_ADD_ADDR6_BASE; - if (!echo) - len += MPTCPOPT_THMAC_LEN; - /* account for 2 trailing 'nop' options */ - if (port) - len += TCPOLEN_MPTCP_PORT_LEN + TCPOLEN_MPTCP_PORT_ALIGN; - - return len; -} - -static inline int mptcp_rm_addr_len(const struct mptcp_rm_list *rm_list) -{ - if (rm_list->nr == 0 || rm_list->nr > MPTCP_RM_IDS_MAX) - return -EINVAL; - - return TCPOLEN_MPTCP_RM_ADDR_BASE + roundup(rm_list->nr - 1, 4) + 1; -} - -bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, - unsigned int remaining, - struct mptcp_addr_info *addr, bool *echo); +bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, int *size, int remaining, + struct mptcp_addr_info *addr, bool *echo, + bool *drop_ts); bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, - struct mptcp_rm_list *rm_list); + struct mptcp_rm_list *rm_list, int *len); int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_pm_addr_entry *skc); diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index d562e149606f..8e386899ceb9 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -235,7 +235,7 @@ static int subflow_check_req(struct request_sock *req, pr_debug("syn inet_sport=%d %d\n", ntohs(inet_sk(sk_listener)->inet_sport), ntohs(inet_sk((struct sock *)subflow_req->msk)->inet_sport)); - if (!mptcp_pm_sport_in_anno_list(subflow_req->msk, sk_listener)) { + if (!mptcp_pm_announced_has_ssk(subflow_req->msk, sk_listener)) { SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MISMATCHPORTSYNRX); subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT); return -EPERM; @@ -926,7 +926,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk, pr_debug("ack inet_sport=%d %d\n", ntohs(inet_sk(sk)->inet_sport), ntohs(inet_sk((struct sock *)owner)->inet_sport)); - if (!mptcp_pm_sport_in_anno_list(owner, sk)) { + if (!mptcp_pm_announced_has_ssk(owner, sk)) { SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MISMATCHPORTACKRX); subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT); goto dispose_child; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 682c675125fc..665f8008cc4b 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -22,7 +22,7 @@ config NETFILTER_SKIP_EGRESS def_bool NETFILTER_EGRESS && (NET_CLS_ACT || IFB) config NETFILTER_NETLINK - tristate + bool config NETFILTER_FAMILY_BRIDGE bool @@ -1648,6 +1648,14 @@ config NETFILTER_XT_MATCH_U32 endif # NETFILTER_XTABLES +config GCOV_PROFILE_NETFILTER + bool "Enable GCOV profiling for netfilter" + depends on GCOV_KERNEL + help + Enable GCOV profiling for netfilter to check which functions/lines + are executed. + + If unsure, say N. endmenu source "net/netfilter/ipset/Kconfig" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 6bfc250e474f..6bf74d488a29 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o utils.o +netfilter-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o \ nf_conntrack_proto.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o \ @@ -23,7 +24,6 @@ endif obj-$(CONFIG_NETFILTER) = netfilter.o obj-$(CONFIG_NETFILTER_BPF_LINK) += nf_bpf_link.o -obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o @@ -240,3 +240,7 @@ obj-$(CONFIG_IP_VS) += ipvs/ # lwtunnel obj-$(CONFIG_LWTUNNEL) += nf_hooks_lwtunnel.o + +ifdef CONFIG_GCOV_PROFILE_NETFILTER +GCOV_PROFILE := y +endif diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index b1ea054bb82c..6c4d54758106 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -175,4 +175,13 @@ config IP_SET_LIST_SET To compile it as a module, choose M here. If unsure, say N. +config GCOV_PROFILE_IPSET + bool "Enable GCOV profiling for ipset" + depends on GCOV_KERNEL + help + Enable GCOV profiling for ipset to check which functions/lines + are executed. + + If unsure, say N. + endif # IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index a445a6bf4f11..4f48df5406cd 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -29,3 +29,6 @@ obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o # list types obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o +ifdef CONFIG_GCOV_PROFILE_IPSET +GCOV_PROFILE := y +endif diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig index c203252e856d..7724cb44e6de 100644 --- a/net/netfilter/ipvs/Kconfig +++ b/net/netfilter/ipvs/Kconfig @@ -349,4 +349,13 @@ config IP_VS_PE_SIP help Allow persistence based on the SIP Call-ID +config GCOV_PROFILE_IPVS + bool "Enable GCOV profiling for IPVS" + depends on GCOV_KERNEL + help + Enable GCOV profiling for IPVS to check which functions/lines + are executed. + + If unsure, say N. + endif # IP_VS diff --git a/net/netfilter/ipvs/Makefile b/net/netfilter/ipvs/Makefile index bb5d8125c82a..8e4cc67ad39d 100644 --- a/net/netfilter/ipvs/Makefile +++ b/net/netfilter/ipvs/Makefile @@ -43,3 +43,6 @@ obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o # IPVS connection template retrievers obj-$(CONFIG_IP_VS_PE_SIP) += ip_vs_pe_sip.o +ifdef CONFIG_GCOV_PROFILE_IPVS +GCOV_PROFILE := y +endif diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 9ea6b4fa78bf..cb36641f8d1c 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -285,7 +285,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) /* Schedule resizing if load increases */ if (atomic_read(&ipvs->conn_count) > t->u_thresh && !test_and_set_bit(IP_VS_WORK_CONN_RESIZE, &ipvs->work_flags)) - mod_delayed_work(system_unbound_wq, &ipvs->conn_resize_work, 0); + mod_delayed_work(system_dfl_long_wq, &ipvs->conn_resize_work, 0); return ret; } @@ -916,7 +916,7 @@ static void conn_resize_work_handler(struct work_struct *work) out: /* Monitor if we need to shrink table */ - queue_delayed_work(system_unbound_wq, &ipvs->conn_resize_work, + queue_delayed_work(system_dfl_long_wq, &ipvs->conn_resize_work, more_work ? 1 : 2 * HZ); } @@ -1358,9 +1358,18 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, struct netns_ipvs *ipvs = p->ipvs; struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->ipvs, p->protocol); + /* Increment conn_count up to conn_max */ + int count = atomic_read(&ipvs->conn_count); + int max = sysctl_conn_max(ipvs); + + do { + if (count >= max) + return NULL; + } while (!atomic_try_cmpxchg(&ipvs->conn_count, &count, count + 1)); cp = kmem_cache_alloc(ip_vs_conn_cachep, GFP_ATOMIC); if (cp == NULL) { + atomic_dec(&ipvs->conn_count); IP_VS_ERR_RL("%s(): no memory\n", __func__); return NULL; } @@ -1414,7 +1423,6 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, cp->in_seq.delta = 0; cp->out_seq.delta = 0; - atomic_inc(&ipvs->conn_count); if (unlikely(flags & IP_VS_CONN_F_NO_CPORT)) { int af_id = ip_vs_af_index(cp->af); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 16daba8cac83..bcf40b8c41cf 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -821,7 +821,7 @@ static void svc_resize_work_handler(struct work_struct *work) if (!READ_ONCE(ipvs->enable) || !more_work || test_bit(IP_VS_WORK_SVC_NORESIZE, &ipvs->work_flags)) return; - queue_delayed_work(system_unbound_wq, &ipvs->svc_resize_work, 1); + queue_delayed_work(system_dfl_long_wq, &ipvs->svc_resize_work, 1); return; unlock_m: @@ -1869,7 +1869,7 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, /* Schedule resize work */ if (grow && !test_and_set_bit(IP_VS_WORK_SVC_RESIZE, &ipvs->work_flags)) - queue_delayed_work(system_unbound_wq, &ipvs->svc_resize_work, + queue_delayed_work(system_dfl_long_wq, &ipvs->svc_resize_work, 1); *svc_p = svc; @@ -2125,7 +2125,7 @@ static int ip_vs_del_service(struct ip_vs_service *svc) rcu_read_unlock(); if (shrink && !test_and_set_bit(IP_VS_WORK_SVC_RESIZE, &ipvs->work_flags)) - queue_delayed_work(system_unbound_wq, + queue_delayed_work(system_dfl_long_wq, &ipvs->svc_resize_work, 1); } return 0; @@ -2322,6 +2322,45 @@ static int ip_vs_zero_all(struct netns_ipvs *ipvs) #ifdef CONFIG_SYSCTL +static int +proc_do_conn_max(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + int *valp = table->data; + /* We can not use *valp to check if new value is provided, use INT_MIN + * for this because different admins change different limits. + */ + int unset = INT_MIN; + int val = write ? unset : READ_ONCE(*valp); + int rc; + + const struct ctl_table tmp = { + .data = &val, + .maxlen = sizeof(int), + }; + + rc = proc_dointvec(&tmp, write, buffer, lenp, ppos); + if (write && !rc && val != unset) { + struct netns_ipvs *ipvs = table->extra2; + bool priv = capable(CAP_NET_ADMIN); + int max; + + mutex_lock(&ipvs->service_mutex); + /* Unprivileged admins can not go above the hard limit */ + max = priv ? IP_VS_CONN_MAX : ipvs->conn_max_limit; + if (val < 0 || val > max) { + rc = -EINVAL; + } else { + /* Privileged admin changes both limits */ + if (priv) + ipvs->conn_max_limit = val; + WRITE_ONCE(*valp, val); + } + mutex_unlock(&ipvs->service_mutex); + } + return rc; +} + static int proc_do_defense_mode(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) @@ -2567,7 +2606,7 @@ static int ipvs_proc_conn_lfactor(const struct ctl_table *table, int write, } else { WRITE_ONCE(*valp, val); if (rcu_access_pointer(ipvs->conn_tab)) - mod_delayed_work(system_unbound_wq, + mod_delayed_work(system_dfl_long_wq, &ipvs->conn_resize_work, 0); } } @@ -2599,7 +2638,7 @@ static int ipvs_proc_svc_lfactor(const struct ctl_table *table, int write, READ_ONCE(ipvs->enable) && !test_bit(IP_VS_WORK_SVC_NORESIZE, &ipvs->work_flags)) - mod_delayed_work(system_unbound_wq, + mod_delayed_work(system_dfl_long_wq, &ipvs->svc_resize_work, 0); mutex_unlock(&ipvs->service_mutex); } @@ -2626,6 +2665,12 @@ static struct ctl_table vs_vars[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "conn_max", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_do_conn_max, + }, { .procname = "drop_entry", .maxlen = sizeof(int), @@ -4980,6 +5025,14 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) tbl[idx++].data = &ipvs->sysctl_amemthresh; ipvs->sysctl_am_droprate = 10; tbl[idx++].data = &ipvs->sysctl_am_droprate; + + /* Inherit both limits from init_net:conn_max */ + ipvs->conn_max_limit = net_eq(net, &init_net) ? IP_VS_CONN_MAX : + READ_ONCE(*(int *)vs_vars[idx].data); + ipvs->sysctl_conn_max = ipvs->conn_max_limit; + tbl[idx].extra2 = ipvs; + tbl[idx++].data = &ipvs->sysctl_conn_max; + tbl[idx++].data = &ipvs->sysctl_drop_entry; tbl[idx++].data = &ipvs->sysctl_drop_packet; #ifdef CONFIG_IP_VS_NFCT diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c index 00eed5b4d1b1..dd67004a5cc0 100644 --- a/net/netfilter/nf_conncount.c +++ b/net/netfilter/nf_conncount.c @@ -54,18 +54,22 @@ struct nf_conncount_rb { struct rcu_head rcu_head; }; -static spinlock_t nf_conncount_locks[CONNCOUNT_SLOTS] __cacheline_aligned_in_smp; +struct nf_conncount_root { + struct rb_root root; + spinlock_t lock; + seqcount_spinlock_t count; +}; struct nf_conncount_data { unsigned int keylen; - struct rb_root root[CONNCOUNT_SLOTS]; + u32 initval; + struct nf_conncount_root root[CONNCOUNT_SLOTS]; struct net *net; struct work_struct gc_work; unsigned long pending_trees[BITS_TO_LONGS(CONNCOUNT_SLOTS)]; unsigned int gc_tree; }; -static u_int32_t conncount_rnd __read_mostly; static struct kmem_cache *conncount_rb_cachep __read_mostly; static struct kmem_cache *conncount_conn_cachep __read_mostly; @@ -367,19 +371,22 @@ static void __tree_nodes_free(struct rcu_head *h) kmem_cache_free(conncount_rb_cachep, rbconn); } -/* caller must hold tree nf_conncount_locks[] lock */ -static void tree_nodes_free(struct rb_root *root, +static void tree_nodes_free(struct nf_conncount_root *root, struct nf_conncount_rb *gc_nodes[], unsigned int gc_count) { struct nf_conncount_rb *rbconn; + lockdep_assert_held(&root->lock); + while (gc_count) { rbconn = gc_nodes[--gc_count]; spin_lock(&rbconn->list.list_lock); if (!rbconn->list.count) { - rb_erase(&rbconn->node, root); + write_seqcount_begin(&root->count); + rb_erase(&rbconn->node, &root->root); call_rcu(&rbconn->rcu_head, __tree_nodes_free); + write_seqcount_end(&root->count); } spin_unlock(&rbconn->list.list_lock); } @@ -396,10 +403,10 @@ insert_tree(struct net *net, const struct sk_buff *skb, u16 l3num, struct nf_conncount_data *data, - struct rb_root *root, unsigned int hash, const u32 *key) { + struct nf_conncount_root *root = &data->root[hash]; struct nf_conncount_rb *gc_nodes[CONNCOUNT_GC_MAX_NODES]; const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; bool do_gc = true, refcounted = false; @@ -410,10 +417,10 @@ insert_tree(struct net *net, struct nf_conncount_rb *rbconn; struct nf_conn *ct = NULL; - spin_lock_bh(&nf_conncount_locks[hash]); + spin_lock_bh(&root->lock); restart: parent = NULL; - rbnode = &(root->rb_node); + rbnode = &root->root.rb_node; while (*rbnode) { int diff; rbconn = rb_entry(*rbnode, struct nf_conncount_rb, node); @@ -474,16 +481,53 @@ insert_tree(struct net *net, count = 1; rbconn->list.count = count; + write_seqcount_begin(&root->count); rb_link_node_rcu(&rbconn->node, parent, rbnode); - rb_insert_color(&rbconn->node, root); + rb_insert_color(&rbconn->node, &root->root); + write_seqcount_end(&root->count); } out_unlock: if (refcounted) nf_ct_put(ct); - spin_unlock_bh(&nf_conncount_locks[hash]); + spin_unlock_bh(&root->lock); return count; } +static struct nf_conncount_rb * +find_tree_node(struct nf_conncount_root *root, struct nf_conncount_data *data, + const u32 *key) +{ + unsigned int seq = read_seqcount_begin(&root->count); + struct rb_node *parent; + + parent = rcu_dereference_check(root->root.rb_node, + lockdep_is_held(&root->lock)); + while (parent) { + struct nf_conncount_rb *rbconn; + int diff; + + rbconn = rb_entry(parent, struct nf_conncount_rb, node); + + diff = key_diff(key, rbconn->key, data->keylen); + if (diff < 0) + parent = rcu_dereference_check(parent->rb_left, + lockdep_is_held(&root->lock)); + else if (diff > 0) + parent = rcu_dereference_check(parent->rb_right, + lockdep_is_held(&root->lock)); + else + return rbconn; + + if (read_seqcount_retry(&root->count, seq)) + return ERR_PTR(-EAGAIN); + } + + if (read_seqcount_retry(&root->count, seq)) + return ERR_PTR(-EAGAIN); + + return ERR_PTR(-ENOENT); +} + static unsigned int count_tree(struct net *net, const struct sk_buff *skb, @@ -491,107 +535,114 @@ count_tree(struct net *net, struct nf_conncount_data *data, const u32 *key) { - struct rb_root *root; - struct rb_node *parent; + struct nf_conncount_root *root; struct nf_conncount_rb *rbconn; unsigned int hash; + int ret; - hash = jhash2(key, data->keylen, conncount_rnd) % CONNCOUNT_SLOTS; + hash = jhash2(key, data->keylen, data->initval) % CONNCOUNT_SLOTS; root = &data->root[hash]; - parent = rcu_dereference_raw(root->rb_node); - while (parent) { - int diff; - - rbconn = rb_entry(parent, struct nf_conncount_rb, node); - - diff = key_diff(key, rbconn->key, data->keylen); - if (diff < 0) { - parent = rcu_dereference_raw(parent->rb_left); - } else if (diff > 0) { - parent = rcu_dereference_raw(parent->rb_right); - } else { - int ret; - - if (!skb) { - nf_conncount_gc_list(net, &rbconn->list); - return rbconn->list.count; - } - - spin_lock_bh(&rbconn->list.list_lock); - /* Node might be about to be free'd. - * We need to defer to insert_tree() in this case. - */ - if (rbconn->list.count == 0) { - spin_unlock_bh(&rbconn->list.list_lock); - break; - } - - /* same source network -> be counted! */ - ret = __nf_conncount_add(net, skb, l3num, &rbconn->list); - spin_unlock_bh(&rbconn->list.list_lock); - if (ret && ret != -EEXIST) { - return 0; /* hotdrop */ - } else { - /* -EEXIST means add was skipped, update the list */ - if (ret == -EEXIST) - nf_conncount_gc_list(net, &rbconn->list); - return rbconn->list.count; - } + rbconn = find_tree_node(root, data, key); + if (IS_ERR(rbconn)) { + if (PTR_ERR(rbconn) == -EAGAIN) { + spin_lock_bh(&root->lock); + rbconn = find_tree_node(root, data, key); + spin_unlock_bh(&root->lock); } + + if (PTR_ERR(rbconn) == -ENOENT) { + if (!skb) + return 0; + + return insert_tree(net, skb, l3num, data, hash, key); + } + DEBUG_NET_WARN_ON_ONCE(IS_ERR(rbconn)); } - if (!skb) + DEBUG_NET_WARN_ON_ONCE(IS_ERR_OR_NULL(rbconn)); + if (IS_ERR_OR_NULL(rbconn)) return 0; - return insert_tree(net, skb, l3num, data, root, hash, key); + if (!skb) { + nf_conncount_gc_list(net, &rbconn->list); + return rbconn->list.count; + } + + spin_lock_bh(&rbconn->list.list_lock); + /* Node might be about to be free'd. + * We need to defer to insert_tree() in this case. + */ + if (rbconn->list.count == 0) { + spin_unlock_bh(&rbconn->list.list_lock); + return insert_tree(net, skb, l3num, data, hash, key); + } + + /* same source network -> be counted! */ + ret = __nf_conncount_add(net, skb, l3num, &rbconn->list); + spin_unlock_bh(&rbconn->list.list_lock); + + if (ret && ret != -EEXIST) + return 0; /* hotdrop */ + /* -EEXIST means add was skipped, update the list */ + if (ret == -EEXIST) + nf_conncount_gc_list(net, &rbconn->list); + + return rbconn->list.count; } static void tree_gc_worker(struct work_struct *work) { struct nf_conncount_data *data = container_of(work, struct nf_conncount_data, gc_work); struct nf_conncount_rb *gc_nodes[CONNCOUNT_GC_MAX_NODES], *rbconn; - struct rb_root *root; - struct rb_node *node; unsigned int tree, next_tree, gc_count = 0; + struct nf_conncount_root *root; + struct rb_node *node; + + if (data->gc_tree == 0) + data->gc_tree = find_first_bit(data->pending_trees, CONNCOUNT_SLOTS); tree = data->gc_tree % CONNCOUNT_SLOTS; root = &data->root[tree]; - local_bh_disable(); - rcu_read_lock(); - for (node = rb_first(root); node != NULL; node = rb_next(node)) { - rbconn = rb_entry(node, struct nf_conncount_rb, node); - if (nf_conncount_gc_list(data->net, &rbconn->list)) - gc_count++; - } - rcu_read_unlock(); - local_bh_enable(); - - cond_resched(); - - spin_lock_bh(&nf_conncount_locks[tree]); - if (gc_count < ARRAY_SIZE(gc_nodes)) - goto next; /* do not bother */ - + spin_lock_bh(&root->lock); gc_count = 0; - node = rb_first(root); + node = rb_first(&root->root); while (node != NULL) { + u32 key[MAX_KEYLEN]; + bool drop_lock; + rbconn = rb_entry(node, struct nf_conncount_rb, node); node = rb_next(node); - if (rbconn->list.count > 0) - continue; + if (nf_conncount_gc_list(data->net, &rbconn->list)) + gc_nodes[gc_count++] = rbconn; - gc_nodes[gc_count++] = rbconn; - if (gc_count >= ARRAY_SIZE(gc_nodes)) { + drop_lock = need_resched(); + + if (drop_lock || gc_count >= ARRAY_SIZE(gc_nodes)) { tree_nodes_free(root, gc_nodes, gc_count); gc_count = 0; } + + if (!drop_lock || !node) + continue; + + rbconn = rb_entry(node, struct nf_conncount_rb, node); + memcpy(key, rbconn->key, sizeof(key)); + spin_unlock_bh(&root->lock); + + cond_resched(); + + spin_lock_bh(&root->lock); + rbconn = find_tree_node(root, data, key); + if (IS_ERR_OR_NULL(rbconn)) /* rbconn was reaped */ + break; + + node = &rbconn->node; } tree_nodes_free(root, gc_nodes, gc_count); -next: clear_bit(tree, data->pending_trees); next_tree = (tree + 1) % CONNCOUNT_SLOTS; @@ -600,9 +651,11 @@ static void tree_gc_worker(struct work_struct *work) if (next_tree < CONNCOUNT_SLOTS) { data->gc_tree = next_tree; schedule_work(work); + } else { + data->gc_tree = 0; } - spin_unlock_bh(&nf_conncount_locks[tree]); + spin_unlock_bh(&root->lock); } /* Count and return number of conntrack entries in 'net' with particular 'key'. @@ -620,6 +673,13 @@ unsigned int nf_conncount_count_skb(struct net *net, } EXPORT_SYMBOL_GPL(nf_conncount_count_skb); +static void nf_conncount_root_init(struct nf_conncount_root *r) +{ + r->root = RB_ROOT; + spin_lock_init(&r->lock); + seqcount_spinlock_init(&r->count, &r->lock); +} + struct nf_conncount_data *nf_conncount_init(struct net *net, unsigned int keylen) { struct nf_conncount_data *data; @@ -630,17 +690,16 @@ struct nf_conncount_data *nf_conncount_init(struct net *net, unsigned int keylen keylen == 0) return ERR_PTR(-EINVAL); - net_get_random_once(&conncount_rnd, sizeof(conncount_rnd)); - - data = kmalloc_obj(*data); + data = kvzalloc_obj(*data); if (!data) return ERR_PTR(-ENOMEM); for (i = 0; i < ARRAY_SIZE(data->root); ++i) - data->root[i] = RB_ROOT; + nf_conncount_root_init(&data->root[i]); data->keylen = keylen / sizeof(u32); data->net = net; + data->initval = get_random_u32(); INIT_WORK(&data->gc_work, tree_gc_worker); return data; @@ -656,15 +715,15 @@ void nf_conncount_cache_free(struct nf_conncount_list *list) } EXPORT_SYMBOL_GPL(nf_conncount_cache_free); -static void destroy_tree(struct rb_root *r) +static void destroy_tree(struct nf_conncount_root *r) { struct nf_conncount_rb *rbconn; struct rb_node *node; - while ((node = rb_first(r)) != NULL) { + while ((node = rb_first(&r->root)) != NULL) { rbconn = rb_entry(node, struct nf_conncount_rb, node); - rb_erase(node, r); + rb_erase(node, &r->root); nf_conncount_cache_free(&rbconn->list); @@ -676,22 +735,17 @@ void nf_conncount_destroy(struct net *net, struct nf_conncount_data *data) { unsigned int i; - cancel_work_sync(&data->gc_work); + disable_work_sync(&data->gc_work); for (i = 0; i < ARRAY_SIZE(data->root); ++i) destroy_tree(&data->root[i]); - kfree(data); + kvfree(data); } EXPORT_SYMBOL_GPL(nf_conncount_destroy); static int __init nf_conncount_modinit(void) { - int i; - - for (i = 0; i < CONNCOUNT_SLOTS; ++i) - spin_lock_init(&nf_conncount_locks[i]); - conncount_conn_cachep = KMEM_CACHE(nf_conncount_tuple, 0); if (!conncount_conn_cachep) return -ENOMEM; @@ -707,6 +761,7 @@ static int __init nf_conncount_modinit(void) static void __exit nf_conncount_modexit(void) { + rcu_barrier(); kmem_cache_destroy(conncount_conn_cachep); kmem_cache_destroy(conncount_rb_cachep); } diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index d2c09e8dd872..ddafbdfc96dc 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -169,35 +169,15 @@ static const struct nf_conntrack_expect_policy amanda_exp_policy = { .timeout = 180, }; -static struct nf_conntrack_helper amanda_helper[2] __read_mostly = { - { - .name = HELPER_NAME, - .me = THIS_MODULE, - .help = amanda_help, - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(10080), - .tuple.dst.protonum = IPPROTO_UDP, - .expect_policy = &amanda_exp_policy, - .nat_mod_name = NF_NAT_HELPER_NAME(HELPER_NAME), - }, - { - .name = "amanda", - .me = THIS_MODULE, - .help = amanda_help, - .tuple.src.l3num = AF_INET6, - .tuple.src.u.udp.port = cpu_to_be16(10080), - .tuple.dst.protonum = IPPROTO_UDP, - .expect_policy = &amanda_exp_policy, - .nat_mod_name = NF_NAT_HELPER_NAME(HELPER_NAME), - }, -}; +static struct nf_conntrack_helper amanda_helper[2] __read_mostly; +static struct nf_conntrack_helper *amanda_helper_ptr[2] __read_mostly; static void __exit nf_conntrack_amanda_fini(void) { int i; - nf_conntrack_helpers_unregister(amanda_helper, - ARRAY_SIZE(amanda_helper)); + nf_conntrack_helpers_unregister(amanda_helper_ptr, + ARRAY_SIZE(amanda_helper_ptr)); for (i = 0; i < ARRAY_SIZE(search); i++) textsearch_destroy(search[i].ts); } @@ -217,8 +197,17 @@ static int __init nf_conntrack_amanda_init(void) goto err1; } } + + nf_ct_helper_init(&amanda_helper[0], AF_INET, IPPROTO_UDP, + HELPER_NAME, 10080, 10080, 10080, + &amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE); + nf_ct_helper_init(&amanda_helper[1], AF_INET6, IPPROTO_UDP, + HELPER_NAME, 10080, 10080, 10080, + &amanda_exp_policy, 0, amanda_help, NULL, THIS_MODULE); + ret = nf_conntrack_helpers_register(amanda_helper, - ARRAY_SIZE(amanda_helper)); + ARRAY_SIZE(amanda_helper), + amanda_helper_ptr); if (ret < 0) goto err1; return 0; diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 75e53fde6b29..400119b6320e 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -29,6 +29,9 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, struct nf_conn_help *help = nfct_help(ct); __be32 mask = 0; + if (!help) + goto out; + /* we're only interested in locally generated packets */ if (skb->sk == NULL || !net_eq(nf_ct_net(ct), sock_net(skb->sk))) goto out; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b521b5ebd664..4fb3a2d18631 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -562,9 +562,23 @@ static void destroy_gre_conntrack(struct nf_conn *ct) { #ifdef CONFIG_NF_CT_PROTO_GRE struct nf_conn *master = ct->master; + struct nf_conn_help *help; - if (master) - nf_ct_gre_keymap_destroy(master); + if (!master) + return; + + help = nfct_help(master); + if (help) { + struct nf_conntrack_helper *helper; + + rcu_read_lock(); + helper = rcu_dereference(help->helper); + /* Only pptp helper has a destroy callback. */ + if (helper && helper->destroy) + nf_ct_gre_keymap_destroy(master); + + rcu_read_unlock(); + } #endif } @@ -840,33 +854,6 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct, &nf_conntrack_hash[reply_hash]); } -static bool nf_ct_ext_valid_pre(const struct nf_ct_ext *ext) -{ - /* if ext->gen_id is not equal to nf_conntrack_ext_genid, some extensions - * may contain stale pointers to e.g. helper that has been removed. - * - * The helper can't clear this because the nf_conn object isn't in - * any hash and synchronize_rcu() isn't enough because associated skb - * might sit in a queue. - */ - return !ext || ext->gen_id == atomic_read(&nf_conntrack_ext_genid); -} - -static bool nf_ct_ext_valid_post(struct nf_ct_ext *ext) -{ - if (!ext) - return true; - - if (ext->gen_id != atomic_read(&nf_conntrack_ext_genid)) - return false; - - /* inserted into conntrack table, nf_ct_iterate_cleanup() - * will find it. Disable nf_ct_ext_find() id check. - */ - WRITE_ONCE(ext->gen_id, 0); - return true; -} - int nf_conntrack_hash_check_insert(struct nf_conn *ct) { @@ -882,9 +869,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) zone = nf_ct_zone(ct); - if (!nf_ct_ext_valid_pre(ct->ext)) - return -EAGAIN; - local_bh_disable(); do { sequence = read_seqcount_begin(&nf_conntrack_generation); @@ -918,18 +902,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) goto chaintoolong; } - /* If genid has changed, we can't insert anymore because ct - * extensions could have stale pointers and nf_ct_iterate_destroy - * might have completed its table scan already. - * - * Increment of the ext genid right after this check is fine: - * nf_ct_iterate_destroy blocks until locks are released. - */ - if (!nf_ct_ext_valid_post(ct->ext)) { - err = -EAGAIN; - goto out; - } - smp_wmb(); /* The caller holds a reference to this object */ refcount_set(&ct->ct_general.use, 2); @@ -1257,11 +1229,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) return NF_DROP; } - if (!nf_ct_ext_valid_pre(ct->ext)) { - NF_CT_STAT_INC(net, insert_failed); - goto dying; - } - /* We have to check the DYING flag after unlink to prevent * a race against nf_ct_get_next_corpse() possibly called from * user context, else we insert an already 'dead' hash, blocking @@ -1324,16 +1291,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) nf_conntrack_double_unlock(hash, reply_hash); local_bh_enable(); - /* ext area is still valid (rcu read lock is held, - * but will go out of scope soon, we need to remove - * this conntrack again. - */ - if (!nf_ct_ext_valid_post(ct->ext)) { - nf_ct_kill(ct); - NF_CT_STAT_INC_ATOMIC(net, drop); - return NF_DROP; - } - help = nfct_help(ct); if (help && help->helper) nf_conntrack_event_cache(IPCT_HELPER, ct); @@ -1737,16 +1694,19 @@ void nf_conntrack_free(struct nf_conn *ct) */ WARN_ON(refcount_read(&ct->ct_general.use) != 0); + rcu_read_lock(); 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(); } + nf_ct_help_put(ct); + nf_ct_timeout_put(ct); + rcu_read_unlock(); + kfree(ct->ext); kmem_cache_free(nf_conntrack_cachep, ct); cnet = nf_ct_pernet(net); @@ -1827,7 +1787,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, assign_helper = rcu_dereference(exp->assign_helper); if (assign_helper) { help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); - if (help) + if (help && refcount_inc_not_zero(&assign_helper->ct_refcnt)) rcu_assign_pointer(help->helper, assign_helper); } @@ -2213,6 +2173,7 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, { const struct nf_conntrack_helper *helper; const struct nf_conn_help *help; + unsigned int helper_flags; int protoff; help = nfct_help(ct); @@ -2223,7 +2184,8 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, if (!helper) return NF_ACCEPT; - if (!(helper->flags & NF_CT_HELPER_F_USERSPACE)) + helper_flags = READ_ONCE(helper->flags); + if (!(helper_flags & NF_CT_HELPER_F_USERSPACE)) return NF_ACCEPT; switch (nf_ct_l3num(ct)) { @@ -2436,13 +2398,11 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data) */ synchronize_net(); - nf_ct_ext_bump_genid(); iter_data.data = data; nf_ct_iterate_cleanup(iter, &iter_data); /* Another cpu might be in a rcu read section with - * rcu protected pointer cleared in iter callback - * or hidden via nf_ct_ext_bump_genid() above. + * rcu protected pointer cleared in iter callback. * * Wait until those are done. */ diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 8e943efbdf0a..5c9b17835c28 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -52,8 +52,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, struct nf_conntrack_net *cnet; lockdep_nfct_expect_lock_held(); - WARN_ON(!master_help); - WARN_ON(timer_pending(&exp->timeout)); + WARN_ON_ONCE(timer_pending(&exp->timeout)); hlist_del_rcu(&exp->hnode); @@ -61,7 +60,8 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, cnet->expect_count--; hlist_del_rcu(&exp->lnode); - master_help->expecting[exp->class]--; + if (master_help) + master_help->expecting[exp->class]--; nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report); nf_ct_expect_put(exp); @@ -405,10 +405,10 @@ void nf_ct_expect_put(struct nf_conntrack_expect *exp) } EXPORT_SYMBOL_GPL(nf_ct_expect_put); -static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) +static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, + struct nf_conn_help *master_help) { struct nf_conntrack_net *cnet; - struct nf_conn_help *master_help = nfct_help(exp->master); struct nf_conntrack_helper *helper; struct net *net = nf_ct_exp_net(exp); unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); @@ -436,10 +436,9 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) } /* Race with expectations being used means we could have none to find; OK. */ -static void evict_oldest_expect(struct nf_conn *master, +static void evict_oldest_expect(struct nf_conn_help *master_help, struct nf_conntrack_expect *new) { - struct nf_conn_help *master_help = nfct_help(master); struct nf_conntrack_expect *exp, *last = NULL; hlist_for_each_entry(exp, &master_help->expectations, lnode) { @@ -452,13 +451,12 @@ static void evict_oldest_expect(struct nf_conn *master, } static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, + struct nf_conn_help *master_help, unsigned int flags) { const struct nf_conntrack_expect_policy *p; struct nf_conntrack_expect *i; struct nf_conntrack_net *cnet; - struct nf_conn *master = expect->master; - struct nf_conn_help *master_help = nfct_help(master); struct nf_conntrack_helper *helper; struct net *net = nf_ct_exp_net(expect); struct hlist_node *next; @@ -467,10 +465,6 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, lockdep_nfct_expect_lock_held(); - if (!master_help) { - ret = -ESHUTDOWN; - goto out; - } h = nf_ct_expect_dst_hash(net, &expect->tuple); hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { if (master_matches(i, expect, flags) && @@ -493,7 +487,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, p = &helper->expect_policy[expect->class]; if (p->max_expected && master_help->expecting[expect->class] >= p->max_expected) { - evict_oldest_expect(master, expect); + evict_oldest_expect(master_help, expect); if (master_help->expecting[expect->class] >= p->max_expected) { ret = -EMFILE; @@ -514,14 +508,21 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect, int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, u32 portid, int report, unsigned int flags) { + struct nf_conn_help *master_help; int ret; spin_lock_bh(&nf_conntrack_expect_lock); - ret = __nf_ct_expect_check(expect, flags); + master_help = nfct_help(expect->master); + if (!master_help) { + ret = -ESHUTDOWN; + goto out; + } + + ret = __nf_ct_expect_check(expect, master_help, flags); if (ret < 0) goto out; - nf_ct_expect_insert(expect); + nf_ct_expect_insert(expect, master_help); nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); spin_unlock_bh(&nf_conntrack_expect_lock); diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index dd62cc12e775..0da105e1ded9 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -27,8 +27,6 @@ #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ -atomic_t nf_conntrack_ext_genid __read_mostly = ATOMIC_INIT(1); - 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) @@ -118,10 +116,8 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) if (!new) return NULL; - if (!ct->ext) { + if (!ct->ext) memset(new->offset, 0, sizeof(new->offset)); - new->gen_id = atomic_read(&nf_conntrack_ext_genid); - } new->offset[id] = newoff; new->len = newlen; @@ -131,29 +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); - -/* Use nf_ct_ext_find wrapper. This is only useful for unconfirmed entries. */ -void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id) -{ - unsigned int gen_id = atomic_read(&nf_conntrack_ext_genid); - unsigned int this_id = READ_ONCE(ext->gen_id); - - if (!__nf_ct_ext_exist(ext, id)) - return NULL; - - if (this_id == 0 || ext->gen_id == gen_id) - return (void *)ext + ext->offset[id]; - - return NULL; -} -EXPORT_SYMBOL(__nf_ct_ext_find); - -void nf_ct_ext_bump_genid(void) -{ - unsigned int value = atomic_inc_return(&nf_conntrack_ext_genid); - - if (value == UINT_MAX) - atomic_set(&nf_conntrack_ext_genid, 1); - - msleep(HZ); -} diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index de83bf9e6c61..0847f845613d 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -120,6 +120,8 @@ static int try_number(const char *data, size_t dlen, u_int32_t array[], for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { if (*data >= '0' && *data <= '9') { array[i] = array[i]*10 + *data - '0'; + if (array[i] > 255) + return 0; } else if (*data == sep) i++; @@ -189,7 +191,7 @@ static int try_rfc1123(const char *data, size_t dlen, static int get_port(const char *data, int start, size_t dlen, char delim, __be16 *port) { - u_int16_t tmp_port = 0; + u32 tmp_port = 0; int i; for (i = start; i < dlen; i++) { @@ -200,10 +202,11 @@ static int get_port(const char *data, int start, size_t dlen, char delim, *port = htons(tmp_port); pr_debug("get_port: return %d\n", tmp_port); return i + 1; - } - else if (data[i] >= '0' && data[i] <= '9') + } else if (data[i] >= '0' && data[i] <= '9') { tmp_port = tmp_port*10 + data[i] - '0'; - else { /* Some other crap */ + if (tmp_port > 65535) + break; + } else { /* Some other crap */ pr_debug("get_port: invalid char.\n"); break; } @@ -381,6 +384,9 @@ static int help(struct sk_buff *skb, int found = 0, ends_in_nl; nf_nat_ftp_hook_fn *nf_nat_ftp; + if (!ct_ftp_info) + return NF_DROP; + /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) { @@ -542,6 +548,9 @@ static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct) { struct nf_ct_ftp_master *ftp = nfct_help_data(ct); + if (!ftp) + return -ENOENT; + /* This conntrack has been injected from user-space, always pick up * sequence tracking. Otherwise, the first FTP command after the * failover breaks. @@ -552,6 +561,7 @@ static int nf_ct_ftp_from_nlattr(struct nlattr *attr, struct nf_conn *ct) } static struct nf_conntrack_helper ftp[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper *ftp_ptr[MAX_PORTS * 2] __read_mostly; static const struct nf_conntrack_expect_policy ftp_exp_policy = { .max_expected = 1, @@ -560,7 +570,7 @@ static const struct nf_conntrack_expect_policy ftp_exp_policy = { static void __exit nf_conntrack_ftp_fini(void) { - nf_conntrack_helpers_unregister(ftp, ports_c * 2); + nf_conntrack_helpers_unregister(ftp_ptr, ports_c * 2); } static int __init nf_conntrack_ftp_init(void) @@ -585,7 +595,7 @@ static int __init nf_conntrack_ftp_init(void) nf_ct_ftp_from_nlattr, THIS_MODULE); } - ret = nf_conntrack_helpers_register(ftp, ports_c * 2); + ret = nf_conntrack_helpers_register(ftp, ports_c * 2, ftp_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index b2fe6554b9cf..7f189dceb3c4 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -76,6 +76,9 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff, int tpktlen; int tpktoff; + if (!info) + return 0; + /* Get TCP header */ th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); if (th == NULL) @@ -577,14 +580,8 @@ static const struct nf_conntrack_expect_policy h245_exp_policy = { .timeout = 240, }; -static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly = { - .name = "H.245", - .me = THIS_MODULE, - .tuple.src.l3num = AF_UNSPEC, - .tuple.dst.protonum = IPPROTO_UDP, - .help = h245_help, - .expect_policy = &h245_exp_policy, -}; +static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly; +static struct nf_conntrack_helper *nf_conntrack_helper_h245_ptr __read_mostly; int get_h225_addr(struct nf_conn *ct, unsigned char *data, TransportAddress *taddr, @@ -643,7 +640,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_h245_ptr); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -732,6 +729,9 @@ static int callforward_do_filter(struct net *net, } +static struct nf_conntrack_helper nf_conntrack_helper_q931[2] __read_mostly; +static struct nf_conntrack_helper *nf_conntrack_helper_q931_ptr[2] __read_mostly; + static int expect_callforwarding(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); nathook = rcu_dereference(nfct_h323_nat_hook); if (memcmp(&ct->tuplehash[dir].tuple.src.u3, @@ -1140,27 +1140,6 @@ static const struct nf_conntrack_expect_policy q931_exp_policy = { .timeout = 240, }; -static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = { - { - .name = "Q.931", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET, - .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), - .tuple.dst.protonum = IPPROTO_TCP, - .help = q931_help, - .expect_policy = &q931_exp_policy, - }, - { - .name = "Q.931", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET6, - .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), - .tuple.dst.protonum = IPPROTO_TCP, - .help = q931_help, - .expect_policy = &q931_exp_policy, - }, -}; - static unsigned char *get_udp_data(struct sk_buff *skb, unsigned int protoff, int *datalen) { @@ -1215,6 +1194,9 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, union nf_inet_addr addr; struct nf_conntrack_expect *exp; + if (!info) + return -1; + /* Look for the first related address */ for (i = 0; i < count; i++) { if (get_h225_addr(ct, *data, &taddr[i], &addr, &port) && @@ -1234,7 +1216,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3 : NULL, &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_TCP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */ nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1275,6 +1257,9 @@ static int process_grq(struct sk_buff *skb, struct nf_conn *ct, return 0; } +static struct nf_conntrack_helper nf_conntrack_helper_ras[2] __read_mostly; +static struct nf_conntrack_helper *nf_conntrack_helper_ras_ptr[2] __read_mostly; + static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned int protoff, @@ -1306,7 +1291,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct, nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_UDP, NULL, &port); - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras_ptr[0]); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect RAS "); @@ -1328,6 +1313,9 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, const struct nfct_h323_nat_hooks *nathook; int ret; + if (!info) + return -1; + pr_debug("nf_ct_ras: RRQ\n"); ret = expect_q931(skb, ct, ctinfo, protoff, data, @@ -1366,6 +1354,9 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, int ret; struct nf_conntrack_expect *exp; + if (!info) + return -1; + pr_debug("nf_ct_ras: RCF\n"); nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1416,6 +1407,9 @@ static int process_urq(struct sk_buff *skb, struct nf_conn *ct, int dir = CTINFO2DIR(ctinfo); int ret; + if (!info) + return -1; + pr_debug("nf_ct_ras: URQ\n"); nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1450,6 +1444,9 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, __be16 port; union nf_inet_addr addr; + if (!info) + return 0; + pr_debug("nf_ct_ras: ARQ\n"); nathook = rcu_dereference(nfct_h323_nat_hook); @@ -1523,7 +1520,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1577,7 +1574,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct, &ct->tuplehash[!dir].tuple.src.u3, &addr, IPPROTO_TCP, NULL, &port); exp->flags = NF_CT_EXPECT_PERMANENT; - rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931); + rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931_ptr[0]); if (nf_ct_expect_related(exp, 0) == 0) { pr_debug("nf_ct_ras: expect Q.931 "); @@ -1711,59 +1708,57 @@ static const struct nf_conntrack_expect_policy ras_exp_policy = { .timeout = 240, }; -static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = { - { - .name = "RAS", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET, - .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .help = ras_help, - .expect_policy = &ras_exp_policy, - }, - { - .name = "RAS", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET6, - .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .help = ras_help, - .expect_policy = &ras_exp_policy, - }, -}; - static int __init h323_helper_init(void) { int ret; - ret = nf_conntrack_helper_register(&nf_conntrack_helper_h245); + nf_ct_helper_init(&nf_conntrack_helper_ras[0], AF_INET, IPPROTO_UDP, + "RAS", RAS_PORT, RAS_PORT, RAS_PORT, + &ras_exp_policy, 0, ras_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_ras[1], AF_INET6, IPPROTO_UDP, + "RAS", RAS_PORT, RAS_PORT, RAS_PORT, + &ras_exp_policy, 0, ras_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_h245, AF_UNSPEC, IPPROTO_UDP, + "H.245", 0, 0, 0, + &h245_exp_policy, 0, h245_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_q931[0], AF_INET, IPPROTO_TCP, + "Q.931", Q931_PORT, Q931_PORT, Q931_PORT, + &q931_exp_policy, 0, q931_help, NULL, THIS_MODULE); + nf_ct_helper_init(&nf_conntrack_helper_q931[1], AF_INET6, IPPROTO_TCP, + "Q.931", Q931_PORT, Q931_PORT, Q931_PORT, + &q931_exp_policy, 0, q931_help, NULL, THIS_MODULE); + + ret = nf_conntrack_helper_register(&nf_conntrack_helper_h245, + &nf_conntrack_helper_h245_ptr); if (ret < 0) return ret; ret = nf_conntrack_helpers_register(nf_conntrack_helper_q931, - ARRAY_SIZE(nf_conntrack_helper_q931)); + ARRAY_SIZE(nf_conntrack_helper_q931), + nf_conntrack_helper_q931_ptr); if (ret < 0) goto err1; ret = nf_conntrack_helpers_register(nf_conntrack_helper_ras, - ARRAY_SIZE(nf_conntrack_helper_ras)); + ARRAY_SIZE(nf_conntrack_helper_ras), + nf_conntrack_helper_ras_ptr); if (ret < 0) goto err2; return 0; err2: - nf_conntrack_helpers_unregister(nf_conntrack_helper_q931, - ARRAY_SIZE(nf_conntrack_helper_q931)); + nf_conntrack_helpers_unregister(nf_conntrack_helper_q931_ptr, + ARRAY_SIZE(nf_conntrack_helper_q931_ptr)); err1: - nf_conntrack_helper_unregister(&nf_conntrack_helper_h245); + nf_conntrack_helper_unregister(nf_conntrack_helper_h245_ptr); return ret; } static void __exit h323_helper_exit(void) { - nf_conntrack_helpers_unregister(nf_conntrack_helper_ras, + nf_conntrack_helpers_unregister(nf_conntrack_helper_ras_ptr, ARRAY_SIZE(nf_conntrack_helper_ras)); - nf_conntrack_helpers_unregister(nf_conntrack_helper_q931, + nf_conntrack_helpers_unregister(nf_conntrack_helper_q931_ptr, ARRAY_SIZE(nf_conntrack_helper_q931)); - nf_conntrack_helper_unregister(&nf_conntrack_helper_h245); + nf_conntrack_helper_unregister(nf_conntrack_helper_h245_ptr); } static void __exit nf_conntrack_h323_fini(void) diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 2c5a71735561..2f35bdd0d7d7 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -92,7 +92,7 @@ nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum) #endif if (h != NULL && !try_module_get(h->me)) h = NULL; - if (h != NULL && !refcount_inc_not_zero(&h->refcnt)) { + if (h != NULL && !refcount_inc_not_zero(&h->ct_refcnt)) { module_put(h->me); h = NULL; } @@ -105,8 +105,9 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); void nf_conntrack_helper_put(struct nf_conntrack_helper *helper) { - refcount_dec(&helper->refcnt); module_put(helper->me); + if (refcount_dec_and_test(&helper->ct_refcnt)) + kfree_rcu(helper, rcu); } EXPORT_SYMBOL_GPL(nf_conntrack_helper_put); @@ -210,8 +211,13 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, help = nfct_help(ct); if (helper == NULL) { - if (help) + if (help) { + struct nf_conntrack_helper *tmp = rcu_dereference(help->helper); + RCU_INIT_POINTER(help->helper, NULL); + if (tmp && refcount_dec_and_test(&tmp->ct_refcnt)) + kfree_rcu(tmp, rcu); + } return 0; } @@ -225,32 +231,23 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, */ struct nf_conntrack_helper *tmp = rcu_dereference(help->helper); - if (tmp && tmp->help != helper->help) { - RCU_INIT_POINTER(help->helper, NULL); + if (tmp) { + if (tmp->help != helper->help) { + RCU_INIT_POINTER(help->helper, NULL); + if (refcount_dec_and_test(&tmp->ct_refcnt)) + kfree_rcu(tmp, rcu); + } return 0; } } - rcu_assign_pointer(help->helper, helper); + if (refcount_inc_not_zero(&helper->ct_refcnt)) + rcu_assign_pointer(help->helper, helper); return 0; } EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); -/* appropriate ct lock protecting must be taken by caller */ -static int unhelp(struct nf_conn *ct, void *me) -{ - struct nf_conn_help *help = nfct_help(ct); - - if (help && rcu_dereference_raw(help->helper) == me) { - nf_conntrack_event(IPCT_HELPER, ct); - RCU_INIT_POINTER(help->helper, NULL); - } - - /* We are not intended to delete this conntrack. */ - return 0; -} - void nf_ct_helper_destroy(struct nf_conn *ct) { struct nf_conn_help *help = nfct_help(ct); @@ -366,14 +363,13 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, } EXPORT_SYMBOL_GPL(nf_ct_helper_log); -int nf_conntrack_helper_register(struct nf_conntrack_helper *me) +int __nf_conntrack_helper_register(struct nf_conntrack_helper *me) { struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) }; unsigned int h = helper_hash(&me->tuple); struct nf_conntrack_helper *cur; int ret = 0, i; - BUG_ON(me->expect_policy == NULL); BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); @@ -406,13 +402,40 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) } } } - refcount_set(&me->refcnt, 1); + refcount_set(&me->ct_refcnt, 1); hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); nf_ct_helper_count++; out: mutex_unlock(&nf_ct_helper_mutex); return ret; } +EXPORT_SYMBOL_GPL(__nf_conntrack_helper_register); + +int nf_conntrack_helper_register(struct nf_conntrack_helper *me, + struct nf_conntrack_helper **helper_ptr) +{ + struct nf_conntrack_helper *new_helper; + int err; + + new_helper = kzalloc_obj(*new_helper, GFP_KERNEL_ACCOUNT); + if (!new_helper) + return -ENOMEM; + + memcpy(new_helper, me, sizeof(*new_helper)); + *helper_ptr = new_helper; + + err = __nf_conntrack_helper_register(new_helper); + if (err < 0) + goto err_helper; + + return 0; + +err_helper: + *helper_ptr = NULL; + kfree(new_helper); + + return err; +} EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) @@ -437,18 +460,18 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) nf_ct_helper_count--; mutex_unlock(&nf_ct_helper_mutex); + /* This helper is going away, disable it. */ + rcu_assign_pointer(me->help, NULL); + /* Make sure every nothing is still using the helper unless its a * connection in the hash. */ synchronize_rcu(); nf_ct_expect_iterate_destroy(expect_iter_me, me); - nf_ct_iterate_destroy(unhelp, me); - /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as - * last step, this ensures rcu readers of exp->helper are done. - * No need for another synchronize_rcu() here. - */ + if (refcount_dec_and_test(&me->ct_refcnt)) + kfree_rcu(me, rcu); } EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); @@ -464,12 +487,13 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, struct nf_conn *ct), struct module *module) { + memset(helper, 0, sizeof(*helper)); + helper->tuple.src.l3num = l3num; helper->tuple.dst.protonum = protonum; helper->tuple.src.u.all = htons(spec_port); - helper->expect_policy = exp_pol; - helper->expect_class_max = expect_class_max; - helper->help = help; + + rcu_assign_pointer(helper->help, help); helper->from_nlattr = from_nlattr; helper->me = module; snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name), @@ -479,34 +503,57 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, snprintf(helper->name, sizeof(helper->name), "%s", name); else snprintf(helper->name, sizeof(helper->name), "%s-%u", name, id); + + if (WARN_ON_ONCE(expect_class_max >= NF_CT_MAX_EXPECT_CLASSES)) + return; + + memcpy(helper->expect_policy, exp_pol, + (expect_class_max + 1) * sizeof(*exp_pol)); + helper->expect_class_max = expect_class_max; } EXPORT_SYMBOL_GPL(nf_ct_helper_init); int nf_conntrack_helpers_register(struct nf_conntrack_helper *helper, - unsigned int n) + unsigned int n, struct nf_conntrack_helper **helper_ptr) { + struct nf_conntrack_helper *new_helper; unsigned int i; int err = 0; for (i = 0; i < n; i++) { - err = nf_conntrack_helper_register(&helper[i]); - if (err < 0) + new_helper = kzalloc_obj(*new_helper, GFP_KERNEL_ACCOUNT); + if (!new_helper) { + err = -ENOMEM; goto err; + } + + memcpy(new_helper, &helper[i], sizeof(*new_helper)); + helper_ptr[i] = new_helper; + + err = __nf_conntrack_helper_register(new_helper); + if (err < 0) { + helper_ptr[i] = NULL; + goto err_helper; + } } return err; +err_helper: + kfree(new_helper); err: if (i > 0) - nf_conntrack_helpers_unregister(helper, i); + nf_conntrack_helpers_unregister(helper_ptr, i); return err; } EXPORT_SYMBOL_GPL(nf_conntrack_helpers_register); -void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper, - unsigned int n) +void nf_conntrack_helpers_unregister(struct nf_conntrack_helper **helper, + unsigned int n) { - while (n-- > 0) - nf_conntrack_helper_unregister(&helper[n]); + while (n-- > 0) { + nf_conntrack_helper_unregister(helper[n]); + helper[n] = NULL; + } } EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister); diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 2ebe4cb47cf6..0c117b8492e9 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -59,7 +59,7 @@ static const char *const dccprotos[] = { /* tries to get the ip_addr and port out of a dcc command * return value: -1 on failure, 0 on success * data pointer to first byte of DCC command data - * data_end pointer to last byte of dcc command data + * data_end one past end of data * ip returns parsed ip of dcc command * port returns parsed port of dcc command * ad_beg_p returns pointer to first byte of addr data @@ -77,10 +77,10 @@ static int parse_dcc(char *data, const char *data_end, __be32 *ip, /* Make sure we have a newline character within the packet boundaries * because simple_strtoul parses until the first invalid character. */ - for (tmp = data; tmp <= data_end; tmp++) + for (tmp = data; tmp < data_end; tmp++) if (*tmp == '\n') break; - if (tmp > data_end || *tmp != '\n') + if (tmp >= data_end || *tmp != '\n') return -1; *ad_beg_p = data; @@ -255,6 +255,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, } static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly; +static struct nf_conntrack_helper *irc_ptr[MAX_PORTS] __read_mostly; static struct nf_conntrack_expect_policy irc_exp_policy; static int __init nf_conntrack_irc_init(void) @@ -289,7 +290,7 @@ static int __init nf_conntrack_irc_init(void) 0, help, NULL, THIS_MODULE); } - ret = nf_conntrack_helpers_register(&irc[0], ports_c); + ret = nf_conntrack_helpers_register(&irc[0], ports_c, irc_ptr); if (ret) { pr_err("failed to register helpers\n"); kfree(irc_buffer); @@ -301,7 +302,7 @@ static int __init nf_conntrack_irc_init(void) static void __exit nf_conntrack_irc_fini(void) { - nf_conntrack_helpers_unregister(irc, ports_c); + nf_conntrack_helpers_unregister(irc_ptr, ports_c); kfree(irc_buffer); } diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c index 55415f011943..89d1cf7d6512 100644 --- a/net/netfilter/nf_conntrack_netbios_ns.c +++ b/net/netfilter/nf_conntrack_netbios_ns.c @@ -44,27 +44,25 @@ static int netbios_ns_help(struct sk_buff *skb, unsigned int protoff, return nf_conntrack_broadcast_help(skb, ct, ctinfo, timeout); } -static struct nf_conntrack_helper helper __read_mostly = { - .name = HELPER_NAME, - .tuple.src.l3num = NFPROTO_IPV4, - .tuple.src.u.udp.port = cpu_to_be16(NMBD_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .me = THIS_MODULE, - .help = netbios_ns_help, - .expect_policy = &exp_policy, -}; +static struct nf_conntrack_helper helper __read_mostly; +static struct nf_conntrack_helper *helper_ptr __read_mostly; static int __init nf_conntrack_netbios_ns_init(void) { NF_CT_HELPER_BUILD_BUG_ON(0); exp_policy.timeout = timeout; - return nf_conntrack_helper_register(&helper); + + nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, HELPER_NAME, + NMBD_PORT, NMBD_PORT, NMBD_PORT, + &exp_policy, 0, netbios_ns_help, NULL, THIS_MODULE); + + return nf_conntrack_helper_register(&helper, &helper_ptr); } static void __exit nf_conntrack_netbios_ns_fini(void) { - nf_conntrack_helper_unregister(&helper); + nf_conntrack_helper_unregister(helper_ptr); } module_init(nf_conntrack_netbios_ns_init); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index befa7e83ee49..b429e648f06c 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -240,7 +240,8 @@ static int ctnetlink_dump_helpinfo(struct sk_buff *skb, if (nla_put_string(skb, CTA_HELP_NAME, helper->name)) goto nla_put_failure; - if (helper->to_nlattr) + if (rcu_access_pointer(helper->help) && + helper->to_nlattr) helper->to_nlattr(skb, ct); nla_nest_end(skb, nest_helper); @@ -1935,6 +1936,7 @@ static int ctnetlink_change_helper(struct nf_conn *ct, if (err < 0) return err; + rcu_read_lock(); /* don't change helper of sibling connections */ if (ct->master) { /* If we try to change the helper to the same thing twice, @@ -1943,27 +1945,27 @@ static int ctnetlink_change_helper(struct nf_conn *ct, */ err = -EBUSY; if (help) { - rcu_read_lock(); helper = rcu_dereference(help->helper); if (helper && !strcmp(helper->name, helpname)) err = 0; - rcu_read_unlock(); } - + rcu_read_unlock(); return err; } - if (!strcmp(helpname, "")) { - if (help && help->helper) { + if (!strcmp(helpname, "") && help) { + helper = rcu_dereference(help->helper); + if (helper) { /* we had a helper before ... */ nf_ct_remove_expectations(ct); RCU_INIT_POINTER(help->helper, NULL); + if (refcount_dec_and_test(&helper->ct_refcnt)) + kfree_rcu(helper, rcu); } - + rcu_read_unlock(); return 0; } - rcu_read_lock(); helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), nf_ct_protonum(ct)); if (helper == NULL) { @@ -1974,7 +1976,8 @@ static int ctnetlink_change_helper(struct nf_conn *ct, if (help) { if (rcu_access_pointer(help->helper) == helper) { /* update private helper data if allowed. */ - if (helper->from_nlattr) + if (rcu_access_pointer(helper->help) && + helper->from_nlattr) helper->from_nlattr(helpinfo, ct); err = 0; } else @@ -2289,11 +2292,16 @@ ctnetlink_create_conntrack(struct net *net, goto err2; } /* set private helper data if allowed. */ - if (helper->from_nlattr) + if (rcu_access_pointer(helper->help) && + helper->from_nlattr) helper->from_nlattr(helpinfo, ct); /* disable helper auto-assignment for this entry */ ct->status |= IPS_HELPER; + if (!refcount_inc_not_zero(&helper->ct_refcnt)) { + err = -ENOENT; + goto err2; + } RCU_INIT_POINTER(help->helper, helper); } } @@ -3173,7 +3181,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) if (l3proto && exp->tuple.src.l3num != l3proto) continue; - if (!net_eq(nf_ct_net(exp->master), net)) + if (!net_eq(nf_ct_exp_net(exp), net)) continue; if (cb->args[1]) { diff --git a/net/netfilter/nf_conntrack_ovs.c b/net/netfilter/nf_conntrack_ovs.c index a6988eeb1579..49d1511e9921 100644 --- a/net/netfilter/nf_conntrack_ovs.c +++ b/net/netfilter/nf_conntrack_ovs.c @@ -12,6 +12,9 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, u16 proto) { + int (*helper_cb)(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info conntrackinfo); const struct nf_conntrack_helper *helper; const struct nf_conn_help *help; unsigned int protoff; @@ -60,7 +63,11 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct, if (helper->tuple.dst.protonum != proto) return NF_ACCEPT; - err = helper->help(skb, protoff, ct, ctinfo); + helper_cb = rcu_dereference(helper->help); + if (!helper_cb) + return NF_ACCEPT; + + err = helper_cb(skb, protoff, ct, ctinfo); if (err != NF_ACCEPT) return err; diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index dc23e4181618..776505a78e64 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -124,65 +124,6 @@ static void pptp_expectfn(struct nf_conn *ct, } } -static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, - const struct nf_conntrack_tuple *t) -{ - const struct nf_conntrack_tuple_hash *h; - const struct nf_conntrack_zone *zone; - struct nf_conntrack_expect *exp; - struct nf_conn *sibling; - - pr_debug("trying to timeout ct or exp for tuple "); - nf_ct_dump_tuple(t); - - zone = nf_ct_zone(ct); - h = nf_conntrack_find_get(net, zone, t); - if (h) { - sibling = nf_ct_tuplehash_to_ctrack(h); - pr_debug("setting timeout of conntrack %p to 0\n", sibling); - sibling->proto.gre.timeout = 0; - sibling->proto.gre.stream_timeout = 0; - nf_ct_kill(sibling); - nf_ct_put(sibling); - return 1; - } else { - exp = nf_ct_expect_find_get(net, zone, t); - if (exp) { - pr_debug("unexpect_related of expect %p\n", exp); - nf_ct_unexpect_related(exp); - nf_ct_expect_put(exp); - return 1; - } - } - return 0; -} - -/* timeout GRE data connections */ -static void pptp_destroy_siblings(struct nf_conn *ct) -{ - struct net *net = nf_ct_net(ct); - const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); - struct nf_conntrack_tuple t; - - nf_ct_gre_keymap_destroy(ct); - - /* try original (pns->pac) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct_pptp_info->pns_call_id; - t.dst.u.gre.key = ct_pptp_info->pac_call_id; - if (!destroy_sibling_or_exp(net, ct, &t)) - pr_debug("failed to timeout original pns->pac ct/exp\n"); - - /* try reply (pac->pns) tuple */ - memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); - t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = ct_pptp_info->pac_call_id; - t.dst.u.gre.key = ct_pptp_info->pns_call_id; - if (!destroy_sibling_or_exp(net, ct, &t)) - pr_debug("failed to timeout reply pac->pns ct/exp\n"); -} - /* expect GRE connections (PNS->PAC and PAC->PNS direction) */ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) { @@ -257,6 +198,9 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, u_int16_t msg; __be16 cid = 0, pcid = 0; + if (!info) + return NF_DROP; + msg = ntohs(ctlh->messageType); pr_debug("inbound control message %s\n", pptp_msg_name(msg)); @@ -343,7 +287,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, info->cstate = PPTP_CALL_NONE; /* untrack this call id, unexpect GRE packets */ - pptp_destroy_siblings(ct); + gre_pptp_destroy_siblings(ct); break; case PPTP_WAN_ERROR_NOTIFY: @@ -384,6 +328,9 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, u_int16_t msg; __be16 cid = 0, pcid = 0; + if (!info) + return NF_DROP; + msg = ntohs(ctlh->messageType); pr_debug("outbound control message %s\n", pptp_msg_name(msg)); @@ -502,6 +449,9 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, int ret; u_int16_t msg; + if (!info) + return NF_DROP; + #if IS_ENABLED(CONFIG_NF_NAT) if (!nf_ct_is_confirmed(ct) && (ct->status & IPS_NAT_MASK)) { struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); @@ -582,27 +532,25 @@ static const struct nf_conntrack_expect_policy pptp_exp_policy = { }; /* control protocol helper */ -static struct nf_conntrack_helper pptp __read_mostly = { - .name = "pptp", - .me = THIS_MODULE, - .tuple.src.l3num = AF_INET, - .tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT), - .tuple.dst.protonum = IPPROTO_TCP, - .help = conntrack_pptp_help, - .destroy = pptp_destroy_siblings, - .expect_policy = &pptp_exp_policy, -}; +static struct nf_conntrack_helper pptp __read_mostly; +static struct nf_conntrack_helper *pptp_ptr __read_mostly; static int __init nf_conntrack_pptp_init(void) { NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_pptp_master)); - return nf_conntrack_helper_register(&pptp); + nf_ct_helper_init(&pptp, AF_INET, IPPROTO_TCP, + "pptp", PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, PPTP_CONTROL_PORT, + &pptp_exp_policy, 0, conntrack_pptp_help, NULL, THIS_MODULE); + + pptp.destroy = gre_pptp_destroy_siblings; + + return nf_conntrack_helper_register(&pptp, &pptp_ptr); } static void __exit nf_conntrack_pptp_fini(void) { - nf_conntrack_helper_unregister(&pptp); + nf_conntrack_helper_unregister(pptp_ptr); } module_init(nf_conntrack_pptp_init); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 50ddd3d613e1..ad96896516b6 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -129,6 +129,9 @@ unsigned int nf_confirm(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { + int (*helper_cb)(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info conntrackinfo); const struct nf_conn_help *help; enum ip_conntrack_info ctinfo; unsigned int protoff; @@ -175,11 +178,13 @@ unsigned int nf_confirm(void *priv, /* rcu_read_lock()ed by nf_hook */ helper = rcu_dereference(help->helper); if (helper) { - ret = helper->help(skb, - protoff, - ct, ctinfo); - if (ret != NF_ACCEPT) - return ret; + helper_cb = rcu_dereference(helper->help); + if (helper_cb) { + ret = helper_cb(skb, protoff, + ct, ctinfo); + if (ret != NF_ACCEPT) + return ret; + } } } diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 35e22082d65a..616ab1e2fc5e 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -131,6 +131,9 @@ bool nf_ct_gre_keymap_add(struct nf_conn *ct, struct nf_ct_gre_keymap *km_orig, *km_repl; bool ret = false; + if (!ct_pptp_info) + return false; + km_orig = kmalloc_obj(*km_orig, GFP_ATOMIC); if (!km_orig) return false; @@ -187,6 +190,9 @@ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); enum ip_conntrack_dir dir; + if (!ct_pptp_info) + return; + pr_debug("entering for ct %p\n", ct); spin_lock_bh(&keymap_lock); @@ -349,6 +355,70 @@ gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = { }; #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ +#if IS_ENABLED(CONFIG_NF_CONNTRACK_PPTP) +static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, + const struct nf_conntrack_tuple *t) +{ + const struct nf_conntrack_tuple_hash *h; + const struct nf_conntrack_zone *zone; + struct nf_conntrack_expect *exp; + struct nf_conn *sibling; + + pr_debug("trying to timeout ct or exp for tuple "); + nf_ct_dump_tuple(t); + + zone = nf_ct_zone(ct); + h = nf_conntrack_find_get(net, zone, t); + if (h) { + sibling = nf_ct_tuplehash_to_ctrack(h); + pr_debug("setting timeout of conntrack %p to 0\n", sibling); + sibling->proto.gre.timeout = 0; + sibling->proto.gre.stream_timeout = 0; + nf_ct_kill(sibling); + nf_ct_put(sibling); + return 1; + } else { + exp = nf_ct_expect_find_get(net, zone, t); + if (exp) { + pr_debug("unexpect_related of expect %p\n", exp); + nf_ct_unexpect_related(exp); + nf_ct_expect_put(exp); + return 1; + } + } + return 0; +} + +void gre_pptp_destroy_siblings(struct nf_conn *ct) +{ + struct net *net = nf_ct_net(ct); + const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); + struct nf_conntrack_tuple t; + + if (!ct_pptp_info) + return; + + nf_ct_gre_keymap_destroy(ct); + + /* try original (pns->pac) tuple */ + memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); + t.dst.protonum = IPPROTO_GRE; + t.src.u.gre.key = ct_pptp_info->pns_call_id; + t.dst.u.gre.key = ct_pptp_info->pac_call_id; + if (!destroy_sibling_or_exp(net, ct, &t)) + pr_debug("failed to timeout original pns->pac ct/exp\n"); + + /* try reply (pac->pns) tuple */ + memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); + t.dst.protonum = IPPROTO_GRE; + t.src.u.gre.key = ct_pptp_info->pac_call_id; + t.dst.u.gre.key = ct_pptp_info->pns_call_id; + if (!destroy_sibling_or_exp(net, ct, &t)) + pr_debug("failed to timeout reply pac->pns ct/exp\n"); +} +EXPORT_SYMBOL_GPL(gre_pptp_destroy_siblings); +#endif + void nf_conntrack_gre_init_net(struct net *net) { struct nf_gre_net *net_gre = gre_pernet(net); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index e99ab1e88e9f..ceeed3d7fe52 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -169,14 +169,14 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { /*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV }, /* * sNO -> sIV Too late and no reason to do anything... - * sSS -> sIV Client migth not send FIN in this state: + * sSS -> sIV Client might not send FIN in this state: * we enforce waiting for a SYN/ACK reply first. * sS2 -> sIV * sSR -> sFW Close started. * sES -> sFW * sFW -> sLA FIN seen in both directions, waiting for * the last ACK. - * Migth be a retransmitted FIN as well... + * Might be a retransmitted FIN as well... * sCW -> sLA * sLA -> sLA Retransmitted FIN. Remain in the same state. * sTW -> sTW @@ -405,11 +405,11 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, return; /* Fast path for timestamp-only option */ - if (length == TCPOLEN_TSTAMP_ALIGNED - && *(__be32 *)ptr == htonl((TCPOPT_NOP << 24) - | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) - | TCPOLEN_TIMESTAMP)) + if (length == TCPOLEN_TSTAMP_ALIGNED && + get_unaligned_be32(ptr) == ((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | + TCPOLEN_TIMESTAMP)) return; while (length > 0) { @@ -798,7 +798,7 @@ static void tcp_error_log(const struct sk_buff *skb, nf_l4proto_log_invalid(skb, state, IPPROTO_TCP, "%s", msg); } -/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ +/* Protect conntrack against broken packets. Code taken from ipt_unclean.c. */ static bool tcp_error(const struct tcphdr *th, struct sk_buff *skb, unsigned int dataoff, @@ -1098,7 +1098,7 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, } /* Mark the potential for RFC5961 challenge ACK, * this pose a special problem for LAST_ACK state - * as ACK is intrepretated as ACKing last FIN. + * as ACK is interpreted as ACKing last FIN. */ if (old_state == TCP_CONNTRACK_LAST_ACK) ct->proto.tcp.last_flags |= diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 13dc421fc4f5..39085acf7a71 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -74,6 +74,9 @@ static int help(struct sk_buff *skb, struct sane_reply_net_start repl; } buf; + if (!ct_sane_info) + return NF_DROP; + /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) @@ -167,6 +170,7 @@ static int help(struct sk_buff *skb, } static struct nf_conntrack_helper sane[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper *sane_ptr[MAX_PORTS * 2] __read_mostly; static const struct nf_conntrack_expect_policy sane_exp_policy = { .max_expected = 1, @@ -175,7 +179,7 @@ static const struct nf_conntrack_expect_policy sane_exp_policy = { static void __exit nf_conntrack_sane_fini(void) { - nf_conntrack_helpers_unregister(sane, ports_c * 2); + nf_conntrack_helpers_unregister(sane_ptr, ports_c * 2); } static int __init nf_conntrack_sane_init(void) @@ -200,7 +204,7 @@ static int __init nf_conntrack_sane_init(void) THIS_MODULE); } - ret = nf_conntrack_helpers_register(sane, ports_c * 2); + ret = nf_conntrack_helpers_register(sane, ports_c * 2, sane_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index 7ab2b25b57bc..220216a4edc5 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -17,12 +17,17 @@ int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, if (off == 0) return 0; - set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); - + spin_lock_bh(&ct->lock); seqadj = nfct_seqadj(ct); + if (!seqadj) { + spin_unlock_bh(&ct->lock); + return 0; + } + set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); this_way = &seqadj->seq[dir]; this_way->offset_before = off; this_way->offset_after = off; + spin_unlock_bh(&ct->lock); return 0; } EXPORT_SYMBOL_GPL(nf_ct_seqadj_init); @@ -37,10 +42,8 @@ int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, if (off == 0) return 0; - if (unlikely(!seqadj)) { - WARN_ONCE(1, "Missing nfct_seqadj_ext_add() setup call\n"); + if (unlikely(!seqadj)) return 0; - } set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); @@ -123,6 +126,9 @@ static unsigned int nf_ct_sack_adjust(struct sk_buff *skb, struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); unsigned int dir, optoff, optend; + if (!seqadj) + return 0; + optoff = protoff + sizeof(struct tcphdr); optend = protoff + tcph->doff * 4; @@ -173,6 +179,9 @@ int nf_ct_seq_adjust(struct sk_buff *skb, struct nf_ct_seqadj *this_way, *other_way; int res = 1; + if (!seqadj) + return 0; + this_way = &seqadj->seq[dir]; other_way = &seqadj->seq[!dir]; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index e69941f1a101..c606d1f60b58 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -887,6 +887,9 @@ static int refresh_signalling_expectation(struct nf_conn *ct, struct hlist_node *next; int found = 0; + if (!help) + return 0; + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if (exp->class != SIP_EXPECT_SIGNALLING || @@ -910,6 +913,9 @@ static void flush_expectations(struct nf_conn *ct, bool media) struct nf_conntrack_expect *exp; struct hlist_node *next; + if (!help) + return; + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) @@ -940,6 +946,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, u_int16_t base_port; __be16 rtp_port, rtcp_port; const struct nf_nat_sip_hooks *hooks; + struct nf_conn_help *help; + + help = nfct_help(ct); + if (!help) + return NF_DROP; saddr = NULL; if (sip_direct_media) { @@ -1002,7 +1013,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple); if (!exp || exp->master == ct || - exp->helper != nfct_help(ct)->helper || + exp->helper != help->helper || exp->class != class) break; #if IS_ENABLED(CONFIG_NF_NAT) @@ -1227,6 +1238,9 @@ static int process_invite_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + if (!ct_sip_info) + return NF_DROP; + if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1244,6 +1258,9 @@ static int process_update_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + if (!ct_sip_info) + return NF_DROP; + if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1261,6 +1278,9 @@ static int process_prack_response(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + if (!ct_sip_info) + return NF_DROP; + if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1279,6 +1299,9 @@ static int process_invite_request(struct sk_buff *skb, unsigned int protoff, struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); unsigned int ret; + if (!ct_sip_info) + return NF_DROP; + flush_expectations(ct, true); ret = process_sdp(skb, protoff, dataoff, dptr, datalen, cseq); if (ret == NF_ACCEPT) @@ -1316,11 +1339,15 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, union nf_inet_addr *saddr, daddr; const struct nf_nat_sip_hooks *hooks; struct nf_conntrack_helper *helper; + struct nf_conn_help *help; __be16 port; u8 proto; unsigned int expires = 0; int ret; + if (!ct_sip_info) + return NF_DROP; + /* Expected connections can not register again. */ if (ct->status & IPS_EXPECTED) return NF_ACCEPT; @@ -1366,7 +1393,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, goto store_cseq; } - helper = rcu_dereference(nfct_help(ct)->helper); + help = nfct_help(ct); + if (!help) + return NF_DROP; + + helper = rcu_dereference(help->helper); if (!helper) return NF_DROP; @@ -1421,6 +1452,9 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff, unsigned int expires = 0; int in_contact = 0, ret; + if (!ct_sip_info) + return NF_DROP; + /* According to RFC 3261, "UAs MUST NOT send a new registration until * they have received a final response from the registrar for the * previous one or the previous REGISTER request has timed out". @@ -1550,6 +1584,9 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, union nf_inet_addr addr; __be16 port; + if (!ct_sip_info) + return NF_DROP; + /* Many Cisco IP phones use a high source port for SIP requests, but * listen for the response on port 5060. If we are the local * router for one of these phones, save the port number from the @@ -1731,6 +1768,7 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff, } static struct nf_conntrack_helper sip[MAX_PORTS * 4] __read_mostly; +static struct nf_conntrack_helper *sip_ptr[MAX_PORTS * 4] __read_mostly; static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = { [SIP_EXPECT_SIGNALLING] = { @@ -1757,7 +1795,7 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1 static void __exit nf_conntrack_sip_fini(void) { - nf_conntrack_helpers_unregister(sip, ports_c * 4); + nf_conntrack_helpers_unregister(sip_ptr, ports_c * 4); } static int __init nf_conntrack_sip_init(void) @@ -1788,7 +1826,7 @@ static int __init nf_conntrack_sip_init(void) NULL, THIS_MODULE); } - ret = nf_conntrack_helpers_register(sip, ports_c * 4); + ret = nf_conntrack_helpers_register(sip, ports_c * 4, sip_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c index 7b7eed43c54f..b6fce5703fce 100644 --- a/net/netfilter/nf_conntrack_snmp.c +++ b/net/netfilter/nf_conntrack_snmp.c @@ -47,25 +47,24 @@ static struct nf_conntrack_expect_policy exp_policy = { .max_expected = 1, }; -static struct nf_conntrack_helper helper __read_mostly = { - .name = "snmp", - .tuple.src.l3num = NFPROTO_IPV4, - .tuple.src.u.udp.port = cpu_to_be16(SNMP_PORT), - .tuple.dst.protonum = IPPROTO_UDP, - .me = THIS_MODULE, - .help = snmp_conntrack_help, - .expect_policy = &exp_policy, -}; +static struct nf_conntrack_helper helper __read_mostly; +static struct nf_conntrack_helper *helper_ptr __read_mostly; static int __init nf_conntrack_snmp_init(void) { exp_policy.timeout = timeout; - return nf_conntrack_helper_register(&helper); + + nf_ct_helper_init(&helper, AF_INET, IPPROTO_UDP, + "snmp", SNMP_PORT, SNMP_PORT, SNMP_PORT, + &exp_policy, 0, snmp_conntrack_help, NULL, + THIS_MODULE); + + return nf_conntrack_helper_register(&helper, &helper_ptr); } static void __exit nf_conntrack_snmp_fini(void) { - nf_conntrack_helper_unregister(&helper); + nf_conntrack_helper_unregister(helper_ptr); } module_init(nf_conntrack_snmp_init); diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index a2e6833a0bf7..4393c435aa35 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -96,6 +96,7 @@ static int tftp_help(struct sk_buff *skb, } static struct nf_conntrack_helper tftp[MAX_PORTS * 2] __read_mostly; +static struct nf_conntrack_helper *tftp_ptr[MAX_PORTS * 2] __read_mostly; static const struct nf_conntrack_expect_policy tftp_exp_policy = { .max_expected = 1, @@ -104,7 +105,7 @@ static const struct nf_conntrack_expect_policy tftp_exp_policy = { static void __exit nf_conntrack_tftp_fini(void) { - nf_conntrack_helpers_unregister(tftp, ports_c * 2); + nf_conntrack_helpers_unregister(tftp_ptr, ports_c * 2); } static int __init nf_conntrack_tftp_init(void) @@ -127,7 +128,7 @@ static int __init nf_conntrack_tftp_init(void) THIS_MODULE); } - ret = nf_conntrack_helpers_register(tftp, ports_c * 2); + ret = nf_conntrack_helpers_register(tftp, ports_c * 2, tftp_ptr); if (ret < 0) { pr_err("failed to register helpers\n"); return ret; diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c index 0cc584d3dbb1..c81becde2afa 100644 --- a/net/netfilter/nf_conntrack_timeout.c +++ b/net/netfilter/nf_conntrack_timeout.c @@ -25,17 +25,32 @@ const struct nf_ct_timeout_hooks __rcu *nf_ct_timeout_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_timeout_hook); +/* nf_ct_iterate_cleanup() holds the conntrack lock. */ static int untimeout(struct nf_conn *ct, void *timeout) { struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct); if (timeout_ext) { - const struct nf_ct_timeout *t; + struct nf_ct_timeout *t; - t = rcu_access_pointer(timeout_ext->timeout); + rcu_read_lock(); + t = rcu_dereference(timeout_ext->timeout); + if (!t) { + rcu_read_unlock(); + return 0; + } - if (!timeout || t == timeout) + if (!timeout || t == timeout) { RCU_INIT_POINTER(timeout_ext->timeout, NULL); + + /* No race with nf_conntrack_free() which is called + * only after the conntrack has been removed from + * the hashes. + */ + if (refcount_dec_and_test(&t->refcnt)) + kfree_rcu(t, rcu); + } + rcu_read_unlock(); } /* We are not intended to delete this conntrack. */ @@ -70,6 +85,8 @@ int nf_ct_set_timeout(struct net *net, struct nf_conn *ct, const char *errmsg = NULL; int ret = 0; + WARN_ON_ONCE(!nf_ct_is_template(ct)); + rcu_read_lock(); h = rcu_dereference(nf_ct_timeout_hook); if (!h) { @@ -127,6 +144,8 @@ void nf_ct_destroy_timeout(struct nf_conn *ct) struct nf_conn_timeout *timeout_ext; const struct nf_ct_timeout_hooks *h; + WARN_ON_ONCE(!nf_ct_is_template(ct)); + rcu_read_lock(); h = rcu_dereference(nf_ct_timeout_hook); @@ -139,6 +158,8 @@ void nf_ct_destroy_timeout(struct nf_conn *ct) if (t) h->timeout_put(t); RCU_INIT_POINTER(timeout_ext->timeout, NULL); + if (t && refcount_dec_and_test(&t->refcnt)) + kfree_rcu(t, rcu); } } rcu_read_unlock(); diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index 3d88ef927f31..c6bd5c29bed6 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -16,11 +16,6 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev, enum nf_dev_hooks hook) { - u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion(); - - if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) - goto err; - if (hook == NF_NETDEV_INGRESS && skb_mac_header_was_set(skb)) { if (skb_cow_head(skb, skb->mac_len)) goto err; @@ -30,9 +25,15 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev, skb->dev = dev; skb_clear_tstamp(skb); - (*nf_dup_skb_recursion)++; + local_bh_disable(); + if (nf_dev_xmit_recursion()) { + local_bh_enable(); + goto err; + } + nf_dev_xmit_recursion_inc(); dev_queue_xmit(skb); - (*nf_dup_skb_recursion)--; + nf_dev_xmit_recursion_dec(); + local_bh_enable(); return; err: kfree_skb(skb); diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c index 9e88ea6a2eef..1e7e216b9f89 100644 --- a/net/netfilter/nf_flow_table_path.c +++ b/net/netfilter/nf_flow_table_path.c @@ -90,9 +90,9 @@ struct nft_forward_info { enum flow_offload_xmit_type xmit_type; }; -static void nft_dev_path_info(const struct net_device_path_stack *stack, - struct nft_forward_info *info, - unsigned char *ha, struct nf_flowtable *flowtable) +static int nft_dev_path_info(const struct net_device_path_stack *stack, + struct nft_forward_info *info, + unsigned char *ha, struct nf_flowtable *flowtable) { const struct net_device_path *path; int i; @@ -120,19 +120,17 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, /* DEV_PATH_VLAN, DEV_PATH_PPPOE and DEV_PATH_TUN */ if (path->type == DEV_PATH_TUN) { - if (info->num_tuns) { - info->indev = NULL; - break; - } + if (info->num_tuns) + return -1; + info->tun.src_v6 = path->tun.src_v6; info->tun.dst_v6 = path->tun.dst_v6; info->tun.l3_proto = path->tun.l3_proto; info->num_tuns++; } else { - if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { - info->indev = NULL; - break; - } + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) + return -1; + info->encap[info->num_encaps].id = path->encap.id; info->encap[info->num_encaps].proto = @@ -151,22 +149,24 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, switch (path->bridge.vlan_mode) { case DEV_PATH_BR_VLAN_UNTAG_HW: + if (info->num_encaps == 0) + return -1; + info->ingress_vlans |= BIT(info->num_encaps - 1); break; case DEV_PATH_BR_VLAN_TAG: - if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { - info->indev = NULL; - break; - } + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) + return -1; + info->encap[info->num_encaps].id = path->bridge.vlan_id; info->encap[info->num_encaps].proto = path->bridge.vlan_proto; info->num_encaps++; break; case DEV_PATH_BR_VLAN_UNTAG: - if (WARN_ON_ONCE(info->num_encaps-- == 0)) { - info->indev = NULL; - break; - } + if (info->num_encaps == 0) + return -1; + + info->num_encaps--; break; case DEV_PATH_BR_VLAN_KEEP: break; @@ -174,8 +174,7 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; break; default: - info->indev = NULL; - break; + return -1; } } info->outdev = info->indev; @@ -183,6 +182,8 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, if (nf_flowtable_hw_offload(flowtable) && nft_is_valid_ether_device(info->indev)) info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; + + return 0; } static bool nft_flowtable_find_dev(const struct net_device *dev, @@ -240,11 +241,11 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt, return 0; } -static void nft_dev_forward_path(const struct nft_pktinfo *pkt, - struct nf_flow_route *route, - const struct nf_conn *ct, - enum ip_conntrack_dir dir, - struct nft_flowtable *ft) +static int nft_dev_forward_path(const struct nft_pktinfo *pkt, + struct nf_flow_route *route, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, + struct nft_flowtable *ft) { const struct dst_entry *dst = route->tuple[dir].dst; struct net_device_path_stack stack; @@ -252,15 +253,16 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, unsigned char ha[ETH_ALEN]; int i; - if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) - nft_dev_path_info(&stack, &info, ha, &ft->data); + if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) < 0 || + nft_dev_path_info(&stack, &info, ha, &ft->data) < 0) + return -ENOENT; + + if (!nft_flowtable_find_dev(info.indev, ft)) + return -ENOENT; if (info.outdev) route->tuple[dir].out.ifindex = info.outdev->ifindex; - if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) - return; - route->tuple[!dir].in.ifindex = info.indev->ifindex; for (i = 0; i < info.num_encaps; i++) { route->tuple[!dir].in.encap[i].id = info.encap[i].id; @@ -284,6 +286,8 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, route->tuple[dir].xmit_type = info.xmit_type; } route->tuple[dir].out.needs_gso_segment = info.needs_gso_segment; + + return 0; } int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, @@ -328,11 +332,19 @@ int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, nft_default_forward_path(route, this_dst, dir); nft_default_forward_path(route, other_dst, !dir); - if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) - nft_dev_forward_path(pkt, route, ct, dir, ft); - if (route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) - nft_dev_forward_path(pkt, route, ct, !dir, ft); + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && + nft_dev_forward_path(pkt, route, ct, dir, ft) < 0) + goto err_dst_release; + + if (route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && + nft_dev_forward_path(pkt, route, ct, !dir, ft) < 0) + goto err_dst_release; return 0; + +err_dst_release: + dst_release(route->tuple[dir].dst); + dst_release(route->tuple[!dir].dst); + return -ENOENT; } EXPORT_SYMBOL_GPL(nft_flow_route); diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 00838c0cc5bb..67c04d8143ab 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -106,6 +106,9 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, union nf_inet_addr newaddr; __be16 newport; + if (!ct_sip_info) + return 0; + if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) && ct->tuplehash[dir].tuple.src.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.dst.u3; @@ -158,6 +161,9 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, __be16 port; int request, in_header; + if (!ct_sip_info) + return NF_DROP; + /* Basic rules: requests and responses. */ if (strncasecmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { if (ct_sip_parse_request(ct, *dptr, *datalen, @@ -326,6 +332,9 @@ static void nf_nat_sip_expected(struct nf_conn *ct, int range_set_for_snat = 0; struct nf_nat_range2 range; + if (!help) + return; + /* This must be a fresh one. */ BUG_ON(ct->status & IPS_NAT_DONE_MASK); @@ -390,6 +399,9 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; unsigned int buflen; + if (!ct_sip_info) + return NF_DROP; + /* Connection will come from reply */ if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3)) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index ed00114f65f3..acd360515972 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -184,23 +184,23 @@ synproxy_check_timestamp_cookie(struct synproxy_options *opts) opts->options |= opts->tsecr & (1 << 5) ? NF_SYNPROXY_OPT_ECN : 0; } -static unsigned int +static bool synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, struct tcphdr *th, struct nf_conn *ct, enum ip_conntrack_info ctinfo, const struct nf_conn_synproxy *synproxy) { unsigned int optoff, optend; - __be32 *ptr, old; + u32 new, old; if (synproxy->tsoff == 0) - return 1; + return true; optoff = protoff + sizeof(struct tcphdr); optend = protoff + th->doff * 4; if (skb_ensure_writable(skb, optend)) - return 0; + return false; th = (struct tcphdr *)(skb->data + protoff); @@ -209,7 +209,7 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, switch (op[0]) { case TCPOPT_EOL: - return 1; + return true; case TCPOPT_NOP: optoff++; continue; @@ -217,28 +217,26 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, if (optoff + 1 == optend || optoff + op[1] > optend || op[1] < 2) - return 0; + return true; if (op[0] == TCPOPT_TIMESTAMP && op[1] == TCPOLEN_TIMESTAMP) { if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { - ptr = (__be32 *)&op[2]; - old = *ptr; - *ptr = htonl(ntohl(*ptr) - - synproxy->tsoff); + old = get_unaligned_be32(&op[2]); + new = old - synproxy->tsoff; + put_unaligned_be32(new, &op[2]); } else { - ptr = (__be32 *)&op[6]; - old = *ptr; - *ptr = htonl(ntohl(*ptr) + - synproxy->tsoff); + old = get_unaligned_be32(&op[6]); + new = old + synproxy->tsoff; + put_unaligned_be32(new, &op[6]); } inet_proto_csum_replace4(&th->check, skb, - old, *ptr, false); - return 1; + cpu_to_be32(old), + cpu_to_be32(new), false); } optoff += op[1]; } } - return 1; + return true; } #ifdef CONFIG_PROC_FS @@ -749,7 +747,9 @@ ipv4_synproxy_hook(void *priv, struct sk_buff *skb, break; } - synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + if (!synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy)) + return NF_DROP_REASON(skb, SKB_DROP_REASON_NETFILTER_DROP, ENOMEM); + return NF_ACCEPT; } EXPORT_SYMBOL_GPL(ipv4_synproxy_hook); @@ -1177,7 +1177,9 @@ ipv6_synproxy_hook(void *priv, struct sk_buff *skb, break; } - synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + if (!synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy)) + return NF_DROP_REASON(skb, SKB_DROP_REASON_NETFILTER_DROP, ENOMEM); + return NF_ACCEPT; } EXPORT_SYMBOL_GPL(ipv6_synproxy_hook); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 87387adbca65..4884f7f7aaee 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3378,8 +3378,10 @@ static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info, */ int nft_register_expr(struct nft_expr_type *type) { - if (WARN_ON_ONCE(type->maxattr > NFT_EXPR_MAXATTR)) + if (unlikely(type->maxattr > NFT_EXPR_MAXATTR)) { + DEBUG_NET_WARN_ON_ONCE(1); return -ENOMEM; + } nfnl_lock(NFNL_SUBSYS_NFTABLES); if (type->family == NFPROTO_UNSPEC) @@ -3691,8 +3693,10 @@ int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src, gfp_t gfp) { int err; - if (WARN_ON_ONCE(!src->ops->clone)) + if (unlikely(!src->ops->clone)) { + DEBUG_NET_WARN_ON_ONCE(1); return -EINVAL; + } dst->ops = src->ops; err = src->ops->clone(dst, src, gfp); @@ -8327,8 +8331,10 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, return 0; type = nft_obj_type_get(net, objtype, family); - if (WARN_ON_ONCE(IS_ERR(type))) + if (IS_ERR(type)) { + DEBUG_NET_WARN_ON_ONCE(1); return PTR_ERR(type); + } nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); @@ -10306,19 +10312,25 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha prule = (struct nft_rule_dp *)data; data += offsetof(struct nft_rule_dp, data); - if (WARN_ON_ONCE(data > data_boundary)) + if (unlikely(data > data_boundary)) { + DEBUG_NET_WARN_ON_ONCE(1); return -ENOMEM; + } size = 0; nft_rule_for_each_expr(expr, last, rule) { - if (WARN_ON_ONCE(data + size + expr->ops->size > data_boundary)) + if (unlikely(data + size + expr->ops->size > data_boundary)) { + DEBUG_NET_WARN_ON_ONCE(1); return -ENOMEM; + } memcpy(data + size, expr, expr->ops->size); size += expr->ops->size; } - if (WARN_ON_ONCE(size >= 1 << 12)) + if (unlikely(size >= 1 << 12)) { + DEBUG_NET_WARN_ON_ONCE(1); return -ENOMEM; + } prule->handle = rule->handle; prule->dlen = size; @@ -10329,8 +10341,10 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha chain->blob_next->size += (unsigned long)(data - (void *)prule); } - if (WARN_ON_ONCE(data > data_boundary)) + if (unlikely(data > data_boundary)) { + DEBUG_NET_WARN_ON_ONCE(1); return -ENOMEM; + } prule = (struct nft_rule_dp *)data; nft_last_rule(chain, prule); @@ -11636,8 +11650,10 @@ int nft_parse_register_load(const struct nft_ctx *ctx, next_register = DIV_ROUND_UP(len, NFT_REG32_SIZE) + reg; /* Can't happen: nft_validate_register_load() should have failed */ - if (WARN_ON_ONCE(next_register > NFT_REG32_NUM)) + if (unlikely(next_register > NFT_REG32_NUM)) { + DEBUG_NET_WARN_ON_ONCE(1); return -EINVAL; + } /* find first register that did not see an earlier store. */ invalid_reg = find_next_zero_bit(ctx->reg_inited, NFT_REG32_NUM, reg); @@ -11884,8 +11900,10 @@ int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, struct nlattr *tb[NFTA_DATA_MAX + 1]; int err; - if (WARN_ON_ONCE(!desc->size)) + if (unlikely(!desc->size)) { + DEBUG_NET_WARN_ON_ONCE(1); return -EINVAL; + } err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla, nft_data_policy, NULL); @@ -11950,7 +11968,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, break; default: err = -EINVAL; - WARN_ON(1); + DEBUG_NET_WARN_ON_ONCE(1); } nla_nest_end(skb, nest); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 8ab186f86dd4..01a72f334dc6 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -314,8 +314,10 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv) switch (regs.verdict.code) { case NFT_JUMP: - if (WARN_ON_ONCE(stackptr >= NFT_JUMP_STACK_SIZE)) - return NF_DROP; + if (unlikely(stackptr >= NFT_JUMP_STACK_SIZE)) { + DEBUG_NET_WARN_ON_ONCE(1); + return NF_DROP_REASON(pkt->skb, SKB_DROP_REASON_NETFILTER_DROP, ELOOP); + } jumpstack[stackptr].rule = nft_rule_next(rule); stackptr++; fallthrough; @@ -326,7 +328,7 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv) case NFT_RETURN: break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); } if (stackptr > 0) { diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c index 9101b1703b52..8998a24651ff 100644 --- a/net/netfilter/nf_tables_offload.c +++ b/net/netfilter/nf_tables_offload.c @@ -361,7 +361,7 @@ static int nft_block_setup(struct nft_base_chain *basechain, err = nft_flow_offload_unbind(bo, basechain); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); err = -EOPNOTSUPP; } diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index a88abae5a9de..d85b6a2fb43c 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -227,8 +227,10 @@ static const struct nft_chain *nft_trace_get_chain(const struct nft_rule_dp *rul last = (const struct nft_rule_dp_last *)rule; - if (WARN_ON_ONCE(!last->chain)) + if (unlikely(!last->chain)) { + DEBUG_NET_WARN_ON_ONCE(1); return &info->basechain->chain; + } return last->chain; } @@ -354,7 +356,7 @@ void nft_trace_notify(const struct nft_pktinfo *pkt, return; nla_put_failure: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); kfree_skb(skb); } diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 0d16ad82d70c..f1460b683d7a 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -32,7 +32,7 @@ MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers"); struct nfnl_cthelper { struct list_head list; - struct nf_conntrack_helper helper; + struct nf_conntrack_helper *helper; }; static LIST_HEAD(nfnl_cthelper_list); @@ -41,8 +41,9 @@ static int nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - const struct nf_conn_help *help; struct nf_conntrack_helper *helper; + const struct nf_conn_help *help; + unsigned int helper_flags; help = nfct_help(ct); if (help == NULL) @@ -53,8 +54,10 @@ nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, if (helper == NULL) return NF_DROP; + helper_flags = READ_ONCE(helper->flags); + /* This is a user-space helper not yet configured, skip. */ - if ((helper->flags & + if ((helper_flags & (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) == NF_CT_HELPER_F_USERSPACE) return NF_ACCEPT; @@ -98,6 +101,9 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) struct nf_conn_help *help = nfct_help(ct); const struct nf_conntrack_helper *helper; + if (!help) + return -EINVAL; + if (attr == NULL) return -EINVAL; @@ -115,6 +121,9 @@ nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct) const struct nf_conn_help *help = nfct_help(ct); const struct nf_conntrack_helper *helper; + if (!help) + return 0; + helper = rcu_dereference(help->helper); if (helper && helper->data_len && nla_put(skb, CTA_HELP_INFO, helper->data_len, &help->data)) @@ -173,7 +182,6 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, const struct nlattr *attr) { int i, ret; - struct nf_conntrack_expect_policy *expect_policy; struct nlattr *tb[NFCTH_POLICY_SET_MAX+1]; unsigned int class_max; @@ -192,26 +200,19 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, if (class_max > NF_CT_MAX_EXPECT_CLASSES) return -EOVERFLOW; - expect_policy = kzalloc_objs(struct nf_conntrack_expect_policy, - class_max); - if (expect_policy == NULL) - return -ENOMEM; - for (i = 0; i < class_max; i++) { if (!tb[NFCTH_POLICY_SET+i]) goto err; - ret = nfnl_cthelper_expect_policy(&expect_policy[i], + ret = nfnl_cthelper_expect_policy(&helper->expect_policy[i], tb[NFCTH_POLICY_SET+i]); if (ret < 0) goto err; } helper->expect_class_max = class_max - 1; - helper->expect_policy = expect_policy; return 0; err: - kfree(expect_policy); return -EINVAL; } @@ -227,21 +228,28 @@ nfnl_cthelper_create(const struct nlattr * const tb[], if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN]) return -EINVAL; - nfcth = kzalloc_obj(*nfcth); + nfcth = kzalloc_obj(*nfcth, GFP_KERNEL_ACCOUNT); if (nfcth == NULL) return -ENOMEM; - helper = &nfcth->helper; + + helper = kzalloc_obj(*helper, GFP_KERNEL_ACCOUNT); + if (!helper) { + ret = -ENOMEM; + goto err_cth; + } + + nfcth->helper = helper; ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]); if (ret < 0) - goto err1; + goto err_helper; nla_strscpy(helper->name, tb[NFCTH_NAME], NF_CT_HELPER_NAME_LEN); size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); if (size > sizeof_field(struct nf_conn_help, data)) { ret = -ENOMEM; - goto err2; + goto err_helper; } helper->data_len = size; @@ -270,15 +278,15 @@ nfnl_cthelper_create(const struct nlattr * const tb[], } } - ret = nf_conntrack_helper_register(helper); + ret = __nf_conntrack_helper_register(helper); if (ret < 0) - goto err2; + goto err_helper; list_add_tail(&nfcth->list, &nfnl_cthelper_list); return 0; -err2: - kfree(helper->expect_policy); -err1: +err_helper: + kfree(helper); +err_cth: kfree(nfcth); return ret; } @@ -346,8 +354,8 @@ static int nfnl_cthelper_update_policy_all(struct nlattr *tb[], for (i = 0; i < helper->expect_class_max + 1; i++) { policy = (struct nf_conntrack_expect_policy *) &helper->expect_policy[i]; - policy->max_expected = new_policy->max_expected; - policy->timeout = new_policy->timeout; + policy->max_expected = new_policy[i].max_expected; + policy->timeout = new_policy[i].timeout; } err: @@ -404,10 +412,10 @@ nfnl_cthelper_update(const struct nlattr * const tb[], switch(status) { case NFCT_HELPER_STATUS_ENABLED: - helper->flags |= NF_CT_HELPER_F_CONFIGURED; + WRITE_ONCE(helper->flags, helper->flags | NF_CT_HELPER_F_CONFIGURED); break; case NFCT_HELPER_STATUS_DISABLED: - helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; + WRITE_ONCE(helper->flags, helper->flags & ~NF_CT_HELPER_F_CONFIGURED); break; } } @@ -436,7 +444,7 @@ static int nfnl_cthelper_new(struct sk_buff *skb, const struct nfnl_info *info, return ret; list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) continue; @@ -529,8 +537,8 @@ static int nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, struct nf_conntrack_helper *helper) { - struct nlmsghdr *nlh; unsigned int flags = portid ? NLM_F_MULTI : 0; + struct nlmsghdr *nlh; int status; event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event); @@ -554,7 +562,7 @@ nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len))) goto nla_put_failure; - if (helper->flags & NF_CT_HELPER_F_CONFIGURED) + if (READ_ONCE(helper->flags) & NF_CT_HELPER_F_CONFIGURED) status = NFCT_HELPER_STATUS_ENABLED; else status = NFCT_HELPER_STATUS_DISABLED; @@ -575,6 +583,7 @@ static int nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { struct nf_conntrack_helper *cur, *last; + unsigned int helper_flags; rcu_read_lock(); last = (struct nf_conntrack_helper *)cb->args[1]; @@ -583,8 +592,10 @@ nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[cb->args[0]], hnode) { + helper_flags = READ_ONCE(cur->flags); + /* skip non-userspace conntrack helpers. */ - if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + if (!(helper_flags & NF_CT_HELPER_F_USERSPACE)) continue; if (cb->args[1]) { @@ -644,7 +655,7 @@ static int nfnl_cthelper_get(struct sk_buff *skb, const struct nfnl_info *info, } list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; if (helper_name && strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) continue; @@ -702,7 +713,7 @@ static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info, ret = -ENOENT; list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; j++; if (helper_name && @@ -714,16 +725,11 @@ static int nfnl_cthelper_del(struct sk_buff *skb, const struct nfnl_info *info, tuple.dst.protonum != cur->tuple.dst.protonum)) continue; - if (refcount_dec_if_one(&cur->refcnt)) { - found = true; - nf_conntrack_helper_unregister(cur); - kfree(cur->expect_policy); + found = true; + nf_conntrack_helper_unregister(cur); - list_del(&nlcth->list); - kfree(nlcth); - } else { - ret = -EBUSY; - } + list_del(&nlcth->list); + kfree(nlcth); } /* Make sure we return success if we flush and there is no helpers */ @@ -790,10 +796,9 @@ static void __exit nfnl_cthelper_exit(void) nfnetlink_subsys_unregister(&nfnl_cthelper_subsys); list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; + cur = nlcth->helper; nf_conntrack_helper_unregister(cur); - kfree(cur->expect_policy); kfree(nlcth); } } diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index dca6826af7de..170d3db860c5 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -37,11 +37,8 @@ struct ctnl_timeout { struct list_head head; struct list_head free_head; struct rcu_head rcu_head; - refcount_t refcnt; char name[CTNL_TIMEOUT_NAME_MAX]; - - /* must be at the end */ - struct nf_ct_timeout timeout; + struct nf_ct_timeout *timeout; }; struct nfct_timeout_pernet { @@ -132,12 +129,12 @@ static int cttimeout_new_timeout(struct sk_buff *skb, /* You cannot replace one timeout policy by another of * different kind, sorry. */ - if (matching->timeout.l3num != l3num || - matching->timeout.l4proto->l4proto != l4num) + if (matching->timeout->l3num != l3num || + matching->timeout->l4proto->l4proto != l4num) return -EINVAL; - return ctnl_timeout_parse_policy(&matching->timeout.data, - matching->timeout.l4proto, + return ctnl_timeout_parse_policy(&matching->timeout->data, + matching->timeout->l4proto, info->net, cda[CTA_TIMEOUT_DATA]); } @@ -153,26 +150,35 @@ static int cttimeout_new_timeout(struct sk_buff *skb, goto err_proto_put; } - timeout = kzalloc(sizeof(struct ctnl_timeout) + - l4proto->ctnl_timeout.obj_size, GFP_KERNEL); + timeout = kzalloc(sizeof(*timeout), GFP_KERNEL); if (timeout == NULL) { ret = -ENOMEM; goto err_proto_put; } - ret = ctnl_timeout_parse_policy(&timeout->timeout.data, l4proto, + timeout->timeout = kzalloc(sizeof(*timeout->timeout) + + l4proto->ctnl_timeout.obj_size, GFP_KERNEL); + if (!timeout->timeout) { + ret = -ENOMEM; + goto err; + } + + ret = ctnl_timeout_parse_policy(&timeout->timeout->data, l4proto, info->net, cda[CTA_TIMEOUT_DATA]); if (ret < 0) - goto err; + goto err_free_timeout_policy; strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME])); - timeout->timeout.l3num = l3num; - timeout->timeout.l4proto = l4proto; - refcount_set(&timeout->refcnt, 1); + timeout->timeout->l3num = l3num; + timeout->timeout->l4proto = l4proto; + refcount_set(&timeout->timeout->refcnt, 1); __module_get(THIS_MODULE); list_add_tail_rcu(&timeout->head, &pernet->nfct_timeout_list); return 0; + +err_free_timeout_policy: + kfree(timeout->timeout); err: kfree(timeout); err_proto_put: @@ -185,7 +191,7 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, { struct nlmsghdr *nlh; unsigned int flags = portid ? NLM_F_MULTI : 0; - const struct nf_conntrack_l4proto *l4proto = timeout->timeout.l4proto; + const struct nf_conntrack_l4proto *l4proto = timeout->timeout->l4proto; struct nlattr *nest_parms; int ret; @@ -197,17 +203,17 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (nla_put_string(skb, CTA_TIMEOUT_NAME, timeout->name) || nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, - htons(timeout->timeout.l3num)) || + htons(timeout->timeout->l3num)) || nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto) || nla_put_be32(skb, CTA_TIMEOUT_USE, - htonl(refcount_read(&timeout->refcnt)))) + htonl(refcount_read(&timeout->timeout->refcnt)))) goto nla_put_failure; nest_parms = nla_nest_start(skb, CTA_TIMEOUT_DATA); if (!nest_parms) goto nla_put_failure; - ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->timeout.data); + ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->timeout->data); if (ret < 0) goto nla_put_failure; @@ -307,23 +313,17 @@ static int cttimeout_get_timeout(struct sk_buff *skb, return ret; } -/* try to delete object, fail if it is still in use. */ -static int ctnl_timeout_try_del(struct net *net, struct ctnl_timeout *timeout) +static void ctnl_timeout_del(struct net *net, struct ctnl_timeout *timeout) { - int ret = 0; + /* We are protected by nfnl mutex. */ + list_del_rcu(&timeout->head); + nf_ct_untimeout(net, timeout->timeout); - /* We want to avoid races with ctnl_timeout_put. So only when the - * current refcnt is 1, we decrease it to 0. - */ - if (refcount_dec_if_one(&timeout->refcnt)) { - /* We are protected by nfnl mutex. */ - list_del_rcu(&timeout->head); - nf_ct_untimeout(net, &timeout->timeout); - kfree_rcu(timeout, rcu_head); - } else { - ret = -EBUSY; - } - return ret; + if (refcount_dec_and_test(&timeout->timeout->refcnt)) + kfree_rcu(timeout->timeout, rcu); + + kfree_rcu(timeout, rcu_head); + module_put(THIS_MODULE); } static int cttimeout_del_timeout(struct sk_buff *skb, @@ -338,7 +338,7 @@ static int cttimeout_del_timeout(struct sk_buff *skb, if (!cda[CTA_TIMEOUT_NAME]) { list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) - ctnl_timeout_try_del(info->net, cur); + ctnl_timeout_del(info->net, cur); return 0; } @@ -348,10 +348,8 @@ static int cttimeout_del_timeout(struct sk_buff *skb, if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) continue; - ret = ctnl_timeout_try_del(info->net, cur); - if (ret < 0) - return ret; - + ctnl_timeout_del(info->net, cur); + ret = 0; break; } return ret; @@ -511,24 +509,22 @@ static struct nf_ct_timeout *ctnl_timeout_find_get(struct net *net, if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) continue; - if (!refcount_inc_not_zero(&timeout->refcnt)) + if (!refcount_inc_not_zero(&timeout->timeout->refcnt)) goto err; matching = timeout; + __module_get(THIS_MODULE); break; } err: - return matching ? &matching->timeout : NULL; + return matching ? matching->timeout : NULL; } -static void ctnl_timeout_put(struct nf_ct_timeout *t) +static void ctnl_timeout_put(struct nf_ct_timeout *timeout) { - struct ctnl_timeout *timeout = - container_of(t, struct ctnl_timeout, timeout); + if (refcount_dec_and_test(&timeout->refcnt)) + kfree_rcu(timeout, rcu); - if (refcount_dec_and_test(&timeout->refcnt)) { - kfree_rcu(timeout, rcu_head); - module_put(THIS_MODULE); - } + module_put(THIS_MODULE); } static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = { @@ -609,8 +605,11 @@ static void __net_exit cttimeout_net_exit(struct net *net) list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_freelist, free_head) { list_del(&cur->free_head); - if (refcount_dec_and_test(&cur->refcnt)) - kfree_rcu(cur, rcu_head); + if (refcount_dec_and_test(&cur->timeout->refcnt)) + kfree_rcu(cur->timeout, rcu); + + kfree_rcu(cur, rcu_head); + module_put(THIS_MODULE); } } @@ -649,24 +648,13 @@ static int __init cttimeout_init(void) return ret; } -static int untimeout(struct nf_conn *ct, void *timeout) -{ - struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct); - - if (timeout_ext) - RCU_INIT_POINTER(timeout_ext->timeout, NULL); - - return 0; -} - static void __exit cttimeout_exit(void) { nfnetlink_subsys_unregister(&cttimeout_subsys); unregister_pernet_subsys(&cttimeout_ops); RCU_INIT_POINTER(nf_ct_timeout_hook, NULL); - - nf_ct_iterate_destroy(untimeout, NULL); + synchronize_net(); } module_init(cttimeout_init); diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index acb753ec5697..92002079f8ea 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -95,11 +95,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb, switch (*optp) { case OSFOPT_MSS: - mss = optp[3]; - mss <<= 8; - mss |= optp[2]; - - mss = ntohs((__force __be16)mss); + mss = get_unaligned_be16(&optp[2]); break; case OSFOPT_TS: break; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 357513c6dcea..25934c6f01fb 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -897,8 +897,6 @@ static void nft_ct_timeout_obj_eval(struct nft_object *obj, } } - rcu_assign_pointer(timeout->timeout, priv->timeout); - /* adjust the timeout as per 'new' state. ct is unconfirmed, * so the current timestamp must not be added. */ @@ -949,6 +947,7 @@ static int nft_ct_timeout_obj_init(const struct nft_ctx *ctx, timeout->l3num = l3num; timeout->l4proto = l4proto; + refcount_set(&timeout->refcnt, 1); ret = nf_ct_netns_get(ctx->net, ctx->family); if (ret < 0) @@ -969,10 +968,10 @@ static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx, struct nft_ct_timeout_obj *priv = nft_obj_data(obj); struct nf_ct_timeout *timeout = priv->timeout; - nf_queue_nf_hook_drop(ctx->net); nf_ct_untimeout(ctx->net, timeout); nf_ct_netns_put(ctx->net, ctx->family); - kfree_rcu(priv->timeout, rcu); + if (refcount_dec_and_test(&timeout->refcnt)) + kfree_rcu(priv->timeout, rcu); } static int nft_ct_timeout_obj_dump(struct sk_buff *skb, @@ -1102,7 +1101,6 @@ static void nft_ct_helper_obj_destroy(const struct nft_ctx *ctx, { struct nft_ct_helper_obj *priv = nft_obj_data(obj); - nf_queue_nf_hook_drop(ctx->net); if (priv->helper4) nf_conntrack_helper_put(priv->helper4); if (priv->helper6) @@ -1134,7 +1132,7 @@ static void nft_ct_helper_obj_eval(struct nft_object *obj, to_assign = priv->helper6; break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); return; } @@ -1145,7 +1143,7 @@ static void nft_ct_helper_obj_eval(struct nft_object *obj, return; help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); - if (help) { + if (help && refcount_inc_not_zero(&to_assign->ct_refcnt)) { rcu_assign_pointer(help->helper, to_assign); set_bit(IPS_HELPER_BIT, &ct->status); diff --git a/net/netfilter/nft_ct_fast.c b/net/netfilter/nft_ct_fast.c index ecf7b3a404be..a44524c4fe63 100644 --- a/net/netfilter/nft_ct_fast.c +++ b/net/netfilter/nft_ct_fast.c @@ -53,7 +53,7 @@ void nft_ct_get_fast_eval(const struct nft_expr *expr, return; #endif default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); regs->verdict.code = NFT_BREAK; break; } diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index d3fc7969f123..8abf8e8145ce 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -298,7 +298,7 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, old.v32, new.v32, false); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index a1632e308f18..e048f05694cd 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -161,7 +161,7 @@ void nft_fib_store_result(void *reg, const struct nft_fib *priv, strscpy_pad(reg, dev ? dev->name : "", IFNAMSIZ); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); *dreg = 0; break; } diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index b9e88d7cf308..a48c2f765bba 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -95,7 +95,6 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { - u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion(); struct nft_fwd_neigh *priv = nft_expr_priv(expr); void *addr = ®s->data[priv->sreg_addr]; int oif = regs->data[priv->sreg_dev]; @@ -154,13 +153,15 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, goto out; } - if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) { + dev = dev_get_by_index_rcu(nft_net(pkt), oif); + if (!dev) { verdict = NF_DROP; goto out; } - dev = dev_get_by_index_rcu(nft_net(pkt), oif); - if (dev == NULL) { + local_bh_disable(); + if (nf_dev_xmit_recursion()) { + local_bh_enable(); verdict = NF_DROP; goto out; } @@ -169,16 +170,18 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { skb = skb_expand_head(skb, hh_len); if (!skb) { - verdict = NF_STOLEN; + local_bh_enable(); goto out; } } skb->dev = dev; skb_clear_tstamp(skb); - (*nf_dup_skb_recursion)++; + + nf_dev_xmit_recursion_inc(); neigh_xmit(neigh_table, dev, addr, skb); - (*nf_dup_skb_recursion)--; + nf_dev_xmit_recursion_dec(); + local_bh_enable(); out: regs->verdict.code = verdict; } diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index d14ca157910b..97fb4eea2d66 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -308,7 +308,7 @@ static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } nft_inner_save_tun_ctx(pkt, &tun_ctx); diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 9fafe5afc490..ba512e94b402 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -50,7 +50,7 @@ __nft_set_do_lookup(const struct net *net, const struct nft_set *set, if (set->ops == &nft_set_rbtree_type.ops) return nft_rbtree_lookup(net, set, key); - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); #endif return set->ops->lookup(net, set, key); } diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 2b01128737a3..841efd981e20 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -123,7 +123,7 @@ static void nft_masq_eval(const struct nft_expr *expr, break; #endif default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } } diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 5b25851381e5..9b5821c64442 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -116,12 +116,12 @@ nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt, nft_reg_store8(dest, PACKET_MULTICAST); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); return false; } break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); return false; } @@ -460,7 +460,7 @@ void nft_meta_get_eval(const struct nft_expr *expr, nft_meta_get_eval_sdifname(dest, pkt); break; default: - WARN_ON(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } return; @@ -506,7 +506,7 @@ void nft_meta_set_eval(const struct nft_expr *expr, break; #endif default: - WARN_ON(1); + DEBUG_NET_WARN_ON_ONCE(1); } } EXPORT_SYMBOL_GPL(nft_meta_set_eval); @@ -886,7 +886,7 @@ void nft_meta_inner_eval(const struct nft_expr *expr, nft_reg_store8(dest, tun_ctx->l4proto); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } return; diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 484a5490832e..ef2a80dfc68f 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -196,7 +196,7 @@ void nft_payload_eval(const struct nft_expr *expr, goto err; break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } offset += priv->offset; @@ -603,7 +603,7 @@ void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, offset = tun_ctx->inner_thoff; break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } offset += priv->offset; @@ -866,7 +866,7 @@ static void nft_payload_set_eval(const struct nft_expr *expr, goto err; break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 58ae802db8f5..a98aa28180fb 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -126,7 +126,7 @@ static void nft_redir_eval(const struct nft_expr *expr, break; #endif default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } } diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c index 196a92c7ea09..e3972e904cf0 100644 --- a/net/netfilter/nft_reject.c +++ b/net/netfilter/nft_reject.c @@ -102,8 +102,10 @@ static u8 icmp_code_v4[NFT_REJECT_ICMPX_MAX + 1] = { int nft_reject_icmp_code(u8 code) { - if (WARN_ON_ONCE(code > NFT_REJECT_ICMPX_MAX)) + if (unlikely(code > NFT_REJECT_ICMPX_MAX)) { + DEBUG_NET_WARN_ON_ONCE(1); return ICMP_NET_UNREACH; + } return icmp_code_v4[code]; } @@ -120,8 +122,10 @@ static u8 icmp_code_v6[NFT_REJECT_ICMPX_MAX + 1] = { int nft_reject_icmpv6_code(u8 code) { - if (WARN_ON_ONCE(code > NFT_REJECT_ICMPX_MAX)) + if (unlikely(code > NFT_REJECT_ICMPX_MAX)) { + DEBUG_NET_WARN_ON_ONCE(1); return ICMPV6_NOROUTE; + } return icmp_code_v6[code]; } diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index e23cd4759851..aeb0094eafd8 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -93,7 +93,7 @@ void nft_rt_get_eval(const struct nft_expr *expr, break; #endif default: - WARN_ON(1); + DEBUG_NET_WARN_ON_ONCE(1); goto err; } return; diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index b0e571c8e3f3..eb4e382119d4 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -385,7 +385,7 @@ static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set, break; default: iter->err = -EINVAL; - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } } diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 50d4a4f04309..706c78853f24 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -2199,7 +2199,7 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, break; default: iter->err = -EINVAL; - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } } diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c index dad265807b8b..b3f105520a85 100644 --- a/net/netfilter/nft_set_pipapo_avx2.c +++ b/net/netfilter/nft_set_pipapo_avx2.c @@ -144,6 +144,7 @@ static void nft_pipapo_avx2_fill(unsigned long *data, int start, int len) * This is an alternative implementation of pipapo_refill() suitable for usage * with AVX2 lookup routines: we know there are four words to be scanned, at * a given offset inside the map, for each matching iteration. + * The caller must ensure at least one bit in the four words is set. * * This function doesn't actually use any AVX2 instruction. * @@ -179,6 +180,7 @@ static int nft_pipapo_avx2_refill(int offset, unsigned long *map, NFT_PIPAPO_AVX2_REFILL_ONE_WORD(3); #undef NFT_PIPAPO_AVX2_REFILL_ONE_WORD + DEBUG_NET_WARN_ON_ONCE(ret < 0); return ret; } @@ -243,8 +245,7 @@ static int nft_pipapo_avx2_lookup_4b_2(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -320,8 +321,7 @@ static int nft_pipapo_avx2_lookup_4b_4(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -415,8 +415,7 @@ static int nft_pipapo_avx2_lookup_4b_8(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -506,8 +505,7 @@ static int nft_pipapo_avx2_lookup_4b_12(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -642,8 +640,7 @@ static int nft_pipapo_avx2_lookup_4b_32(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -700,8 +697,7 @@ static int nft_pipapo_avx2_lookup_8b_1(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -765,8 +761,7 @@ static int nft_pipapo_avx2_lookup_8b_2(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -840,8 +835,7 @@ static int nft_pipapo_avx2_lookup_8b_4(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -926,8 +920,7 @@ static int nft_pipapo_avx2_lookup_8b_6(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -1020,8 +1013,7 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill, b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last); if (last) ret = b; - - if (unlikely(ret == -1)) + else if (unlikely(ret == -1)) ret = b / XSAVE_YMM_SIZE; continue; @@ -1143,6 +1135,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, const struct nft_pipapo_field *f; unsigned long *res, *fill, *map; bool map_index; + int ret = 0; int i; scratch = *raw_cpu_ptr(m->scratch); @@ -1167,8 +1160,8 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, nft_pipapo_for_each_field(f, i, m) { bool last = i == m->field_count - 1, first = !i; - int ret = 0; + /* NB: previous round @ret is passed to avx2 lookup fn */ #define NFT_SET_PIPAPO_AVX2_LOOKUP(b, n) \ (ret = nft_pipapo_avx2_lookup_##b##b_##n(res, fill, f, \ ret, data, \ diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 560fbe6e3f75..018bbb6df4ce 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -396,9 +396,6 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, const struct nft_rbtree_elem *removed_end; removed_end = nft_rbtree_gc_elem(set, priv, rbe); - if (IS_ERR(removed_end)) - return PTR_ERR(removed_end); - if (removed_end == rbe_le || removed_end == rbe_ge) return -EAGAIN; @@ -657,8 +654,10 @@ static int nft_array_may_resize(const struct nft_set *set, bool flush) } realloc_array: - if (WARN_ON_ONCE(nelems > new_max_intervals)) + if (unlikely(nelems > new_max_intervals)) { + DEBUG_NET_WARN_ON_ONCE(1); return -ENOMEM; + } if (priv->array_next) { if (max_intervals == new_max_intervals) @@ -881,7 +880,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, break; default: iter->err = -EINVAL; - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } } diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index a146a45d7531..52d892e04261 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -71,8 +71,10 @@ static noinline int nft_socket_cgroup_subtree_level(void) if (level > 255) return -ERANGE; - if (WARN_ON_ONCE(level < 0)) + if (unlikely(level < 0)) { + DEBUG_NET_WARN_ON_ONCE(1); return -EINVAL; + } return level; } @@ -97,7 +99,7 @@ static struct sock *nft_socket_do_lookup(const struct nft_pktinfo *pkt) break; #endif default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; } @@ -152,7 +154,7 @@ static void nft_socket_eval(const struct nft_expr *expr, break; #endif default: - WARN_ON(1); + DEBUG_NET_WARN_ON_ONCE(1); regs->verdict.code = NFT_BREAK; } diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 68f7cfbbee06..0a018d4706a9 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -60,7 +60,7 @@ static void nft_tunnel_get_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; break; default: - WARN_ON(1); + DEBUG_NET_WARN_ON_ONCE(1); regs->verdict.code = NFT_BREAK; } } diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 65a75d88e5f0..8cec43064319 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -132,7 +132,7 @@ static void nft_xfrm_state_get_key(const struct nft_xfrm *priv, switch (priv->key) { case NFT_XFRM_KEY_UNSPEC: case __NFT_XFRM_KEY_MAX: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); break; case NFT_XFRM_KEY_DADDR_IP4: *dest = (__force __u32)state->id.daddr.a4; @@ -206,7 +206,7 @@ static void nft_xfrm_get_eval(const struct nft_expr *expr, nft_xfrm_get_eval_out(priv, regs, pkt); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); regs->verdict.code = NFT_BREAK; break; } @@ -252,7 +252,7 @@ static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *e (1 << NF_INET_POST_ROUTING); break; default: - WARN_ON_ONCE(1); + DEBUG_NET_WARN_ON_ONCE(1); return -EINVAL; } diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index d2aeacf94230..e78660dfdf4b 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -284,9 +284,6 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, struct nf_conn_help *help; if (ct) { - if (info->helper[0] || info->timeout[0]) - nf_queue_nf_hook_drop(par->net); - help = nfct_help(ct); xt_ct_put_helper(help); diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 870e7699326a..47bae5e48db6 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -295,7 +295,7 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface, if (ret_val != 0) kfree(entry); - return 0; + return ret_val; } #endif /* IPv6 */ diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 7269e23b578d..5202fe0b0867 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1721,18 +1722,18 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, } static int netlink_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); unsigned int flag; int len, val; + u32 group; if (level != SOL_NETLINK) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; if (len < 0) return -EINVAL; @@ -1756,14 +1757,14 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, idx = pos / sizeof(unsigned long); shift = (pos % sizeof(unsigned long)) * 8; - if (put_user((u32)(nlk->groups[idx] >> shift), - (u32 __user *)(optval + pos))) { + group = (u32)(nlk->groups[idx] >> shift); + if (copy_to_iter(&group, sizeof(u32), + &opt->iter_out) != sizeof(u32)) { err = -EFAULT; break; } } - if (put_user(ALIGN(BITS_TO_BYTES(nlk->ngroups), sizeof(u32)), optlen)) - err = -EFAULT; + opt->optlen = ALIGN(BITS_TO_BYTES(nlk->ngroups), sizeof(u32)); netlink_unlock_table(); return err; } @@ -1789,8 +1790,8 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, len = sizeof(int); val = test_bit(flag, &nlk->flags); - if (put_user(len, optlen) || - copy_to_user(optval, &val, len)) + opt->optlen = len; + if (copy_to_iter(&val, len, &opt->iter_out) != len) return -EFAULT; return 0; @@ -2818,7 +2819,7 @@ static const struct proto_ops netlink_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = netlink_setsockopt, - .getsockopt = netlink_getsockopt, + .getsockopt_iter = netlink_getsockopt, .sendmsg = netlink_sendmsg, .recvmsg = netlink_recvmsg, .mmap = sock_no_mmap, diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c index 5a3aed7f84f5..aa20e8603f32 100644 --- a/net/nfc/nci/uart.c +++ b/net/nfc/nci/uart.c @@ -421,7 +421,7 @@ void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl) } EXPORT_SYMBOL_GPL(nci_uart_set_config); -static struct tty_ldisc_ops nci_uart_ldisc = { +static const struct tty_ldisc_ops nci_uart_ldisc = { .owner = THIS_MODULE, .num = N_NCI, .name = "n_nci", diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 7c9256572284..c6fd9c424e8f 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -1797,10 +1797,10 @@ static int ovs_ct_limit_get_zone_limit(struct net *net, } else { rcu_read_lock(); limit = ct_limit_get(info, zone); - rcu_read_unlock(); err = __ovs_ct_limit_get_zone_limit( net, info->data, zone, limit, reply); + rcu_read_unlock(); if (err) return err; } diff --git a/net/psp/psp-nl-gen.c b/net/psp/psp-nl-gen.c index 953309952cef..0e426ffac398 100644 --- a/net/psp/psp-nl-gen.c +++ b/net/psp/psp-nl-gen.c @@ -53,6 +53,20 @@ static const struct nla_policy psp_get_stats_nl_policy[PSP_A_STATS_DEV_ID + 1] = [PSP_A_STATS_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1), }; +/* PSP_CMD_DEV_ASSOC - do */ +static const struct nla_policy psp_dev_assoc_nl_policy[PSP_A_DEV_NSID + 1] = { + [PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1), + [PSP_A_DEV_IFINDEX] = { .type = NLA_U32, }, + [PSP_A_DEV_NSID] = { .type = NLA_S32, }, +}; + +/* PSP_CMD_DEV_DISASSOC - do */ +static const struct nla_policy psp_dev_disassoc_nl_policy[PSP_A_DEV_NSID + 1] = { + [PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1), + [PSP_A_DEV_IFINDEX] = { .type = NLA_U32, }, + [PSP_A_DEV_NSID] = { .type = NLA_S32, }, +}; + /* Ops table for psp */ static const struct genl_split_ops psp_nl_ops[] = { { @@ -71,7 +85,7 @@ static const struct genl_split_ops psp_nl_ops[] = { }, { .cmd = PSP_CMD_DEV_SET, - .pre_doit = psp_device_get_locked, + .pre_doit = psp_device_get_locked_admin, .doit = psp_nl_dev_set_doit, .post_doit = psp_device_unlock, .policy = psp_dev_set_nl_policy, @@ -80,7 +94,7 @@ static const struct genl_split_ops psp_nl_ops[] = { }, { .cmd = PSP_CMD_KEY_ROTATE, - .pre_doit = psp_device_get_locked, + .pre_doit = psp_device_get_locked_admin, .doit = psp_nl_key_rotate_doit, .post_doit = psp_device_unlock, .policy = psp_key_rotate_nl_policy, @@ -119,6 +133,24 @@ static const struct genl_split_ops psp_nl_ops[] = { .dumpit = psp_nl_get_stats_dumpit, .flags = GENL_CMD_CAP_DUMP, }, + { + .cmd = PSP_CMD_DEV_ASSOC, + .pre_doit = psp_device_get_locked_dev_assoc, + .doit = psp_nl_dev_assoc_doit, + .post_doit = psp_device_unlock, + .policy = psp_dev_assoc_nl_policy, + .maxattr = PSP_A_DEV_NSID, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = PSP_CMD_DEV_DISASSOC, + .pre_doit = psp_device_get_locked, + .doit = psp_nl_dev_disassoc_doit, + .post_doit = psp_device_unlock, + .policy = psp_dev_disassoc_nl_policy, + .maxattr = PSP_A_DEV_NSID, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, }; static const struct genl_multicast_group psp_nl_mcgrps[] = { diff --git a/net/psp/psp-nl-gen.h b/net/psp/psp-nl-gen.h index 599c5f1c82f2..24d51bff997f 100644 --- a/net/psp/psp-nl-gen.h +++ b/net/psp/psp-nl-gen.h @@ -17,8 +17,13 @@ extern const struct nla_policy psp_keys_nl_policy[PSP_A_KEYS_SPI + 1]; int psp_device_get_locked(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); +int psp_device_get_locked_admin(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info); int psp_assoc_device_get_locked(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); +int psp_device_get_locked_dev_assoc(const struct genl_split_ops *ops, + struct sk_buff *skb, + struct genl_info *info); void psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info); @@ -31,6 +36,8 @@ int psp_nl_rx_assoc_doit(struct sk_buff *skb, struct genl_info *info); int psp_nl_tx_assoc_doit(struct sk_buff *skb, struct genl_info *info); int psp_nl_get_stats_doit(struct sk_buff *skb, struct genl_info *info); int psp_nl_get_stats_dumpit(struct sk_buff *skb, struct netlink_callback *cb); +int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info); +int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info); enum { PSP_NLGRP_MGMT, diff --git a/net/psp/psp.h b/net/psp/psp.h index 9f19137593a0..86eeba823ced 100644 --- a/net/psp/psp.h +++ b/net/psp/psp.h @@ -14,7 +14,9 @@ extern struct xarray psp_devs; extern struct mutex psp_devs_lock; void psp_dev_free(struct psp_dev *psd); -int psp_dev_check_access(struct psp_dev *psd, struct net *net); +int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin); +bool psp_has_assoc_dev_in_ns(struct psp_dev *psd, struct net *net); +int psp_attach_netdev_notifier(void); void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd); diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c index e45549f08eef..8b2f178e317c 100644 --- a/net/psp/psp_main.c +++ b/net/psp/psp_main.c @@ -27,13 +27,22 @@ struct mutex psp_devs_lock; * psp_dev_check_access() - check if user in a given net ns can access PSP dev * @psd: PSP device structure user is trying to access * @net: net namespace user is in + * @admin: If true, only allow access from @psd's main device's netns, + * for admin operations like config changes and key rotation. + * If false, also allow access from network namespaces that have + * an associated device with @psd, for read-only and association + * management operations. * * Return: 0 if PSP device should be visible in @net, errno otherwise. */ -int psp_dev_check_access(struct psp_dev *psd, struct net *net) +int psp_dev_check_access(struct psp_dev *psd, struct net *net, bool admin) { if (dev_net(psd->main_netdev) == net) return 0; + + if (!admin && psp_has_assoc_dev_in_ns(psd, net)) + return 0; + return -ENOENT; } @@ -69,6 +78,7 @@ psp_dev_create(struct net_device *netdev, return ERR_PTR(-ENOMEM); psd->main_netdev = netdev; + INIT_LIST_HEAD(&psd->assoc_dev_list); psd->ops = psd_ops; psd->caps = psd_caps; psd->drv_priv = priv_ptr; @@ -90,6 +100,10 @@ psp_dev_create(struct net_device *netdev, mutex_lock(&psd->lock); mutex_unlock(&psp_devs_lock); + /* notify before netdev assignment + * There's no strong reason for it, but thinking is to avoid creating + * implicit expectations about the PSP dev <> netdev relationship. + */ psp_nl_notify_dev(psd, PSP_CMD_DEV_ADD_NTF); rcu_assign_pointer(netdev->psp_dev, psd); @@ -116,6 +130,7 @@ void psp_dev_free(struct psp_dev *psd) */ void psp_dev_unregister(struct psp_dev *psd) { + struct psp_assoc_dev *entry, *entry_tmp; struct psp_assoc *pas, *next; mutex_lock(&psp_devs_lock); @@ -135,6 +150,15 @@ void psp_dev_unregister(struct psp_dev *psd) list_for_each_entry_safe(pas, next, &psd->stale_assocs, assocs_list) psp_dev_tx_key_del(psd, pas); + list_for_each_entry_safe(entry, entry_tmp, &psd->assoc_dev_list, + dev_list) { + list_del(&entry->dev_list); + rcu_assign_pointer(entry->assoc_dev->psp_dev, NULL); + netdev_put(entry->assoc_dev, &entry->dev_tracker); + kfree(entry); + } + psd->assoc_dev_cnt = 0; + rcu_assign_pointer(psd->main_netdev->psp_dev, NULL); psd->ops = NULL; @@ -228,6 +252,10 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi, u32 ethr_len = skb_mac_header_len(skb); u32 bufflen = ethr_len + network_len; + if (skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6)) + return false; + if (skb_cow_head(skb, PSP_ENCAP_HLEN)) return false; @@ -243,11 +271,9 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi, ip_hdr(skb)->check = 0; ip_hdr(skb)->check = ip_fast_csum((u8 *)ip_hdr(skb), ip_hdr(skb)->ihl); - } else if (skb->protocol == htons(ETH_P_IPV6)) { + } else { ipv6_hdr(skb)->nexthdr = IPPROTO_UDP; be16_add_cpu(&ipv6_hdr(skb)->payload_len, PSP_ENCAP_HLEN); - } else { - return false; } skb_set_inner_ipproto(skb, IPPROTO_TCP); @@ -295,6 +321,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) if (proto == htons(ETH_P_IP)) { struct iphdr *iph = (struct iphdr *)(skb->data + l2_hlen); + if (unlikely(iph->ihl < 5)) + return -EINVAL; + is_udp = iph->protocol == IPPROTO_UDP; l3_hlen = iph->ihl * 4; if (l3_hlen != sizeof(struct iphdr) && @@ -348,6 +377,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) if (proto == htons(ETH_P_IP)) { struct iphdr *iph = (struct iphdr *)(skb->data + l2_hlen); + if (unlikely(ntohs(iph->tot_len) < l3_hlen + encap)) + return -EINVAL; + iph->protocol = psph->nexthdr; iph->tot_len = htons(ntohs(iph->tot_len) - encap); iph->check = 0; @@ -355,6 +387,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) } else { struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + l2_hlen); + if (unlikely(ntohs(ipv6h->payload_len) < encap)) + return -EINVAL; + ipv6h->nexthdr = psph->nexthdr; ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) - encap); } @@ -370,6 +405,81 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv) } EXPORT_SYMBOL(psp_dev_rcv); +static void psp_dev_disassoc_one(struct psp_dev *psd, struct net_device *dev) +{ + struct psp_assoc_dev *entry; + + list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) { + if (entry->assoc_dev == dev) { + list_del(&entry->dev_list); + psd->assoc_dev_cnt--; + rcu_assign_pointer(entry->assoc_dev->psp_dev, NULL); + netdev_put(entry->assoc_dev, &entry->dev_tracker); + kfree(entry); + return; + } + } +} + +static int psp_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct psp_dev *psd; + + if (event != NETDEV_UNREGISTER) + return NOTIFY_DONE; + + rcu_read_lock(); + psd = rcu_dereference(dev->psp_dev); + if (psd && psp_dev_tryget(psd)) { + rcu_read_unlock(); + mutex_lock(&psd->lock); + if (psp_dev_is_registered(psd)) + psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF); + psp_dev_disassoc_one(psd, dev); + mutex_unlock(&psd->lock); + psp_dev_put(psd); + } else { + rcu_read_unlock(); + } + + return NOTIFY_DONE; +} + +static struct notifier_block psp_netdev_notifier = { + .notifier_call = psp_netdev_event, +}; + +static DEFINE_MUTEX(psp_notifier_lock); +static bool psp_notifier_registered; + +/* Register the netdevice notifier when the first device association + * is created. In many installations no associations will be created and + * the notifier won't be needed. + * + * Must be called without psd->lock held, due to lock ordering: + * rtnl_lock -> psd->lock (the notifier callback runs under rtnl_lock + * and takes psd->lock). + */ +int psp_attach_netdev_notifier(void) +{ + int err = 0; + + if (READ_ONCE(psp_notifier_registered)) + return 0; + + mutex_lock(&psp_notifier_lock); + if (!psp_notifier_registered) { + err = register_netdevice_notifier(&psp_netdev_notifier); + if (!err) + WRITE_ONCE(psp_notifier_registered, true); + } + mutex_unlock(&psp_notifier_lock); + + return err; +} + static int __init psp_init(void) { mutex_init(&psp_devs_lock); diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c index 0cc744a6e1c9..9610d8c456ff 100644 --- a/net/psp/psp_nl.c +++ b/net/psp/psp_nl.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include #include @@ -38,10 +39,79 @@ static int psp_nl_reply_send(struct sk_buff *rsp, struct genl_info *info) return genlmsg_reply(rsp, info); } +/** + * psp_nl_multicast_per_ns() - multicast a notification to each unique netns + * @psd: PSP device (must be locked) + * @group: multicast group + * @build_ntf: callback to build an skb for a given netns, or NULL on failure + * @ctx: opaque context passed to @build_ntf + * + * Iterates all unique network namespaces from the associated device list + * plus the main device's netns. For each unique netns, calls @build_ntf + * to construct a notification skb and multicasts it. + */ +static void +psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group, + struct sk_buff *(*build_ntf)(struct psp_dev *, + struct net *, + void *), + void *ctx) +{ + struct psp_assoc_dev *entry; + struct xarray sent_nets; + struct net *main_net; + struct sk_buff *ntf; + + main_net = dev_net(psd->main_netdev); + xa_init(&sent_nets); + + list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) { + struct net *assoc_net = dev_net(entry->assoc_dev); + int ret; + + if (net_eq(assoc_net, main_net)) + continue; + + ret = xa_insert(&sent_nets, (unsigned long)assoc_net, assoc_net, + GFP_KERNEL); + if (ret == -EBUSY) + continue; + + ntf = build_ntf(psd, assoc_net, ctx); + if (!ntf) + continue; + + genlmsg_multicast_netns(&psp_nl_family, assoc_net, ntf, 0, + group, GFP_KERNEL); + } + xa_destroy(&sent_nets); + + /* Send to main device netns */ + ntf = build_ntf(psd, main_net, ctx); + if (!ntf) + return; + genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group, + GFP_KERNEL); +} + +static struct sk_buff *psp_nl_clone_ntf(struct psp_dev *psd, struct net *net, + void *ctx) +{ + return skb_clone(ctx, GFP_KERNEL); +} + +static void psp_nl_multicast_all_ns(struct psp_dev *psd, struct sk_buff *ntf, + unsigned int group) +{ + psp_nl_multicast_per_ns(psd, group, psp_nl_clone_ntf, ntf); + nlmsg_consume(ntf); +} + /* Device stuff */ static struct psp_dev * -psp_device_get_and_lock(struct net *net, struct nlattr *dev_id) +psp_device_get_and_lock(struct net *net, struct nlattr *dev_id, + bool admin) { struct psp_dev *psd; int err; @@ -56,7 +126,7 @@ psp_device_get_and_lock(struct net *net, struct nlattr *dev_id) mutex_lock(&psd->lock); mutex_unlock(&psp_devs_lock); - err = psp_dev_check_access(psd, net); + err = psp_dev_check_access(psd, net, admin); if (err) { mutex_unlock(&psd->lock); return ERR_PTR(err); @@ -65,17 +135,79 @@ psp_device_get_and_lock(struct net *net, struct nlattr *dev_id) return psd; } -int psp_device_get_locked(const struct genl_split_ops *ops, - struct sk_buff *skb, struct genl_info *info) +static int __psp_device_get_locked(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info, + bool admin) { if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_ID)) return -EINVAL; info->user_ptr[0] = psp_device_get_and_lock(genl_info_net(info), - info->attrs[PSP_A_DEV_ID]); + info->attrs[PSP_A_DEV_ID], + admin); return PTR_ERR_OR_ZERO(info->user_ptr[0]); } +int psp_device_get_locked_admin(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) +{ + return __psp_device_get_locked(ops, skb, info, true); +} + +int psp_device_get_locked(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) +{ + return __psp_device_get_locked(ops, skb, info, false); +} + +/* + * Non-admin version of psp_device_get_locked() + psp_attach_netdev_notifier() + * only used for dev-assoc. + */ +int psp_device_get_locked_dev_assoc(const struct genl_split_ops *ops, + struct sk_buff *skb, struct genl_info *info) +{ + int err; + + err = psp_attach_netdev_notifier(); + if (err) + return err; + + return __psp_device_get_locked(ops, skb, info, false); +} + +static struct net *psp_nl_resolve_assoc_dev_ns(struct psp_dev *psd, + struct genl_info *info) +{ + struct net *net; + int nsid; + + if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_IFINDEX)) + return ERR_PTR(-EINVAL); + + if (info->attrs[PSP_A_DEV_NSID]) { + /* Only callers in the main netns may specify nsid */ + if (dev_net(psd->main_netdev) != genl_info_net(info)) { + NL_SET_BAD_ATTR(info->extack, + info->attrs[PSP_A_DEV_NSID]); + return ERR_PTR(-EPERM); + } + + nsid = nla_get_s32(info->attrs[PSP_A_DEV_NSID]); + + net = get_net_ns_by_id(genl_info_net(info), nsid); + if (!net) { + NL_SET_BAD_ATTR(info->extack, + info->attrs[PSP_A_DEV_NSID]); + return ERR_PTR(-EINVAL); + } + } else { + net = get_net(genl_info_net(info)); + } + + return net; +} + void psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb, struct genl_info *info) @@ -88,11 +220,67 @@ psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb, sockfd_put(socket); } +bool psp_has_assoc_dev_in_ns(struct psp_dev *psd, struct net *net) +{ + struct psp_assoc_dev *entry; + + list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) { + if (dev_net(entry->assoc_dev) == net) + return true; + } + + return false; +} + +static int psp_nl_fill_assoc_dev_list(struct psp_dev *psd, struct sk_buff *rsp, + struct net *cur_net, + struct net *filter_net) +{ + struct psp_assoc_dev *entry; + struct net *dev_net_ns; + struct nlattr *nest; + int nsid; + + list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) { + dev_net_ns = dev_net(entry->assoc_dev); + + if (filter_net && dev_net_ns != filter_net) + continue; + + /* When filtering by namespace, all devices are in the caller's + * namespace so nsid is always NETNSA_NSID_NOT_ASSIGNED (-1). + * Otherwise, calculate the nsid relative to cur_net. + */ + nsid = filter_net ? NETNSA_NSID_NOT_ASSIGNED : + peernet2id_alloc(cur_net, dev_net_ns, + GFP_KERNEL); + + nest = nla_nest_start(rsp, PSP_A_DEV_ASSOC_LIST); + if (!nest) + return -EMSGSIZE; + + if (nla_put_u32(rsp, PSP_A_ASSOC_DEV_INFO_IFINDEX, + entry->assoc_dev->ifindex) || + nla_put_s32(rsp, PSP_A_ASSOC_DEV_INFO_NSID, nsid)) { + nla_nest_cancel(rsp, nest); + return -EMSGSIZE; + } + + nla_nest_end(rsp, nest); + } + + return 0; +} + static int psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp, const struct genl_info *info) { + struct net *cur_net; void *hdr; + int err; + + cur_net = genl_info_net(info); hdr = genlmsg_iput(rsp, info); if (!hdr) @@ -104,6 +292,22 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp, nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions)) goto err_cancel_msg; + if (cur_net == dev_net(psd->main_netdev)) { + /* Primary device - dump assoc list */ + err = psp_nl_fill_assoc_dev_list(psd, rsp, cur_net, NULL); + if (err) + goto err_cancel_msg; + } else { + /* In netns: set by-association flag and dump filtered + * assoc list containing only devices in cur_net + */ + if (nla_put_flag(rsp, PSP_A_DEV_BY_ASSOCIATION)) + goto err_cancel_msg; + err = psp_nl_fill_assoc_dev_list(psd, rsp, cur_net, cur_net); + if (err) + goto err_cancel_msg; + } + genlmsg_end(rsp, hdr); return 0; @@ -112,27 +316,34 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp, return -EMSGSIZE; } -void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd) +static struct sk_buff *psp_nl_build_dev_ntf(struct psp_dev *psd, + struct net *net, void *ctx) { + u32 cmd = *(u32 *)ctx; struct genl_info info; struct sk_buff *ntf; - if (!genl_has_listeners(&psp_nl_family, dev_net(psd->main_netdev), - PSP_NLGRP_MGMT)) - return; + if (!genl_has_listeners(&psp_nl_family, net, PSP_NLGRP_MGMT)) + return NULL; ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!ntf) - return; + return NULL; genl_info_init_ntf(&info, &psp_nl_family, cmd); + genl_info_net_set(&info, net); if (psp_nl_dev_fill(psd, ntf, &info)) { nlmsg_free(ntf); - return; + return NULL; } - genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf, - 0, PSP_NLGRP_MGMT, GFP_KERNEL); + return ntf; +} + +void psp_nl_notify_dev(struct psp_dev *psd, u32 cmd) +{ + psp_nl_multicast_per_ns(psd, PSP_NLGRP_MGMT, + psp_nl_build_dev_ntf, &cmd); } int psp_nl_dev_get_doit(struct sk_buff *req, struct genl_info *info) @@ -160,7 +371,7 @@ static int psp_nl_dev_get_dumpit_one(struct sk_buff *rsp, struct netlink_callback *cb, struct psp_dev *psd) { - if (psp_dev_check_access(psd, sock_net(rsp->sk))) + if (psp_dev_check_access(psd, sock_net(rsp->sk), false)) return 0; return psp_nl_dev_fill(psd, rsp, genl_info_dump(cb)); @@ -266,8 +477,9 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info) psd->stats.rotations++; nlmsg_end(ntf, (struct nlmsghdr *)ntf->data); - genlmsg_multicast_netns(&psp_nl_family, dev_net(psd->main_netdev), ntf, - 0, PSP_NLGRP_USE, GFP_KERNEL); + + psp_nl_multicast_all_ns(psd, ntf, PSP_NLGRP_USE); + return psp_nl_reply_send(rsp, info); err_free_ntf: @@ -277,6 +489,143 @@ int psp_nl_key_rotate_doit(struct sk_buff *skb, struct genl_info *info) return err; } +int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct psp_dev *psd = info->user_ptr[0]; + struct psp_assoc_dev *psp_assoc_dev; + struct net_device *assoc_dev; + struct sk_buff *rsp; + u32 assoc_ifindex; + struct net *net; + int err; + + if (psd->assoc_dev_cnt >= PSP_ASSOC_DEV_MAX) { + NL_SET_ERR_MSG(info->extack, + "Maximum number of associated devices reached"); + return -ENOSPC; + } + + net = psp_nl_resolve_assoc_dev_ns(psd, info); + if (IS_ERR(net)) + return PTR_ERR(net); + + psp_assoc_dev = kzalloc_obj(*psp_assoc_dev); + if (!psp_assoc_dev) { + err = -ENOMEM; + goto err_put_net; + } + + assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]); + assoc_dev = netdev_get_by_index(net, assoc_ifindex, + &psp_assoc_dev->dev_tracker, + GFP_KERNEL); + if (!assoc_dev) { + NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]); + err = -ENODEV; + goto err_free_assoc; + } + + /* Check if device is already associated with a PSP device */ + if (cmpxchg(&assoc_dev->psp_dev, NULL, RCU_INITIALIZER(psd))) { + NL_SET_ERR_MSG(info->extack, + "Device already associated with a PSP device"); + err = -EBUSY; + goto err_put_dev; + } + + psp_assoc_dev->assoc_dev = assoc_dev; + + /* Check for race with NETDEV_UNREGISTER. The cmpxchg above is a + * full barrier, and the unregister path has synchronize_net() + * between setting NETREG_UNREGISTERING and reading psp_dev in the + * notifier. So at least one side would do the clean-up if we are in + * the middle of unregitering assoc_dev. + * And the clean-up is serialized by psd->lock. + */ + if (READ_ONCE(assoc_dev->reg_state) != NETREG_REGISTERED) { + err = -ENODEV; + goto err_clean_ptr; + } + + rsp = psp_nl_reply_new(info); + if (!rsp) { + err = -ENOMEM; + goto err_clean_ptr; + } + + list_add_tail(&psp_assoc_dev->dev_list, &psd->assoc_dev_list); + psd->assoc_dev_cnt++; + + put_net(net); + + psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF); + + return psp_nl_reply_send(rsp, info); + +err_clean_ptr: + rcu_assign_pointer(assoc_dev->psp_dev, NULL); +err_put_dev: + netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker); +err_free_assoc: + kfree(psp_assoc_dev); +err_put_net: + put_net(net); + + return err; +} + +int psp_nl_dev_disassoc_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct psp_assoc_dev *entry, *found = NULL; + struct psp_dev *psd = info->user_ptr[0]; + struct sk_buff *rsp; + u32 assoc_ifindex; + struct net *net; + + net = psp_nl_resolve_assoc_dev_ns(psd, info); + if (IS_ERR(net)) + return PTR_ERR(net); + + assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]); + + /* Search the association list by ifindex and netns */ + list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) { + if (entry->assoc_dev->ifindex == assoc_ifindex && + dev_net(entry->assoc_dev) == net) { + found = entry; + break; + } + } + + if (!found) { + put_net(net); + NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]); + return -ENODEV; + } + + rsp = psp_nl_reply_new(info); + if (!rsp) { + put_net(net); + return -ENOMEM; + } + + put_net(net); + + /* Notify before removal so listeners in the disassociated namespace + * still receive the notification. + */ + psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF); + + /* Remove from the association list */ + list_del(&found->dev_list); + psd->assoc_dev_cnt--; + rcu_assign_pointer(found->assoc_dev->psp_dev, NULL); + netdev_put(found->assoc_dev, &found->dev_tracker); + kfree(found); + + return psp_nl_reply_send(rsp, info); +} + /* Key etc. */ int psp_assoc_device_get_locked(const struct genl_split_ops *ops, @@ -310,7 +659,7 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops, */ mutex_lock(&psd->lock); if (!psp_dev_is_registered(psd) || - psp_dev_check_access(psd, genl_info_net(info))) { + psp_dev_check_access(psd, genl_info_net(info), false)) { mutex_unlock(&psd->lock); psp_dev_put(psd); psd = NULL; @@ -334,7 +683,7 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops, psp_dev_put(psd); } else { - psd = psp_device_get_and_lock(genl_info_net(info), id); + psd = psp_device_get_and_lock(genl_info_net(info), id, false); if (IS_ERR(psd)) { err = PTR_ERR(psd); goto err_sock_put; @@ -577,7 +926,7 @@ static int psp_nl_stats_get_dumpit_one(struct sk_buff *rsp, struct netlink_callback *cb, struct psp_dev *psd) { - if (psp_dev_check_access(psd, sock_net(rsp->sk))) + if (psp_dev_check_access(psd, sock_net(rsp->sk), false)) return 0; return psp_nl_stats_fill(psd, rsp, genl_info_dump(cb)); diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index db823177e636..d02ef9a74c3c 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -496,7 +496,7 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len) if (cb->dst_port == QRTR_PORT_CTRL_LEGACY) cb->dst_port = QRTR_PORT_CTRL; - if (!size || len != ALIGN(size, 4) + hdrlen) + if (!size || size > len || len != ALIGN(size, 4) + hdrlen) goto err; if ((cb->type == QRTR_TYPE_NEW_SERVER || @@ -1009,8 +1009,10 @@ static int qrtr_send_resume_tx(struct qrtr_cb *cb) return -EINVAL; skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL); - if (!skb) + if (!skb) { + qrtr_node_release(node); return -ENOMEM; + } pkt->cmd = cpu_to_le32(QRTR_TYPE_RESUME_TX); pkt->client.node = cpu_to_le32(cb->dst_node); diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index 76f625986a7f..d5defe9172e3 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -37,13 +37,13 @@ #include #include #include +#include #include #include "rds.h" /* this is just used for stats gathering :/ */ static DEFINE_SPINLOCK(rds_sock_lock); -static unsigned long rds_sock_count; static LIST_HEAD(rds_sock_list); DECLARE_WAIT_QUEUE_HEAD(rds_poll_waitq); @@ -82,7 +82,6 @@ static int rds_release(struct socket *sock) spin_lock_bh(&rds_sock_lock); list_del_init(&rs->rs_item); - rds_sock_count--; spin_unlock_bh(&rds_sock_lock); rds_trans_put(rs->rs_transport); @@ -219,7 +218,7 @@ static __poll_t rds_poll(struct file *file, struct socket *sock, poll_wait(file, sk_sleep(sk), wait); - if (rs->rs_seen_congestion) + if (READ_ONCE(rs->rs_seen_congestion)) poll_wait(file, &rds_poll_waitq, wait); read_lock_irqsave(&rs->rs_recv_lock, flags); @@ -247,7 +246,7 @@ static __poll_t rds_poll(struct file *file, struct socket *sock, /* clear state any time we wake a seen-congested socket */ if (mask) - rs->rs_seen_congestion = 0; + WRITE_ONCE(rs->rs_seen_congestion, 0); return mask; } @@ -487,35 +486,36 @@ static int rds_setsockopt(struct socket *sock, int level, int optname, } static int rds_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct rds_sock *rs = rds_sk_to_rs(sock->sk); int ret = -ENOPROTOOPT, len; int trans; + int val; if (level != SOL_RDS) goto out; - if (get_user(len, optlen)) { - ret = -EFAULT; - goto out; - } + len = opt->optlen; switch (optname) { case RDS_INFO_FIRST ... RDS_INFO_LAST: - ret = rds_info_getsockopt(sock, optname, optval, - optlen); + ret = rds_info_getsockopt(sock, optname, opt); break; case RDS_RECVERR: - if (len < sizeof(int)) + if (len < sizeof(int)) { ret = -EINVAL; - else - if (put_user(rs->rs_recverr, (int __user *) optval) || - put_user(sizeof(int), optlen)) + break; + } + val = rs->rs_recverr; + if (copy_to_iter(&val, sizeof(int), &opt->iter_out) != + sizeof(int)) { ret = -EFAULT; - else + } else { + opt->optlen = sizeof(int); ret = 0; + } break; case SO_RDS_TRANSPORT: if (len < sizeof(int)) { @@ -524,11 +524,13 @@ static int rds_getsockopt(struct socket *sock, int level, int optname, } trans = (rs->rs_transport ? rs->rs_transport->t_type : RDS_TRANS_NONE); /* unbound */ - if (put_user(trans, (int __user *)optval) || - put_user(sizeof(int), optlen)) + if (copy_to_iter(&trans, sizeof(int), &opt->iter_out) != + sizeof(int)) { ret = -EFAULT; - else + } else { + opt->optlen = sizeof(int); ret = 0; + } break; default: break; @@ -655,7 +657,7 @@ static const struct proto_ops rds_proto_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = rds_setsockopt, - .getsockopt = rds_getsockopt, + .getsockopt_iter = rds_getsockopt, .sendmsg = rds_sendmsg, .recvmsg = rds_recvmsg, .mmap = sock_no_mmap, @@ -694,7 +696,6 @@ static int __rds_create(struct socket *sock, struct sock *sk, int protocol) spin_lock_bh(&rds_sock_lock); list_add_tail(&rs->rs_item, &rds_sock_list); - rds_sock_count++; spin_unlock_bh(&rds_sock_lock); return 0; @@ -735,6 +736,7 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { + struct net *net = sock_net(sock->sk); struct rds_sock *rs; struct rds_incoming *inc; unsigned int total = 0; @@ -744,6 +746,9 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len, spin_lock_bh(&rds_sock_lock); list_for_each_entry(rs, &rds_sock_list, rs_item) { + /* Only show sockets in the caller's netns. */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), net)) + continue; /* This option only supports IPv4 sockets. */ if (!ipv6_addr_v4mapped(&rs->rs_bound_addr)) continue; @@ -774,6 +779,7 @@ static void rds6_sock_inc_info(struct socket *sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { + struct net *net = sock_net(sock->sk); struct rds_incoming *inc; unsigned int total = 0; struct rds_sock *rs; @@ -783,6 +789,9 @@ static void rds6_sock_inc_info(struct socket *sock, unsigned int len, spin_lock_bh(&rds_sock_lock); list_for_each_entry(rs, &rds_sock_list, rs_item) { + /* Only show sockets in the caller's netns. */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), net)) + continue; read_lock(&rs->rs_recv_lock); list_for_each_entry(inc, &rs->rs_recv_queue, i_item) { @@ -806,7 +815,9 @@ static void rds_sock_info(struct socket *sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { + struct net *net = sock_net(sock->sk); struct rds_info_socket sinfo; + unsigned int copied = 0; unsigned int cnt = 0; struct rds_sock *rs; @@ -814,12 +825,24 @@ static void rds_sock_info(struct socket *sock, unsigned int len, spin_lock_bh(&rds_sock_lock); - if (len < rds_sock_count) { - cnt = rds_sock_count; - goto out; + /* First pass: count entries visible in the caller's netns. */ + list_for_each_entry(rs, &rds_sock_list, rs_item) { + if (!net_eq(sock_net(rds_rs_to_sk(rs)), net)) + continue; + if (!ipv6_addr_v4mapped(&rs->rs_bound_addr)) + continue; + cnt++; } + if (len < cnt) + goto out; + list_for_each_entry(rs, &rds_sock_list, rs_item) { + if (copied >= cnt) + break; + /* Only show sockets in the caller's netns. */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), net)) + continue; /* This option only supports IPv4 sockets. */ if (!ipv6_addr_v4mapped(&rs->rs_bound_addr)) continue; @@ -832,8 +855,13 @@ static void rds_sock_info(struct socket *sock, unsigned int len, sinfo.inum = sock_i_ino(rds_rs_to_sk(rs)); rds_info_copy(iter, &sinfo, sizeof(sinfo)); - cnt++; + copied++; } + /* A concurrent rds_bind() can change rs_bound_addr between the + * two passes without holding rds_sock_lock, so copied may be + * less than cnt. Report what was actually copied. + */ + cnt = copied; out: lens->nr = cnt; @@ -847,17 +875,32 @@ static void rds6_sock_info(struct socket *sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { + struct net *net = sock_net(sock->sk); struct rds6_info_socket sinfo6; + unsigned int copied = 0; + unsigned int cnt = 0; struct rds_sock *rs; len /= sizeof(struct rds6_info_socket); spin_lock_bh(&rds_sock_lock); - if (len < rds_sock_count) + /* First pass: count entries visible in the caller's netns. */ + list_for_each_entry(rs, &rds_sock_list, rs_item) { + if (!net_eq(sock_net(rds_rs_to_sk(rs)), net)) + continue; + cnt++; + } + + if (len < cnt) goto out; list_for_each_entry(rs, &rds_sock_list, rs_item) { + if (copied >= cnt) + break; + /* Only show sockets in the caller's netns. */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), net)) + continue; sinfo6.sndbuf = rds_sk_sndbuf(rs); sinfo6.rcvbuf = rds_sk_rcvbuf(rs); sinfo6.bound_addr = rs->rs_bound_addr; @@ -867,10 +910,12 @@ static void rds6_sock_info(struct socket *sock, unsigned int len, sinfo6.inum = sock_i_ino(rds_rs_to_sk(rs)); rds_info_copy(iter, &sinfo6, sizeof(sinfo6)); + copied++; } + cnt = copied; out: - lens->nr = rds_sock_count; + lens->nr = cnt; lens->each = sizeof(struct rds6_info_socket); spin_unlock_bh(&rds_sock_lock); diff --git a/net/rds/connection.c b/net/rds/connection.c index c10b7ed06c49..7c8ab8e973e1 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -568,6 +568,7 @@ static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len, struct rds_info_lengths *lens, int want_send, bool isv6) { + struct net *net = sock_net(sock->sk); struct hlist_head *head; struct list_head *list; struct rds_connection *conn; @@ -590,6 +591,9 @@ static void rds_conn_message_info_cmn(struct socket *sock, unsigned int len, struct rds_conn_path *cp; int npaths; + /* Only show connections in the caller's netns. */ + if (!net_eq(rds_conn_net(conn), net)) + continue; if (!isv6 && conn->c_isv6) continue; @@ -688,6 +692,7 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, u64 *buffer, size_t item_len) { + struct net *net = sock_net(sock->sk); struct hlist_head *head; struct rds_connection *conn; size_t i; @@ -700,6 +705,9 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, for (i = 0, head = rds_conn_hash; i < ARRAY_SIZE(rds_conn_hash); i++, head++) { hlist_for_each_entry_rcu(conn, head, c_hash_node) { + /* Only show connections in the caller's netns. */ + if (!net_eq(rds_conn_net(conn), net)) + continue; /* Zero the per-item buffer before handing it to the * visitor so any field the visitor does not write - @@ -733,6 +741,7 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, u64 *buffer, size_t item_len) { + struct net *net = sock_net(sock->sk); struct hlist_head *head; struct rds_connection *conn; size_t i; @@ -747,6 +756,10 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, hlist_for_each_entry_rcu(conn, head, c_hash_node) { struct rds_conn_path *cp; + /* Only show connections in the caller's netns. */ + if (!net_eq(rds_conn_net(conn), net)) + continue; + /* XXX We only copy the information from the first * path for now. The problem is that if there are * more than one underlying paths, we cannot report diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 4001de0c4959..5667f0173b47 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -1039,6 +1039,19 @@ int rds_ib_conn_path_connect(struct rds_conn_path *cp) return ret; } +static unsigned long rds_ib_conn_path_shutdown_check_wait(struct rds_conn_path *cp) +{ + struct rds_connection *conn = cp->cp_conn; + struct rds_ib_connection *ic = conn->c_transport_data; + + return (!ic->i_cm_id || + (rds_ib_ring_empty(&ic->i_recv_ring) && + (atomic_read(&ic->i_signaled_sends) == 0) && + (atomic_read(&ic->i_fastreg_inuse_count)) == 0 && + (atomic_read(&ic->i_fastreg_wrs) == RDS_IB_DEFAULT_FR_WR))) ? 0 + : msecs_to_jiffies(1000); +} + /* * This is so careful about only cleaning up resources that were built up * so that it can be called at any point during startup. In fact it @@ -1079,11 +1092,13 @@ void rds_ib_conn_path_shutdown(struct rds_conn_path *cp) * sends to complete we're ensured that there will be no * more tx processing. */ - wait_event(rds_ib_ring_empty_wait, - rds_ib_ring_empty(&ic->i_recv_ring) && - (atomic_read(&ic->i_signaled_sends) == 0) && - (atomic_read(&ic->i_fastreg_inuse_count) == 0) && - (atomic_read(&ic->i_fastreg_wrs) == RDS_IB_DEFAULT_FR_WR)); + while (!wait_event_timeout(rds_ib_ring_empty_wait, + rds_ib_conn_path_shutdown_check_wait(cp) == 0, + msecs_to_jiffies(1000))) { + tasklet_schedule(&ic->i_send_tasklet); + tasklet_schedule(&ic->i_recv_tasklet); + } + tasklet_kill(&ic->i_send_tasklet); tasklet_kill(&ic->i_recv_tasklet); diff --git a/net/rds/info.c b/net/rds/info.c index 17061f6ff74e..21b32eb16559 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "rds.h" @@ -144,39 +145,41 @@ void rds_info_copy(struct rds_info_iterator *iter, void *data, EXPORT_SYMBOL_GPL(rds_info_copy); /* - * @optval points to the userspace buffer that the information snapshot - * will be copied into. - * - * @optlen on input is the size of the buffer in userspace. @optlen - * on output is the size of the requested snapshot in bytes. + * @opt->iter_out describes the buffer that the information snapshot will be + * copied into, and @opt->optlen is the size of that buffer on input. On + * output @opt->optlen is set to the size of the requested snapshot in bytes. * * This function returns -errno if there is a failure, particularly -ENOSPC - * if the given userspace buffer was not large enough to fit the snapshot. - * On success it returns the positive number of bytes of each array element - * in the snapshot. + * if the given buffer was not large enough to fit the snapshot. On success + * it returns the positive number of bytes of each array element in the + * snapshot. */ -int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, - int __user *optlen) +int rds_info_getsockopt(struct socket *sock, int optname, sockopt_t *opt) { struct rds_info_iterator iter; struct rds_info_lengths lens; unsigned long nr_pages = 0; - unsigned long start; rds_info_func func; struct page **pages = NULL; + size_t offset0 = 0; + int npages = 0; int ret; int len; int total; - if (get_user(len, optlen)) { - ret = -EFAULT; + len = opt->optlen; + + /* check for all kinds of wrapping and the like */ + if (len < 0 || len > INT_MAX - PAGE_SIZE + 1) { + ret = -EINVAL; goto out; } - /* check for all kinds of wrapping and the like */ - start = (unsigned long)optval; - if (len < 0 || len > INT_MAX - PAGE_SIZE + 1 || start + len < start) { - ret = -EINVAL; + /* The info producers write into the pages with kmap_atomic() while + * holding a spinlock, so they need a genuine page-backed user buffer. + */ + if (!user_backed_iter(&opt->iter_out)) { + ret = -EOPNOTSUPP; goto out; } @@ -184,20 +187,26 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, if (len == 0) goto call_func; - nr_pages = (PAGE_ALIGN(start + len) - (start & PAGE_MASK)) - >> PAGE_SHIFT; - - pages = kmalloc_objs(struct page *, nr_pages); + /* + * Preallocate the page array and pass it in so that + * iov_iter_extract_pages() fills it in place rather than allocating + * one for us. Handing it a non-NULL array keeps ownership of the + * array with us on every return path, instead of depending on the + * iterator code to allocate and hand it back. + */ + npages = iov_iter_npages(&opt->iter_out, INT_MAX); + pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL); if (!pages) { ret = -ENOMEM; goto out; } - ret = pin_user_pages_fast(start, nr_pages, FOLL_WRITE, pages); - if (ret != nr_pages) { - if (ret > 0) - nr_pages = ret; - else - nr_pages = 0; + + ret = iov_iter_extract_pages(&opt->iter_out, &pages, len, npages, + 0, &offset0); + if (ret < 0) + goto out; + nr_pages = DIV_ROUND_UP(offset0 + ret, PAGE_SIZE); + if (ret != len) { ret = -EAGAIN; /* XXX ? */ goto out; } @@ -213,7 +222,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, iter.pages = pages; iter.addr = NULL; - iter.offset = start & (PAGE_SIZE - 1); + iter.offset = offset0; func(sock, len, &iter, &lens); BUG_ON(lens.each == 0); @@ -230,13 +239,16 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, ret = lens.each; } - if (put_user(len, optlen)) - ret = -EFAULT; + opt->optlen = len; out: - if (pages) + /* + * iov_iter_extract_pages() pins only user-backed (ubuf) iters; + * iov_iter_extract_will_pin() reports whether an unpin is owed here. + */ + if (pages && iov_iter_extract_will_pin(&opt->iter_out)) unpin_user_pages_dirty_lock(pages, nr_pages, true); - kfree(pages); + kvfree(pages); return ret; } diff --git a/net/rds/info.h b/net/rds/info.h index a069b51c4679..1aab62ab6d00 100644 --- a/net/rds/info.h +++ b/net/rds/info.h @@ -21,8 +21,7 @@ typedef void (*rds_info_func)(struct socket *sock, unsigned int len, void rds_info_register_func(int optname, rds_info_func func); void rds_info_deregister_func(int optname, rds_info_func func); -int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, - int __user *optlen); +int rds_info_getsockopt(struct socket *sock, int optname, sockopt_t *opt); void rds_info_copy(struct rds_info_iterator *iter, void *data, unsigned long bytes); void rds_info_iter_unmap(struct rds_info_iterator *iter); diff --git a/net/rds/send.c b/net/rds/send.c index d8b14ff9d366..e5d58c29aabe 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -1388,7 +1388,7 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) ret = rds_cong_wait(conn->c_fcong, dport, nonblock, rs); if (ret) { - rs->rs_seen_congestion = 1; + WRITE_ONCE(rs->rs_seen_congestion, 1); goto out; } while (!rds_send_queue_rm(rs, conn, cpath, rm, rs->rs_bound_port, diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 5830b31a1f37..a1de114d5e2e 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -46,14 +46,6 @@ static DEFINE_SPINLOCK(rds_tcp_tc_list_lock); static LIST_HEAD(rds_tcp_tc_list); -/* rds_tcp_tc_count counts only IPv4 connections. - * rds6_tcp_tc_count counts both IPv4 and IPv6 connections. - */ -static unsigned int rds_tcp_tc_count; -#if IS_ENABLED(CONFIG_IPV6) -static unsigned int rds6_tcp_tc_count; -#endif - /* Track rds_tcp_connection structs so they can be cleaned up */ static DEFINE_SPINLOCK(rds_tcp_conn_lock); static LIST_HEAD(rds_tcp_conn_list); @@ -110,11 +102,6 @@ void rds_tcp_restore_callbacks(struct socket *sock, /* done under the callback_lock to serialize with write_space */ spin_lock(&rds_tcp_tc_list_lock); list_del_init(&tc->t_list_item); -#if IS_ENABLED(CONFIG_IPV6) - rds6_tcp_tc_count--; -#endif - if (!tc->t_cpath->cp_conn->c_isv6) - rds_tcp_tc_count--; spin_unlock(&rds_tcp_tc_list_lock); tc->t_sock = NULL; @@ -206,11 +193,6 @@ void rds_tcp_set_callbacks(struct socket *sock, struct rds_conn_path *cp) spin_lock(&rds_tcp_tc_list_lock); tc->t_sock = sock; list_add_tail(&tc->t_list_item, &rds_tcp_tc_list); -#if IS_ENABLED(CONFIG_IPV6) - rds6_tcp_tc_count++; -#endif - if (!tc->t_cpath->cp_conn->c_isv6) - rds_tcp_tc_count++; spin_unlock(&rds_tcp_tc_list_lock); /* accepted sockets need our listen data ready undone */ @@ -238,20 +220,37 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { + struct net *net = sock_net(rds_sock->sk); struct rds_info_tcp_socket tsinfo; struct rds_tcp_connection *tc; + unsigned int copied = 0; + unsigned int cnt = 0; unsigned long flags; spin_lock_irqsave(&rds_tcp_tc_list_lock, flags); - if (len / sizeof(tsinfo) < rds_tcp_tc_count) + /* First pass: count entries visible in the caller's netns. */ + list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { + if (tc->t_cpath->cp_conn->c_isv6) + continue; + if (!net_eq(rds_conn_net(tc->t_cpath->cp_conn), net)) + continue; + cnt++; + } + + if (len / sizeof(tsinfo) < cnt) goto out; list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { struct inet_sock *inet = inet_sk(tc->t_sock->sk); + if (copied >= cnt) + break; if (tc->t_cpath->cp_conn->c_isv6) continue; + /* Only show connections in the caller's netns. */ + if (!net_eq(rds_conn_net(tc->t_cpath->cp_conn), net)) + continue; tsinfo.local_addr = inet->inet_saddr; tsinfo.local_port = inet->inet_sport; @@ -266,10 +265,12 @@ static void rds_tcp_tc_info(struct socket *rds_sock, unsigned int len, tsinfo.tos = tc->t_cpath->cp_conn->c_tos; rds_info_copy(iter, &tsinfo, sizeof(tsinfo)); + copied++; } + cnt = copied; out: - lens->nr = rds_tcp_tc_count; + lens->nr = cnt; lens->each = sizeof(tsinfo); spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags); @@ -284,19 +285,35 @@ static void rds6_tcp_tc_info(struct socket *sock, unsigned int len, struct rds_info_iterator *iter, struct rds_info_lengths *lens) { + struct net *net = sock_net(sock->sk); struct rds6_info_tcp_socket tsinfo6; struct rds_tcp_connection *tc; + unsigned int copied = 0; + unsigned int cnt = 0; unsigned long flags; spin_lock_irqsave(&rds_tcp_tc_list_lock, flags); - if (len / sizeof(tsinfo6) < rds6_tcp_tc_count) + /* First pass: count entries visible in the caller's netns. */ + list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { + if (!net_eq(rds_conn_net(tc->t_cpath->cp_conn), net)) + continue; + cnt++; + } + + if (len / sizeof(tsinfo6) < cnt) goto out; list_for_each_entry(tc, &rds_tcp_tc_list, t_list_item) { struct sock *sk = tc->t_sock->sk; struct inet_sock *inet = inet_sk(sk); + if (copied >= cnt) + break; + /* Only show connections in the caller's netns. */ + if (!net_eq(rds_conn_net(tc->t_cpath->cp_conn), net)) + continue; + tsinfo6.local_addr = sk->sk_v6_rcv_saddr; tsinfo6.local_port = inet->inet_sport; tsinfo6.peer_addr = sk->sk_v6_daddr; @@ -309,10 +326,12 @@ static void rds6_tcp_tc_info(struct socket *sock, unsigned int len, tsinfo6.last_seen_una = tc->t_last_seen_una; rds_info_copy(iter, &tsinfo6, sizeof(tsinfo6)); + copied++; } + cnt = copied; out: - lens->nr = rds6_tcp_tc_count; + lens->nr = cnt; lens->each = sizeof(tsinfo6); spin_unlock_irqrestore(&rds_tcp_tc_list_lock, flags); diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index 08a506aa7ce7..a3db9b057084 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -69,7 +69,7 @@ rds_tcp_get_peer_sport(struct socket *sock) /* rds_tcp_accept_one_path(): if accepting on cp_index > 0, make sure the * client's ipaddr < server's ipaddr. Otherwise, close the accepted - * socket and force a reconneect from smaller -> larger ip addr. The reason + * socket and force a reconnect from smaller -> larger ip addr. The reason * we special case cp_index 0 is to allow the rds probe ping itself to itself * get through efficiently. */ @@ -143,7 +143,7 @@ void rds_tcp_conn_slots_available(struct rds_connection *conn, bool fan_out) * * Doing so is necessary to address the case where an * incoming connection on "rds_tcp_listen_sock" is ready - * to be acccepted prior to a free slot being available: + * to be accepted prior to a free slot being available: * the -ENOBUFS case in "rds_tcp_accept_one". */ rds_tcp_accept_work(rtn); diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 4827e1fb8804..9e143c4bfe6a 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1000,6 +1000,7 @@ struct rfkill * __must_check rfkill_alloc(const char *name, { struct rfkill *rfkill; struct device *dev; + size_t name_len; if (WARN_ON(!ops)) return NULL; @@ -1013,14 +1014,15 @@ struct rfkill * __must_check rfkill_alloc(const char *name, if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES)) return NULL; - rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL); + name_len = strlen(name); + rfkill = kzalloc(sizeof(*rfkill) + name_len + 1, GFP_KERNEL); if (!rfkill) return NULL; spin_lock_init(&rfkill->lock); INIT_LIST_HEAD(&rfkill->node); rfkill->type = type; - strcpy(rfkill->name, name); + memcpy(rfkill->name, name, name_len); rfkill->ops = ops; rfkill->data = ops_data; diff --git a/net/rxrpc/.kunitconfig b/net/rxrpc/.kunitconfig new file mode 100644 index 000000000000..44e4a909ff07 --- /dev/null +++ b/net/rxrpc/.kunitconfig @@ -0,0 +1,6 @@ +CONFIG_KUNIT=y +CONFIG_NET=y +CONFIG_INET=y +CONFIG_AF_RXRPC=y +CONFIG_RXKAD=y +CONFIG_AF_RXRPC_KUNIT_TEST=y diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig index 43416b3026fb..2cebb666dc07 100644 --- a/net/rxrpc/Kconfig +++ b/net/rxrpc/Kconfig @@ -7,6 +7,7 @@ config AF_RXRPC tristate "RxRPC session sockets" depends on INET select CRYPTO + select CRYPTO_LIB_DES if RXKAD select KEYS select NET_UDP_TUNNEL help @@ -56,11 +57,6 @@ config AF_RXRPC_DEBUG config RXKAD bool "RxRPC Kerberos security" - select CRYPTO - select CRYPTO_MANAGER - select CRYPTO_SKCIPHER - select CRYPTO_PCBC - select CRYPTO_FCRYPT help Provide kerberos 4 and AFS kaserver security handling for AF_RXRPC through the use of the key retention service. @@ -97,4 +93,11 @@ config RXPERF incoming calls from the rxperf program (an example of which can be found in OpenAFS). +config AF_RXRPC_KUNIT_TEST + tristate "RxRPC crypto KUnit test" if !KUNIT_ALL_TESTS + depends on KUNIT && RXKAD + default KUNIT_ALL_TESTS + help + Enable the KUnit test suite for RxRPC's crypto routines. + endif diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile index c0542bae719e..f994f9f30a29 100644 --- a/net/rxrpc/Makefile +++ b/net/rxrpc/Makefile @@ -38,7 +38,7 @@ rxrpc-y := \ utils.o rxrpc-$(CONFIG_PROC_FS) += proc.o -rxrpc-$(CONFIG_RXKAD) += rxkad.o +rxrpc-$(CONFIG_RXKAD) += rxkad.o fcrypt.o rxrpc-$(CONFIG_SYSCTL) += sysctl.o rxrpc-$(CONFIG_RXGK) += \ rxgk.o \ @@ -46,3 +46,4 @@ rxrpc-$(CONFIG_RXGK) += \ rxgk_kdf.o obj-$(CONFIG_RXPERF) += rxperf.o +obj-$(CONFIG_KUNIT) += tests/ diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 32ec91fa938f..9ab0f22c881e 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -743,23 +744,24 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, * Get socket options. */ static int rxrpc_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *_optlen) + sockopt_t *opt) { - int optlen; + int optlen, val; if (level != SOL_RXRPC) return -EOPNOTSUPP; - if (get_user(optlen, _optlen)) - return -EFAULT; + optlen = opt->optlen; switch (optname) { case RXRPC_SUPPORTED_CMSG: if (optlen < sizeof(int)) return -ETOOSMALL; - if (put_user(RXRPC__SUPPORTED - 1, (int __user *)optval) || - put_user(sizeof(int), _optlen)) + val = RXRPC__SUPPORTED - 1; + if (copy_to_iter(&val, sizeof(val), &opt->iter_out) != + sizeof(val)) return -EFAULT; + opt->optlen = sizeof(val); return 0; default: @@ -1009,7 +1011,7 @@ static const struct proto_ops rxrpc_rpc_ops = { .listen = rxrpc_listen, .shutdown = rxrpc_shutdown, .setsockopt = rxrpc_setsockopt, - .getsockopt = rxrpc_getsockopt, + .getsockopt_iter = rxrpc_getsockopt, .sendmsg = rxrpc_sendmsg, .recvmsg = rxrpc_recvmsg, .mmap = sock_no_mmap, diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 98f2165159d7..5802f6f78723 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -15,6 +15,12 @@ #include #include "protocol.h" +#define FCRYPT_ROUNDS 16 + +struct fcrypt_key { + __be32 sched[FCRYPT_ROUNDS]; +}; + #define FCRYPT_BSIZE 8 struct rxrpc_crypt { union { @@ -23,6 +29,19 @@ struct rxrpc_crypt { }; } __attribute__((aligned(8))); +void fcrypt_preparekey(struct fcrypt_key *key, const u8 raw_key[FCRYPT_BSIZE]); +void fcrypt_pcbc_encrypt(const struct fcrypt_key *key, + const u8 iv[FCRYPT_BSIZE], const void *src, void *dst, + size_t nblocks); +void fcrypt_pcbc_decrypt(const struct fcrypt_key *key, + const u8 iv[FCRYPT_BSIZE], const void *src, void *dst, + size_t nblocks); +#if IS_ENABLED(CONFIG_KUNIT) +struct des_ctx; +void des_pcbc_decrypt_inplace(const struct des_ctx *key, __le64 iv, u8 *data, + size_t len); +#endif + #define rxrpc_queue_work(WS) queue_work(rxrpc_workqueue, (WS)) #define rxrpc_queue_delayed_work(WS,D) \ queue_delayed_work(rxrpc_workqueue, (WS), (D)) @@ -563,7 +582,7 @@ struct rxrpc_connection { const struct rxrpc_security *security; /* applied security module */ union { struct { - struct crypto_sync_skcipher *cipher; /* encryption handle */ + struct fcrypt_key *cipher; /* encryption key */ struct rxrpc_crypt csum_iv; /* packet checksum base */ u32 nonce; /* response re-use preventer */ } rxkad; diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index ee2d1319e69a..47824120f1da 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -471,13 +471,26 @@ int rxrpc_kernel_charge_accept(struct socket *sock, rxrpc_notify_rx_t notify_rx, unsigned long user_call_ID, gfp_t gfp, unsigned int debug_id) { - struct rxrpc_sock *rx = rxrpc_sk(sock->sk); - struct rxrpc_backlog *b = rx->backlog; + struct rxrpc_backlog *b; + struct rxrpc_sock *rx; + struct sock *sk; + int ret; - if (sock->sk->sk_state == RXRPC_CLOSE) - return -ESHUTDOWN; + sk = sock->sk; + rx = rxrpc_sk(sk); - return rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID, - gfp, debug_id); + lock_sock(sk); + if (sk->sk_state != RXRPC_SERVER_LISTENING || !rx->backlog) { + ret = -ESHUTDOWN; + goto out; + } + + b = rx->backlog; + ret = rxrpc_service_prealloc_one(rx, b, notify_rx, user_call_ID, + gfp, debug_id); + +out: + release_sock(sk); + return ret; } EXPORT_SYMBOL(rxrpc_kernel_charge_accept); diff --git a/crypto/fcrypt.c b/net/rxrpc/fcrypt.c similarity index 65% rename from crypto/fcrypt.c rename to net/rxrpc/fcrypt.c index 80036835cec5..a919e7598765 100644 --- a/crypto/fcrypt.c +++ b/net/rxrpc/fcrypt.c @@ -43,27 +43,10 @@ */ #include -#include -#include -#include -#include - -#define ROUNDS 16 - -struct fcrypt_ctx { - __be32 sched[ROUNDS]; -}; - -/* Rotate right two 32 bit numbers as a 56 bit number */ -#define ror56(hi, lo, n) \ -do { \ - u32 t = lo & ((1 << n) - 1); \ - lo = (lo >> n) | ((hi & ((1 << n) - 1)) << (32 - n)); \ - hi = (hi >> n) | (t << (24-n)); \ -} while (0) - -/* Rotate right one 64 bit number as a 56 bit number */ -#define ror56_64(k, n) (k = (k >> n) | ((k & ((1 << n) - 1)) << (56 - n))) +#include +#include +#include +#include "ar-internal.h" /* * Sboxes for Feistel network derived from @@ -217,204 +200,153 @@ static const __be32 sbox3[256] = { Z(0xa1), Z(0xc2), Z(0xc5), Z(0xe3), Z(0xba), Z(0xfc), Z(0x0e), Z(0x25) }; -/* - * This is a 16 round Feistel network with permutation F_ENCRYPT - */ -#define F_ENCRYPT(R, L, sched) \ -do { \ - union lc4 { __be32 l; u8 c[4]; } u; \ - u.l = sched ^ R; \ - L ^= sbox0[u.c[0]] ^ sbox1[u.c[1]] ^ sbox2[u.c[2]] ^ sbox3[u.c[3]]; \ -} while (0) - -/* - * encryptor - */ -static void fcrypt_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) -{ - const struct fcrypt_ctx *ctx = crypto_tfm_ctx(tfm); +union fcrypt_block { + __be64 a; struct { __be32 l, r; - } X; - - memcpy(&X, src, sizeof(X)); - - F_ENCRYPT(X.r, X.l, ctx->sched[0x0]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x1]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x2]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x3]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x4]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x5]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x6]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x7]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x8]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x9]); - F_ENCRYPT(X.r, X.l, ctx->sched[0xa]); - F_ENCRYPT(X.l, X.r, ctx->sched[0xb]); - F_ENCRYPT(X.r, X.l, ctx->sched[0xc]); - F_ENCRYPT(X.l, X.r, ctx->sched[0xd]); - F_ENCRYPT(X.r, X.l, ctx->sched[0xe]); - F_ENCRYPT(X.l, X.r, ctx->sched[0xf]); - - memcpy(dst, &X, sizeof(X)); -} - -/* - * decryptor - */ -static void fcrypt_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) -{ - const struct fcrypt_ctx *ctx = crypto_tfm_ctx(tfm); - struct { - __be32 l, r; - } X; - - memcpy(&X, src, sizeof(X)); - - F_ENCRYPT(X.l, X.r, ctx->sched[0xf]); - F_ENCRYPT(X.r, X.l, ctx->sched[0xe]); - F_ENCRYPT(X.l, X.r, ctx->sched[0xd]); - F_ENCRYPT(X.r, X.l, ctx->sched[0xc]); - F_ENCRYPT(X.l, X.r, ctx->sched[0xb]); - F_ENCRYPT(X.r, X.l, ctx->sched[0xa]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x9]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x8]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x7]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x6]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x5]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x4]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x3]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x2]); - F_ENCRYPT(X.l, X.r, ctx->sched[0x1]); - F_ENCRYPT(X.r, X.l, ctx->sched[0x0]); - - memcpy(dst, &X, sizeof(X)); -} - -/* - * Generate a key schedule from key, the least significant bit in each key byte - * is parity and shall be ignored. This leaves 56 significant bits in the key - * to scatter over the 16 key schedules. For each schedule extract the low - * order 32 bits and use as schedule, then rotate right by 11 bits. - */ -static int fcrypt_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) -{ - struct fcrypt_ctx *ctx = crypto_tfm_ctx(tfm); - -#if BITS_PER_LONG == 64 /* the 64-bit version can also be used for 32-bit - * kernels - it seems to be faster but the code is - * larger */ - - u64 k; /* k holds all 56 non-parity bits */ - - /* discard the parity bits */ - k = (*key++) >> 1; - k <<= 7; - k |= (*key++) >> 1; - k <<= 7; - k |= (*key++) >> 1; - k <<= 7; - k |= (*key++) >> 1; - k <<= 7; - k |= (*key++) >> 1; - k <<= 7; - k |= (*key++) >> 1; - k <<= 7; - k |= (*key++) >> 1; - k <<= 7; - k |= (*key) >> 1; - - /* Use lower 32 bits for schedule, rotate by 11 each round (16 times) */ - ctx->sched[0x0] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x1] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x2] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x3] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x4] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x5] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x6] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x7] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x8] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0x9] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0xa] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0xb] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0xc] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0xd] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0xe] = cpu_to_be32(k); ror56_64(k, 11); - ctx->sched[0xf] = cpu_to_be32(k); - - return 0; -#else - u32 hi, lo; /* hi is upper 24 bits and lo lower 32, total 56 */ - - /* discard the parity bits */ - lo = (*key++) >> 1; - lo <<= 7; - lo |= (*key++) >> 1; - lo <<= 7; - lo |= (*key++) >> 1; - lo <<= 7; - lo |= (*key++) >> 1; - hi = lo >> 4; - lo &= 0xf; - lo <<= 7; - lo |= (*key++) >> 1; - lo <<= 7; - lo |= (*key++) >> 1; - lo <<= 7; - lo |= (*key++) >> 1; - lo <<= 7; - lo |= (*key) >> 1; - - /* Use lower 32 bits for schedule, rotate by 11 each round (16 times) */ - ctx->sched[0x0] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x1] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x2] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x3] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x4] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x5] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x6] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x7] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x8] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0x9] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0xa] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0xb] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0xc] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0xd] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0xe] = cpu_to_be32(lo); ror56(hi, lo, 11); - ctx->sched[0xf] = cpu_to_be32(lo); - return 0; -#endif -} - -static struct crypto_alg fcrypt_alg = { - .cra_name = "fcrypt", - .cra_driver_name = "fcrypt-generic", - .cra_flags = CRYPTO_ALG_TYPE_CIPHER, - .cra_blocksize = 8, - .cra_ctxsize = sizeof(struct fcrypt_ctx), - .cra_module = THIS_MODULE, - .cra_u = { .cipher = { - .cia_min_keysize = 8, - .cia_max_keysize = 8, - .cia_setkey = fcrypt_setkey, - .cia_encrypt = fcrypt_encrypt, - .cia_decrypt = fcrypt_decrypt } } + }; }; -static int __init fcrypt_mod_init(void) +#define F_ENCRYPT(R, L, sched) \ + do { \ + union lc4 { \ + __be32 l; \ + u8 c[4]; \ + } u; \ + u.l = sched ^ R; \ + L ^= sbox0[u.c[0]] ^ sbox1[u.c[1]] ^ sbox2[u.c[2]] ^ \ + sbox3[u.c[3]]; \ + } while (0) + +/* Encrypt one block using FCrypt. */ +static __be64 fcrypt_encrypt(const struct fcrypt_key *key, __be64 ptext) { - return crypto_register_alg(&fcrypt_alg); + union fcrypt_block X = { .a = ptext }; + + /* This is a 16 round Feistel network with permutation F_ENCRYPT. */ + F_ENCRYPT(X.r, X.l, key->sched[0x0]); + F_ENCRYPT(X.l, X.r, key->sched[0x1]); + F_ENCRYPT(X.r, X.l, key->sched[0x2]); + F_ENCRYPT(X.l, X.r, key->sched[0x3]); + F_ENCRYPT(X.r, X.l, key->sched[0x4]); + F_ENCRYPT(X.l, X.r, key->sched[0x5]); + F_ENCRYPT(X.r, X.l, key->sched[0x6]); + F_ENCRYPT(X.l, X.r, key->sched[0x7]); + F_ENCRYPT(X.r, X.l, key->sched[0x8]); + F_ENCRYPT(X.l, X.r, key->sched[0x9]); + F_ENCRYPT(X.r, X.l, key->sched[0xa]); + F_ENCRYPT(X.l, X.r, key->sched[0xb]); + F_ENCRYPT(X.r, X.l, key->sched[0xc]); + F_ENCRYPT(X.l, X.r, key->sched[0xd]); + F_ENCRYPT(X.r, X.l, key->sched[0xe]); + F_ENCRYPT(X.l, X.r, key->sched[0xf]); + return X.a; } -static void __exit fcrypt_mod_fini(void) +/* Decrypt one block using FCrypt. */ +static __be64 fcrypt_decrypt(const struct fcrypt_key *key, __be64 ctext) { - crypto_unregister_alg(&fcrypt_alg); + union fcrypt_block X = { .a = ctext }; + + /* This is a 16 round Feistel network with permutation F_ENCRYPT. */ + F_ENCRYPT(X.l, X.r, key->sched[0xf]); + F_ENCRYPT(X.r, X.l, key->sched[0xe]); + F_ENCRYPT(X.l, X.r, key->sched[0xd]); + F_ENCRYPT(X.r, X.l, key->sched[0xc]); + F_ENCRYPT(X.l, X.r, key->sched[0xb]); + F_ENCRYPT(X.r, X.l, key->sched[0xa]); + F_ENCRYPT(X.l, X.r, key->sched[0x9]); + F_ENCRYPT(X.r, X.l, key->sched[0x8]); + F_ENCRYPT(X.l, X.r, key->sched[0x7]); + F_ENCRYPT(X.r, X.l, key->sched[0x6]); + F_ENCRYPT(X.l, X.r, key->sched[0x5]); + F_ENCRYPT(X.r, X.l, key->sched[0x4]); + F_ENCRYPT(X.l, X.r, key->sched[0x3]); + F_ENCRYPT(X.r, X.l, key->sched[0x2]); + F_ENCRYPT(X.l, X.r, key->sched[0x1]); + F_ENCRYPT(X.r, X.l, key->sched[0x0]); + return X.a; } -module_init(fcrypt_mod_init); -module_exit(fcrypt_mod_fini); +/** + * fcrypt_preparekey - Prepare a key for FCrypt encryption and decryption + * @key: (out) The prepared key + * @raw_key: The raw key as an 8-byte array + * + * This computes the FCrypt key schedule. + */ +void fcrypt_preparekey(struct fcrypt_key *key, const u8 raw_key[FCRYPT_BSIZE]) +{ + u64 k = 0; -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("FCrypt Cipher Algorithm"); -MODULE_AUTHOR("David Howells "); -MODULE_ALIAS_CRYPTO("fcrypt"); + /* Load the 56 non-parity bits of the key. Discard the parity bits. */ + for (int i = 0; i < 8; i++) + k = (k << 7) | (raw_key[i] >> 1); + + /* Generate the key schedule word for each round. */ + for (int i = 0; i < FCRYPT_ROUNDS; i++) { + key->sched[i] = cpu_to_be32(k); + /* Rotate the low 56 bits of 'k' right by 11 bits. */ + k = (k >> 11) | ((k & ((1 << 11) - 1)) << (56 - 11)); + } +} +EXPORT_SYMBOL_IF_KUNIT(fcrypt_preparekey); + +/** + * fcrypt_pcbc_encrypt - Encrypt data using FCrypt cipher in PCBC mode + * @key: The key + * @iv: The 8-byte initialization vector + * @src: The source data + * @dst: The destination data. Both in-place and out-of-place are supported. + * @nblocks: The number of 8-byte blocks to encrypt + * + * WARNING: This cipher is insecure. Not only is the 56-bit key easily + * brute-forced, the cipher itself is cryptographically weak and doesn't even + * provide the intended 56-bit security level. It effectively just acts as an + * obfuscation algorithm. It is supported only for backwards compatibility. + */ +void fcrypt_pcbc_encrypt(const struct fcrypt_key *key, + const u8 iv[FCRYPT_BSIZE], const void *src, void *dst, + size_t nblocks) +{ + __be64 prev = get_unaligned((const __be64 *)iv); + const __be64 *src_blocks = src; + __be64 *dst_blocks = dst; + + while (nblocks--) { + __be64 ptext, ctext; + + ptext = get_unaligned(src_blocks++); + ctext = fcrypt_encrypt(key, prev ^ ptext); + put_unaligned(ctext, dst_blocks++); + prev = ptext ^ ctext; + } +} +EXPORT_SYMBOL_IF_KUNIT(fcrypt_pcbc_encrypt); + +/** + * fcrypt_pcbc_decrypt - Decrypt data using FCrypt cipher in PCBC mode + * @key: The key + * @iv: The 8-byte initialization vector + * @src: The source data + * @dst: The destination data. Both in-place and out-of-place are supported. + * @nblocks: The number of 8-byte blocks to decrypt + */ +void fcrypt_pcbc_decrypt(const struct fcrypt_key *key, + const u8 iv[FCRYPT_BSIZE], const void *src, void *dst, + size_t nblocks) +{ + __be64 prev = get_unaligned((const __be64 *)iv); + const __be64 *src_blocks = src; + __be64 *dst_blocks = dst; + + while (nblocks--) { + __be64 ptext, ctext; + + ctext = get_unaligned(src_blocks++); + ptext = prev ^ fcrypt_decrypt(key, ctext); + put_unaligned(ptext, dst_blocks++); + prev = ptext ^ ctext; + } +} +EXPORT_SYMBOL_IF_KUNIT(fcrypt_pcbc_decrypt); diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 3ec3d89fdf14..a0aa78d89289 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -10,7 +10,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index 111f574fe667..169f9dfdaa77 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -194,7 +194,7 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net) tuncfg.encap_rcv = rxrpc_encap_rcv; tuncfg.encap_err_rcv = rxrpc_encap_err_rcv; tuncfg.sk_user_data = local; - setup_udp_tunnel_sock(net, local->socket, &tuncfg); + setup_udp_tunnel_sock(net, local->socket->sk, &tuncfg); /* set the socket up */ usk = local->socket->sk; diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index c940600117a4..82614cbdb60f 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -161,7 +161,7 @@ static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb) struct rxrpc_skb_priv *sp = rxrpc_skb(skb); int ret; - if (sp->len > call->rx_dec_bsize) { + if (sp->len > call->rx_dec_bsize || !call->rx_dec_buffer) { /* Make sure we can hold a 1412-byte jumbo subpacket and make * sure that the buffer size is aligned to a crypto blocksize. */ @@ -262,12 +262,13 @@ static int rxrpc_recvmsg_oob(struct socket *sock, struct msghdr *msg, break; } - if (!(flags & MSG_PEEK)) + if (!(flags & MSG_PEEK)) { skb_unlink(skb, &rx->recvmsg_oobq); - if (need_response) - rxrpc_add_pending_oob(rx, skb); - else - rxrpc_free_skb(skb, rxrpc_skb_put_oob); + if (need_response) + rxrpc_add_pending_oob(rx, skb); + else + rxrpc_free_skb(skb, rxrpc_skb_put_oob); + } return ret; } diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index a1ee102abae1..77a67ace1d24 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -687,16 +687,17 @@ static int rxgk_issue_challenge(struct rxrpc_connection *conn) ret = do_udp_sendmsg(conn->local->socket, &msg, len); if (ret > 0) rxrpc_peer_mark_tx(conn->peer); - __free_page(page); if (ret < 0) { trace_rxrpc_tx_fail(conn->debug_id, serial, ret, rxrpc_tx_point_rxgk_challenge); + __free_page(page); return -EAGAIN; } trace_rxrpc_tx_packet(conn->debug_id, whdr, rxrpc_tx_point_rxgk_challenge); + __free_page(page); _leave(" = 0"); return 0; } diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 6fbd883401ac..ca9f0e82cb9a 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -7,15 +7,18 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include +#include +#include +#include +#include #include #include #include #include -#include #include #include #include +#include #include #include #include @@ -30,6 +33,8 @@ #define SNAME_SZ 40 /* size of service name */ #define RXKAD_ALIGN 8 +static const u8 zero_iv[FCRYPT_BSIZE]; + struct rxkad_level1_hdr { __be32 data_size; /* true data size (excluding padding) */ }; @@ -39,17 +44,8 @@ struct rxkad_level2_hdr { __be32 checksum; /* decrypted data checksum */ }; -static int rxkad_prime_packet_security(struct rxrpc_connection *conn, - struct crypto_sync_skcipher *ci); - -/* - * this holds a pinned cipher so that keventd doesn't get called by the cipher - * alloc routine, but since we have it to hand, we use it to decrypt RESPONSE - * packets - */ -static struct crypto_sync_skcipher *rxkad_ci; -static struct skcipher_request *rxkad_ci_req; -static DEFINE_MUTEX(rxkad_ci_mutex); +static void rxkad_prime_packet_security(struct rxrpc_connection *conn, + const struct fcrypt_key *cipher); /* * Parse the information from a server key @@ -58,40 +54,41 @@ static DEFINE_MUTEX(rxkad_ci_mutex); */ static int rxkad_preparse_server_key(struct key_preparsed_payload *prep) { - struct crypto_skcipher *ci; + struct des_ctx *des_key; + int err; if (prep->datalen != 8) return -EINVAL; memcpy(&prep->payload.data[2], prep->data, 8); - ci = crypto_alloc_skcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(ci)) { - _leave(" = %ld", PTR_ERR(ci)); - return PTR_ERR(ci); + des_key = kmalloc_obj(*des_key); + if (!des_key) { + _leave(" = -ENOMEM"); + return -ENOMEM; } - if (crypto_skcipher_setkey(ci, prep->data, 8) < 0) - BUG(); + err = des_expand_key(des_key, prep->data, 8); + if (err) { + kfree_sensitive(des_key); + _leave(" = %d", err); + return err; + } - prep->payload.data[0] = ci; + prep->payload.data[0] = des_key; _leave(" = 0"); return 0; } static void rxkad_free_preparse_server_key(struct key_preparsed_payload *prep) { - - if (prep->payload.data[0]) - crypto_free_skcipher(prep->payload.data[0]); + kfree_sensitive(prep->payload.data[0]); } static void rxkad_destroy_server_key(struct key *key) { - if (key->payload.data[0]) { - crypto_free_skcipher(key->payload.data[0]); - key->payload.data[0] = NULL; - } + kfree_sensitive(key->payload.data[0]); + key->payload.data[0] = NULL; } /* @@ -100,23 +97,19 @@ static void rxkad_destroy_server_key(struct key *key) static int rxkad_init_connection_security(struct rxrpc_connection *conn, struct rxrpc_key_token *token) { - struct crypto_sync_skcipher *ci; + struct fcrypt_key *ci; int ret; _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); conn->security_ix = token->security_index; - ci = crypto_alloc_sync_skcipher("pcbc(fcrypt)", 0, 0); - if (IS_ERR(ci)) { - _debug("no cipher"); - ret = PTR_ERR(ci); + ci = kmalloc_obj(*ci); + if (!ci) { + ret = -ENOMEM; goto error; } - - if (crypto_sync_skcipher_setkey(ci, token->kad->session_key, - sizeof(token->kad->session_key)) < 0) - BUG(); + fcrypt_preparekey(ci, token->kad->session_key); switch (conn->security_level) { case RXRPC_SECURITY_PLAIN: @@ -125,18 +118,16 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn, break; default: ret = -EKEYREJECTED; - goto error; + goto error_ci; } - ret = rxkad_prime_packet_security(conn, ci); - if (ret < 0) - goto error_ci; + rxkad_prime_packet_security(conn, ci); conn->rxkad.cipher = ci; return 0; error_ci: - crypto_free_sync_skcipher(ci); + kfree_sensitive(ci); error: _leave(" = %d", ret); return ret; @@ -188,62 +179,28 @@ static struct rxrpc_txbuf *rxkad_alloc_txbuf(struct rxrpc_call *call, size_t rem * prime the encryption state with the invariant parts of a connection's * description */ -static int rxkad_prime_packet_security(struct rxrpc_connection *conn, - struct crypto_sync_skcipher *ci) +static void rxkad_prime_packet_security(struct rxrpc_connection *conn, + const struct fcrypt_key *cipher) { - struct skcipher_request *req; struct rxrpc_key_token *token; - struct scatterlist sg; - struct rxrpc_crypt iv; - __be32 *tmpbuf; - size_t tmpsize = 4 * sizeof(__be32); - int ret; + __be32 tmpbuf[4]; _enter(""); if (!conn->key) - return 0; - - tmpbuf = kmalloc(tmpsize, GFP_KERNEL); - if (!tmpbuf) - return -ENOMEM; - - req = skcipher_request_alloc(&ci->base, GFP_NOFS); - if (!req) { - kfree(tmpbuf); - return -ENOMEM; - } - + return; token = conn->key->payload.data[0]; - memcpy(&iv, token->kad->session_key, sizeof(iv)); tmpbuf[0] = htonl(conn->proto.epoch); tmpbuf[1] = htonl(conn->proto.cid); tmpbuf[2] = 0; tmpbuf[3] = htonl(conn->security_ix); - sg_init_one(&sg, tmpbuf, tmpsize); - skcipher_request_set_sync_tfm(req, ci); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg, &sg, tmpsize, iv.x); - ret = crypto_skcipher_encrypt(req); - skcipher_request_free(req); - - memcpy(&conn->rxkad.csum_iv, tmpbuf + 2, sizeof(conn->rxkad.csum_iv)); - kfree(tmpbuf); - _leave(" = %d", ret); - return ret; -} - -/* - * Allocate and prepare the crypto request on a call. For any particular call, - * this is called serially for the packets, so no lock should be necessary. - */ -static struct skcipher_request *rxkad_get_call_crypto(struct rxrpc_call *call) -{ - struct crypto_skcipher *tfm = &call->conn->rxkad.cipher->base; - - return skcipher_request_alloc(tfm, GFP_NOFS); + static_assert(sizeof(tmpbuf) % FCRYPT_BSIZE == 0); + fcrypt_pcbc_encrypt(cipher, /* iv= */ token->kad->session_key, tmpbuf, + tmpbuf, sizeof(tmpbuf) / FCRYPT_BSIZE); + memcpy(&conn->rxkad.csum_iv, &tmpbuf[2], sizeof(conn->rxkad.csum_iv)); + _leave(""); } /* @@ -256,16 +213,12 @@ static void rxkad_free_call_crypto(struct rxrpc_call *call) /* * partially encrypt a packet (level 1 security) */ -static int rxkad_secure_packet_auth(const struct rxrpc_call *call, - struct rxrpc_txbuf *txb, - struct skcipher_request *req) +static void rxkad_secure_packet_auth(const struct rxrpc_call *call, + struct rxrpc_txbuf *txb) { struct rxkad_level1_hdr *hdr = txb->data; - struct rxrpc_crypt iv; - struct scatterlist sg; size_t pad; u16 check; - int ret; _enter(""); @@ -282,33 +235,20 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, } /* start the encryption afresh */ - memset(&iv, 0, sizeof(iv)); - - sg_init_one(&sg, hdr, 8); - skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); - ret = crypto_skcipher_encrypt(req); - skcipher_request_zero(req); - - _leave(" = %d", ret); - return ret; + fcrypt_pcbc_encrypt(call->conn->rxkad.cipher, zero_iv, hdr, hdr, 1); + _leave(""); } /* * wholly encrypt a packet (level 2 security) */ -static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, - struct rxrpc_txbuf *txb, - struct skcipher_request *req) +static void rxkad_secure_packet_encrypt(const struct rxrpc_call *call, + struct rxrpc_txbuf *txb) { const struct rxrpc_key_token *token; struct rxkad_level2_hdr *rxkhdr = txb->data; - struct rxrpc_crypt iv; - struct scatterlist sg; size_t content, pad; u16 check; - int ret; _enter(""); @@ -318,22 +258,18 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, rxkhdr->checksum = 0; content = sizeof(struct rxkad_level2_hdr) + txb->len; + static_assert(RXKAD_ALIGN == FCRYPT_BSIZE); txb->pkt_len = round_up(content, RXKAD_ALIGN); pad = txb->pkt_len - content; if (pad) memset(txb->data + txb->offset, 0, pad); + /* Now txb->pkt_len % FCRYPT_BSIZE == 0. */ /* encrypt from the session key */ token = call->conn->key->payload.data[0]; - memcpy(&iv, token->kad->session_key, sizeof(iv)); - - sg_init_one(&sg, rxkhdr, txb->pkt_len); - skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg, &sg, txb->pkt_len, iv.x); - ret = crypto_skcipher_encrypt(req); - skcipher_request_zero(req); - return ret; + fcrypt_pcbc_encrypt(call->conn->rxkad.cipher, token->kad->session_key, + rxkhdr, rxkhdr, txb->pkt_len / FCRYPT_BSIZE); + _leave(""); } /* @@ -341,9 +277,6 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, */ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) { - struct skcipher_request *req; - struct rxrpc_crypt iv; - struct scatterlist sg; union { __be32 buf[2]; } crypto __aligned(8); @@ -361,27 +294,16 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) if (ret < 0) return ret; - req = rxkad_get_call_crypto(call); - if (!req) - return -ENOMEM; - - /* continue encrypting from where we left off */ - memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv)); - /* calculate the security checksum */ x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); x |= txb->seq & 0x3fffffff; crypto.buf[0] = htonl(call->call_id); crypto.buf[1] = htonl(x); - sg_init_one(&sg, crypto.buf, 8); - skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); - ret = crypto_skcipher_encrypt(req); - skcipher_request_zero(req); - if (ret < 0) - goto out; + /* continue encrypting from where we left off */ + fcrypt_pcbc_encrypt(call->conn->rxkad.cipher, + call->conn->rxkad.csum_iv.x, crypto.buf, crypto.buf, + 1); y = ntohl(crypto.buf[1]); y = (y >> 16) & 0xffff; @@ -395,14 +317,16 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) ret = 0; break; case RXRPC_SECURITY_AUTH: - ret = rxkad_secure_packet_auth(call, txb, req); + rxkad_secure_packet_auth(call, txb); if (txb->alloc_size == RXRPC_JUMBO_DATALEN) txb->jumboable = true; + ret = 0; break; case RXRPC_SECURITY_ENCRYPT: - ret = rxkad_secure_packet_encrypt(call, txb, req); + rxkad_secure_packet_encrypt(call, txb); if (txb->alloc_size == RXRPC_JUMBO_DATALEN) txb->jumboable = true; + ret = 0; break; default: ret = -EPERM; @@ -417,8 +341,6 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) memset(p + txb->pkt_len, 0, gap); } -out: - skcipher_request_free(req); _leave(" = %d [set %x]", ret, y); return ret; } @@ -427,17 +349,13 @@ static int rxkad_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) * decrypt partial encryption on a packet (level 1 security) */ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, - rxrpc_seq_t seq, - struct skcipher_request *req) + rxrpc_seq_t seq) { struct rxkad_level1_hdr *sechdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rxrpc_crypt iv; - struct scatterlist sg[1]; void *data = call->rx_dec_buffer; u32 len = sp->len, data_size, buf; u16 check; - int ret; _enter(""); @@ -445,21 +363,8 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, rxkad_abort_1_short_header); - /* Decrypt the skbuff in-place. TODO: We really want to decrypt - * directly into the target buffer. - */ - sg_init_one(sg, data, len); - - /* start the decryption afresh */ - memset(&iv, 0, sizeof(iv)); - - skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, 8, iv.x); - ret = crypto_skcipher_decrypt(req); - skcipher_request_zero(req); - if (ret < 0) - return ret; + /* Decrypt the first 8-byte block of the packet, using the zero IV. */ + fcrypt_pcbc_decrypt(call->conn->rxkad.cipher, zero_iv, data, data, 1); /* Extract the decrypted packet length */ sechdr = data; @@ -488,18 +393,14 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, * wholly decrypt a packet (level 2 security) */ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, - rxrpc_seq_t seq, - struct skcipher_request *req) + rxrpc_seq_t seq) { const struct rxrpc_key_token *token; struct rxkad_level2_hdr *sechdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rxrpc_crypt iv; - struct scatterlist sg[1]; void *data = call->rx_dec_buffer; u32 len = sp->len, data_size, buf; u16 check; - int ret; _enter(",{%d}", len); @@ -510,26 +411,10 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, /* Don't let the crypto algo see a misaligned length. */ len = round_down(len, 8); - /* Decrypt in place in the call's decryption buffer. TODO: We really - * want to decrypt directly into the target buffer. - */ - sg_init_one(sg, data, len); - /* decrypt from the session key */ token = call->conn->key->payload.data[0]; - memcpy(&iv, token->kad->session_key, sizeof(iv)); - - skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, len, iv.x); - ret = crypto_skcipher_decrypt(req); - skcipher_request_zero(req); - if (ret < 0) { - if (ret == -ENOMEM) - return ret; - return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, - rxkad_abort_2_crypto_unaligned); - } + fcrypt_pcbc_decrypt(call->conn->rxkad.cipher, token->kad->session_key, + data, data, len / FCRYPT_BSIZE); /* Extract the decrypted packet length */ sechdr = data; @@ -562,9 +447,6 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct skcipher_request *req; - struct rxrpc_crypt iv; - struct scatterlist sg; union { __be32 buf[2]; } crypto __aligned(8); @@ -579,27 +461,16 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) if (!call->conn->rxkad.cipher) return 0; - req = rxkad_get_call_crypto(call); - if (!req) - return -ENOMEM; - - /* continue encrypting from where we left off */ - memcpy(&iv, call->conn->rxkad.csum_iv.x, sizeof(iv)); - /* validate the security checksum */ x = (call->cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); x |= seq & 0x3fffffff; crypto.buf[0] = htonl(call->call_id); crypto.buf[1] = htonl(x); - sg_init_one(&sg, crypto.buf, 8); - skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, &sg, &sg, 8, iv.x); - ret = crypto_skcipher_encrypt(req); - skcipher_request_zero(req); - if (ret < 0) - goto out; + /* continue encrypting from where we left off */ + fcrypt_pcbc_encrypt(call->conn->rxkad.cipher, + call->conn->rxkad.csum_iv.x, crypto.buf, crypto.buf, + 1); y = ntohl(crypto.buf[1]); cksum = (y >> 16) & 0xffff; @@ -617,18 +488,16 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) ret = 0; break; case RXRPC_SECURITY_AUTH: - ret = rxkad_verify_packet_1(call, skb, seq, req); + ret = rxkad_verify_packet_1(call, skb, seq); break; case RXRPC_SECURITY_ENCRYPT: - ret = rxkad_verify_packet_2(call, skb, seq, req); + ret = rxkad_verify_packet_2(call, skb, seq); break; default: ret = -ENOANO; break; } - out: - skcipher_request_free(req); return ret; } @@ -712,41 +581,6 @@ static void rxkad_calc_response_checksum(struct rxkad_response *response) response->encrypted.checksum = htonl(csum); } -/* - * encrypt the response packet - */ -static int rxkad_encrypt_response(struct rxrpc_connection *conn, - struct sk_buff *response, - const struct rxkad_key *s2) -{ - struct skcipher_request *req; - struct rxrpc_crypt iv; - struct scatterlist sg[1]; - size_t encsize = sizeof(((struct rxkad_response *)0)->encrypted); - int ret; - - sg_init_table(sg, ARRAY_SIZE(sg)); - ret = skb_to_sgvec(response, sg, - sizeof(struct rxrpc_wire_header) + - offsetof(struct rxkad_response, encrypted), encsize); - if (ret < 0) - return ret; - - req = skcipher_request_alloc(&conn->rxkad.cipher->base, GFP_NOFS); - if (!req) - return -ENOMEM; - - /* continue encrypting from where we left off */ - memcpy(&iv, s2->session_key, sizeof(iv)); - - skcipher_request_set_sync_tfm(req, conn->rxkad.cipher); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, encsize, iv.x); - ret = crypto_skcipher_encrypt(req); - skcipher_request_free(req); - return ret; -} - /* * Validate a challenge packet. */ @@ -846,6 +680,12 @@ int rxkad_insert_response_header(struct rxrpc_connection *conn, rxkad_calc_response_checksum(&h.resp); + /* encrypt the response packet */ + static_assert(sizeof(h.resp.encrypted) % FCRYPT_BSIZE == 0); + fcrypt_pcbc_encrypt(conn->rxkad.cipher, token->kad->session_key, + &h.resp.encrypted, &h.resp.encrypted, + sizeof(h.resp.encrypted) / FCRYPT_BSIZE); + ret = skb_store_bits(response, *offset, &h, sizeof(h)); *offset += sizeof(h); return ret; @@ -890,10 +730,6 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, if (ret < 0) goto error; - ret = rxkad_encrypt_response(conn, response, token->kad); - if (ret < 0) - goto error; - ret = skb_store_bits(response, offset, token->kad->ticket, token->kad->ticket_len); if (ret < 0) @@ -938,6 +774,22 @@ int rxkad_kernel_respond_to_challenge(struct sk_buff *challenge) } EXPORT_SYMBOL(rxkad_kernel_respond_to_challenge); +/* Decrypt data in-place using DES-PCBC. @len must be a multiple of 8. */ +VISIBLE_IF_KUNIT void des_pcbc_decrypt_inplace(const struct des_ctx *key, + __le64 iv, u8 *data, size_t len) +{ + for (size_t i = 0; i < len; i += DES_BLOCK_SIZE) { + __le64 ctext, ptext; + + ctext = get_unaligned((const __le64 *)&data[i]); + des_decrypt(key, (u8 *)&ptext, (const u8 *)&ctext); + ptext ^= iv; + put_unaligned(ptext, (__le64 *)&data[i]); + iv = ptext ^ ctext; + } +} +EXPORT_SYMBOL_IF_KUNIT(des_pcbc_decrypt_inplace); + /* * decrypt the kerberos IV ticket in the response */ @@ -948,13 +800,10 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, struct rxrpc_crypt *_session_key, time64_t *_expiry) { - struct skcipher_request *req; - struct rxrpc_crypt iv, key; - struct scatterlist sg[1]; + struct rxrpc_crypt key; struct in_addr addr; unsigned int life; time64_t issue, now; - int ret; bool little_endian; u8 *p, *q, *name, *end; @@ -964,21 +813,13 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, ASSERT(server_key->payload.data[0] != NULL); - memcpy(&iv, &server_key->payload.data[2], sizeof(iv)); - - req = skcipher_request_alloc(server_key->payload.data[0], GFP_NOFS); - if (!req) - return -ENOMEM; - - sg_init_one(&sg[0], ticket, ticket_len); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, ticket_len, iv.x); - ret = crypto_skcipher_decrypt(req); - skcipher_request_free(req); - if (ret < 0) + if (ticket_len % DES_BLOCK_SIZE != 0) return rxrpc_abort_conn(conn, skb, RXKADBADTICKET, -EPROTO, rxkad_abort_resp_tkt_short); - + des_pcbc_decrypt_inplace( + server_key->payload.data[0], + get_unaligned((const __le64 *)&server_key->payload.data[2]), + ticket, ticket_len); p = ticket; end = p + ticket_len; @@ -1072,39 +913,22 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, /* * decrypt the response packet */ -static int rxkad_decrypt_response(struct rxrpc_connection *conn, - struct rxkad_response *resp, - const struct rxrpc_crypt *session_key) +static void rxkad_decrypt_response(struct rxrpc_connection *conn, + struct rxkad_response *resp, + const struct rxrpc_crypt *session_key) { - struct skcipher_request *req = rxkad_ci_req; - struct scatterlist sg[1]; - struct rxrpc_crypt iv; - int ret; + struct fcrypt_key cipher; _enter(",,%08x%08x", ntohl(session_key->n[0]), ntohl(session_key->n[1])); - mutex_lock(&rxkad_ci_mutex); - ret = crypto_sync_skcipher_setkey(rxkad_ci, session_key->x, - sizeof(*session_key)); - if (ret < 0) - goto unlock; - - memcpy(&iv, session_key, sizeof(iv)); - - sg_init_table(sg, 1); - sg_set_buf(sg, &resp->encrypted, sizeof(resp->encrypted)); - skcipher_request_set_sync_tfm(req, rxkad_ci); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, sizeof(resp->encrypted), iv.x); - ret = crypto_skcipher_decrypt(req); - skcipher_request_zero(req); - -unlock: - mutex_unlock(&rxkad_ci_mutex); + fcrypt_preparekey(&cipher, session_key->x); + static_assert(sizeof(resp->encrypted) % FCRYPT_BSIZE == 0); + fcrypt_pcbc_decrypt(&cipher, session_key->x, &resp->encrypted, + &resp->encrypted, + sizeof(resp->encrypted) / FCRYPT_BSIZE); _leave(""); - return ret; } /* @@ -1191,9 +1015,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, /* use the session key from inside the ticket to decrypt the * response */ - ret = rxkad_decrypt_response(conn, response, &session_key); - if (ret < 0) - goto error; + rxkad_decrypt_response(conn, response, &session_key); if (ntohl(response->encrypted.epoch) != conn->proto.epoch || ntohl(response->encrypted.cid) != conn->proto.cid || @@ -1270,8 +1092,8 @@ static void rxkad_clear(struct rxrpc_connection *conn) { _enter(""); - if (conn->rxkad.cipher) - crypto_free_sync_skcipher(conn->rxkad.cipher); + kfree_sensitive(conn->rxkad.cipher); + conn->rxkad.cipher = NULL; } /* @@ -1279,26 +1101,11 @@ static void rxkad_clear(struct rxrpc_connection *conn) */ static int rxkad_init(void) { - struct crypto_sync_skcipher *tfm; - struct skcipher_request *req; - - /* pin the cipher we need so that the crypto layer doesn't invoke - * keventd to go get it */ - tfm = crypto_alloc_sync_skcipher("pcbc(fcrypt)", 0, 0); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - req = skcipher_request_alloc(&tfm->base, GFP_KERNEL); - if (!req) - goto nomem_tfm; - - rxkad_ci_req = req; - rxkad_ci = tfm; + if (fips_enabled) { + pr_warn("rxkad support is disabled due to FIPS\n"); + return -ENOENT; + } return 0; - -nomem_tfm: - crypto_free_sync_skcipher(tfm); - return -ENOMEM; } /* @@ -1306,8 +1113,6 @@ static int rxkad_init(void) */ static void rxkad_exit(void) { - crypto_free_sync_skcipher(rxkad_ci); - skcipher_request_free(rxkad_ci_req); } /* diff --git a/net/rxrpc/server_key.c b/net/rxrpc/server_key.c index 27491f1e1273..3efe104b1930 100644 --- a/net/rxrpc/server_key.c +++ b/net/rxrpc/server_key.c @@ -10,7 +10,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include diff --git a/net/rxrpc/tests/Makefile b/net/rxrpc/tests/Makefile new file mode 100644 index 000000000000..4f51008800b4 --- /dev/null +++ b/net/rxrpc/tests/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_AF_RXRPC_KUNIT_TEST) += rxrpc_kunit.o diff --git a/net/rxrpc/tests/rxrpc_kunit.c b/net/rxrpc/tests/rxrpc_kunit.c new file mode 100644 index 000000000000..95fd05a07d17 --- /dev/null +++ b/net/rxrpc/tests/rxrpc_kunit.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Unit tests for RxRPC crypto functions + * + * Copyright 2026 Google LLC + */ +#include "../ar-internal.h" +#include +#include + +struct fcrypt_pcbc_testvec { + u8 key[FCRYPT_BSIZE]; + u8 iv[FCRYPT_BSIZE]; + const u8 *ptext; /* plaintext */ + const u8 *ctext; /* ciphertext */ + size_t nblocks; /* length of ptext and ctext in blocks */ +}; + +/* FCrypt-PCBC test vectors */ +static const struct fcrypt_pcbc_testvec fcrypt_pcbc_testvecs[] = { + { + /* http://www.openafs.org/pipermail/openafs-devel/2000-December/005320.html */ + .key = "\x00\x00\x00\x00\x00\x00\x00\x00", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ptext = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ctext = "\x0E\x09\x00\xC7\x3E\xF7\xED\x41", + .nblocks = 1, + }, + { + .key = "\x11\x44\x77\xAA\xDD\x00\x33\x66", + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00", + .ptext = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0", + .ctext = "\xD8\xED\x78\x74\x77\xEC\x06\x80", + .nblocks = 1, + }, + { + /* From Arla */ + .key = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .iv = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .ptext = "The quick brown fox jumps over the lazy dogs.\0\0", + .ctext = "\x00\xf0\x0e\x11\x75\xe6\x23\x82" + "\xee\xac\x98\x62\x44\x51\xe4\x84" + "\xc3\x59\xd8\xaa\x64\x60\xae\xf7" + "\xd2\xd9\x13\x79\x72\xa3\x45\x03" + "\x23\xb5\x62\xd7\x0c\xf5\x27\xd1" + "\xf8\x91\x3c\xac\x44\x22\x92\xef", + .nblocks = 6, + }, + { + .key = "\xfe\xdc\xba\x98\x76\x54\x32\x10", + .iv = "\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87", + .ptext = "The quick brown fox jumps over the lazy dogs.\0\0", + .ctext = "\xca\x90\xf5\x9d\xcb\xd4\xd2\x3c" + "\x01\x88\x7f\x3e\x31\x6e\x62\x9d" + "\xd8\xe0\x57\xa3\x06\x3a\x42\x58" + "\x2a\x28\xfe\x72\x52\x2f\xdd\xe0" + "\x19\x89\x09\x1c\x2a\x8e\x8c\x94" + "\xfc\xc7\x68\xe4\x88\xaa\xde\x0f", + .nblocks = 6, + } +}; + +static void test_fcrypt_pcbc(struct kunit *test) +{ + u8 data[48]; + + for (size_t i = 0; i < ARRAY_SIZE(fcrypt_pcbc_testvecs); i++) { + const struct fcrypt_pcbc_testvec *tv = &fcrypt_pcbc_testvecs[i]; + const size_t nblocks = tv->nblocks; + const size_t len = nblocks * FCRYPT_BSIZE; + struct fcrypt_key key; + + KUNIT_ASSERT_GE(test, sizeof(data), len); + + fcrypt_preparekey(&key, tv->key); + + /* out-of-place encryption */ + fcrypt_pcbc_encrypt(&key, tv->iv, tv->ptext, data, nblocks); + KUNIT_ASSERT_MEMEQ(test, tv->ctext, data, len); + + /* in-place encryption */ + memcpy(data, tv->ptext, len); + fcrypt_pcbc_encrypt(&key, tv->iv, data, data, nblocks); + KUNIT_ASSERT_MEMEQ(test, tv->ctext, data, len); + + /* out-of-place decryption */ + fcrypt_pcbc_decrypt(&key, tv->iv, tv->ctext, data, nblocks); + KUNIT_ASSERT_MEMEQ(test, tv->ptext, data, len); + + /* in-place decryption */ + memcpy(data, tv->ctext, len); + fcrypt_pcbc_decrypt(&key, tv->iv, data, data, nblocks); + KUNIT_ASSERT_MEMEQ(test, tv->ptext, data, len); + } +} + +static void test_des_pcbc(struct kunit *test) +{ + /* This was generated from the original pcbc(des) crypto API code. */ + static const u8 expected_ptext[24] = + "\xc8\xe2\x3c\xdf\x80\x61\x8a\xad\xa5\x52\xb4\x20" + "\x74\x32\x1f\xe4\x2c\x15\x7d\x21\x57\xda\x3f\x31"; + u8 key[8]; + union { + __le64 w; + u8 b[8]; + } iv; + u8 data[24]; + struct des_ctx ctx; + int err; + + for (int i = 0; i < 8; i++) { + key[i] = i; + iv.b[i] = 255 - i; + } + for (int i = 0; i < sizeof(data); i++) + data[i] = i; + + err = des_expand_key(&ctx, key, sizeof(key)); + KUNIT_ASSERT_EQ(test, 0, err); + + des_pcbc_decrypt_inplace(&ctx, iv.w, data, sizeof(data)); + KUNIT_ASSERT_MEMEQ(test, expected_ptext, data, sizeof(data)); +} + +static struct kunit_case rxrpc_test_cases[] = { + KUNIT_CASE(test_fcrypt_pcbc), + KUNIT_CASE(test_des_pcbc), + {}, +}; + +static struct kunit_suite rxrpc_test_suite = { + .name = "rxrpc", + .test_cases = rxrpc_test_cases, +}; +kunit_test_suite(rxrpc_test_suite); + +MODULE_DESCRIPTION("Unit tests for RxRPC crypto functions"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +MODULE_LICENSE("GPL"); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 04ea11c90e03..b68be143a067 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1573,7 +1573,7 @@ void tcf_action_update_stats(struct tc_action *a, u64 bytes, u64 packets, if (a->cpu_bstats) { _bstats_update(this_cpu_ptr(a->cpu_bstats), bytes, packets); - this_cpu_ptr(a->cpu_qstats)->drops += drops; + this_cpu_add(a->cpu_qstats->drops, drops); if (hw) _bstats_update(this_cpu_ptr(a->cpu_bstats_hw), diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index c2b5bc19e091..58a074651176 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -76,7 +76,7 @@ TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb, break; case TC_ACT_SHOT: action = filter_res; - qstats_drop_inc(this_cpu_ptr(prog->common.cpu_qstats)); + qstats_cpu_drop_inc(prog->common.cpu_qstats); break; case TC_ACT_UNSPEC: action = prog->tcf_action; diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index a9e4635d899e..078d3a27130b 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -259,7 +259,9 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl, const struct iphdr *iph; u16 ul; - if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) + if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & + (SKB_GSO_UDP | SKB_GSO_UDP_L4 | + SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) return 1; /* @@ -315,7 +317,9 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl, const struct ipv6hdr *ip6h; u16 ul; - if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) + if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & + (SKB_GSO_UDP | SKB_GSO_UDP_L4 | + SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) return 1; /* diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index d5e8a91bb4eb..065228026c58 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -727,7 +727,7 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a, tlv_data = ife_decode(skb, &metalen); if (unlikely(!tlv_data)) { - qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); + qstats_cpu_drop_inc(ife->common.cpu_qstats); return TC_ACT_SHOT; } @@ -740,7 +740,7 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a, curr_data = ife_tlv_meta_decode(tlv_data, ifehdr_end, &mtype, &dlen, NULL); if (!curr_data) { - qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); + qstats_cpu_drop_inc(ife->common.cpu_qstats); return TC_ACT_SHOT; } @@ -750,12 +750,12 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a, */ pr_info_ratelimited("Unknown metaid %d dlen %d\n", mtype, dlen); - qstats_overlimit_inc(this_cpu_ptr(ife->common.cpu_qstats)); + qstats_cpu_overlimit_inc(ife->common.cpu_qstats); } } if (WARN_ON(tlv_data != ifehdr_end)) { - qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); + qstats_cpu_drop_inc(ife->common.cpu_qstats); return TC_ACT_SHOT; } @@ -814,14 +814,14 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a, /* abuse overlimits to count when we allow packet * with no metadata */ - qstats_overlimit_inc(this_cpu_ptr(ife->common.cpu_qstats)); + qstats_cpu_overlimit_inc(ife->common.cpu_qstats); return action; } /* could be stupid policy setup or mtu config * so lets be conservative.. */ if ((action == TC_ACT_SHOT) || exceed_mtu) { drop: - qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); + qstats_cpu_drop_inc(ife->common.cpu_qstats); return TC_ACT_SHOT; } diff --git a/net/sched/act_mpls.c b/net/sched/act_mpls.c index 1abfaf9d99f1..4ea8b2e08c3a 100644 --- a/net/sched/act_mpls.c +++ b/net/sched/act_mpls.c @@ -123,7 +123,7 @@ TC_INDIRECT_SCOPE int tcf_mpls_act(struct sk_buff *skb, return p->action; drop: - qstats_drop_inc(this_cpu_ptr(m->common.cpu_qstats)); + qstats_cpu_drop_inc(m->common.cpu_qstats); return TC_ACT_SHOT; } diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index bd3b1da3cd63..0d652dea4a69 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -331,6 +332,9 @@ static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int head if (!iph) goto out; + if (iph->ihl < 5 || iph->protocol != header_type || + (iph->frag_off & htons(IP_OFFSET))) + goto out; *hoffset = noff + iph->ihl * 4; ret = 0; break; diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 12ea9e5a6005..b16468a98c55 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -307,10 +307,10 @@ TC_INDIRECT_SCOPE int tcf_police_act(struct sk_buff *skb, } inc_overlimits: - qstats_overlimit_inc(this_cpu_ptr(police->common.cpu_qstats)); + qstats_cpu_overlimit_inc(police->common.cpu_qstats); inc_drops: if (ret == TC_ACT_SHOT) - qstats_drop_inc(this_cpu_ptr(police->common.cpu_qstats)); + qstats_cpu_drop_inc(police->common.cpu_qstats); end: return ret; } diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index a778cdba9258..bfec6b668410 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -86,7 +86,7 @@ TC_INDIRECT_SCOPE int tcf_skbedit_act(struct sk_buff *skb, return params->action; err: - qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats)); + qstats_cpu_drop_inc(d->common.cpu_qstats); return TC_ACT_SHOT; } diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c index 23ca46138f04..a464b0a3c1b8 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c @@ -87,7 +87,7 @@ TC_INDIRECT_SCOPE int tcf_skbmod_act(struct sk_buff *skb, return p->action; drop: - qstats_overlimit_inc(this_cpu_ptr(d->common.cpu_qstats)); + qstats_cpu_overlimit_inc(d->common.cpu_qstats); return TC_ACT_SHOT; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 9a346b6221b3..001d8c4ebfed 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -142,7 +142,8 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog) static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, struct cls_bpf_prog *oldprog, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, + bool is_rollback) { struct tcf_block *block = tp->chain->block; struct tc_cls_bpf_offload cls_bpf = {}; @@ -177,7 +178,8 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, &oldprog->in_hw_count, true); if (prog && err) { - cls_bpf_offload_cmd(tp, oldprog, prog, extack); + if (!is_rollback) + cls_bpf_offload_cmd(tp, oldprog, prog, extack, true); return err; } @@ -208,7 +210,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog, if (!prog && !oldprog) return 0; - return cls_bpf_offload_cmd(tp, prog, oldprog, extack); + return cls_bpf_offload_cmd(tp, prog, oldprog, extack, false); } static void cls_bpf_stop_offload(struct tcf_proto *tp, @@ -217,7 +219,7 @@ static void cls_bpf_stop_offload(struct tcf_proto *tp, { int err; - err = cls_bpf_offload_cmd(tp, NULL, prog, extack); + err = cls_bpf_offload_cmd(tp, NULL, prog, extack, false); if (err) pr_err("Stopping hardware offload failed: %d\n", err); } diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index ab364e4e4686..356c68ebc389 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -57,11 +58,15 @@ struct flow_filter { struct rcu_work rwork; }; +static siphash_aligned_key_t flow_keys_secret __read_mostly; + static inline u32 addr_fold(void *addr) { - unsigned long a = (unsigned long)addr; - - return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0); +#ifdef CONFIG_64BIT + return (u32)siphash_1u64((u64)addr, &flow_keys_secret); +#else + return (u32)siphash_1u32((u32)addr, &flow_keys_secret); +#endif } static u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow) @@ -596,6 +601,7 @@ static int flow_init(struct tcf_proto *tp) return -ENOBUFS; INIT_LIST_HEAD(&head->filters); rcu_assign_pointer(tp->root, head); + net_get_random_once(&flow_keys_secret, sizeof(flow_keys_secret)); return 0; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index ed869a5ffc73..8a3236456db4 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -805,8 +805,8 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len) cl = cops->find(sch, parentid); cops->qlen_notify(sch, cl); } - sch->q.qlen -= n; - sch->qstats.backlog -= len; + WRITE_ONCE(sch->q.qlen, sch->q.qlen - n); + qstats_backlog_sub(sch, len); __qdisc_qstats_drop(sch, drops); } rcu_read_unlock(); @@ -859,7 +859,7 @@ void qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch, any_qdisc_is_offloaded |= old && old->flags & TCQ_F_OFFLOADED; if (any_qdisc_is_offloaded) - NL_SET_ERR_MSG(extack, "Offloading graft operation failed."); + NL_SET_ERR_MSG_WEAK(extack, "Offloading graft operation failed."); } EXPORT_SYMBOL(qdisc_offload_graft_helper); @@ -976,16 +976,20 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, out_nlmsg_trim: nla_put_failure: nlmsg_trim(skb, b); - return -1; + return -EMSGSIZE; } -static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) +static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible, + const struct tcmsg *tcm) { if (q->flags & TCQ_F_BUILTIN) return true; if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible) return true; - + if (tcm) { + if (tcm->tcm_handle && tcm->tcm_handle != q->handle) + return true; + } return false; } @@ -1000,7 +1004,7 @@ static int qdisc_get_notify(struct net *net, struct sk_buff *oskb, if (!skb) return -ENOBUFS; - if (!tc_qdisc_dump_ignore(q, false)) { + if (!tc_qdisc_dump_ignore(q, false, NULL)) { if (tc_fill_qdisc(skb, q, clid, portid, n->nlmsg_seq, 0, RTM_NEWQDISC, extack) < 0) goto err_out; @@ -1030,12 +1034,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, if (!skb) return -ENOBUFS; - if (old && !tc_qdisc_dump_ignore(old, false)) { + if (old && !tc_qdisc_dump_ignore(old, false, NULL)) { if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, 0, RTM_DELQDISC, extack) < 0) goto err_out; } - if (new && !tc_qdisc_dump_ignore(new, false)) { + if (new && !tc_qdisc_dump_ignore(new, false, NULL)) { if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC, extack) < 0) goto err_out; @@ -1825,22 +1829,27 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, int *q_idx_p, int s_q_idx, bool recur, bool dump_invisible) { + const struct nlmsghdr *nlh = cb->nlh; int ret = 0, q_idx = *q_idx_p; + const struct tcmsg *tcm; struct Qdisc *q; int b; if (!root) return 0; + tcm = nlmsg_data(nlh); q = root; if (q_idx < s_q_idx) { q_idx++; } else { - if (!tc_qdisc_dump_ignore(q, dump_invisible) && - tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWQDISC, NULL) <= 0) - goto done; + if (!tc_qdisc_dump_ignore(q, dump_invisible, tcm)) + ret = tc_fill_qdisc(skb, q, q->parent, + NETLINK_CB(cb->skb).portid, + nlh->nlmsg_seq, NLM_F_MULTI, + RTM_NEWQDISC, NULL); + if (ret < 0) + goto out; q_idx++; } @@ -1858,79 +1867,81 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, q_idx++; continue; } - if (!tc_qdisc_dump_ignore(q, dump_invisible) && - tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - RTM_NEWQDISC, NULL) <= 0) - goto done; + if (!tc_qdisc_dump_ignore(q, dump_invisible, tcm)) + ret = tc_fill_qdisc(skb, q, q->parent, + NETLINK_CB(cb->skb).portid, + nlh->nlmsg_seq, NLM_F_MULTI, + RTM_NEWQDISC, NULL); + if (ret < 0) + goto out; q_idx++; } out: *q_idx_p = q_idx; return ret; -done: - ret = -1; - goto out; } static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) { - struct net *net = sock_net(skb->sk); - int idx, q_idx; - int s_idx, s_q_idx; - struct net_device *dev; const struct nlmsghdr *nlh = cb->nlh; + struct net *net = sock_net(skb->sk); struct nlattr *tca[TCA_MAX + 1]; + struct { + unsigned long ifindex; + int q_idx; + } *ctx = (void *)cb->ctx; + const struct tcmsg *tcm; + struct net_device *dev; + int s_q_idx, q_idx; int err; - s_idx = cb->args[0]; - s_q_idx = q_idx = cb->args[1]; - - idx = 0; ASSERT_RTNL(); err = nlmsg_parse_deprecated(nlh, sizeof(struct tcmsg), tca, TCA_MAX, rtm_tca_policy, cb->extack); if (err < 0) return err; + tcm = nlmsg_data(nlh); + if (tcm->tcm_ifindex && !ctx->ifindex) + ctx->ifindex = tcm->tcm_ifindex; - for_each_netdev(net, dev) { + s_q_idx = ctx->q_idx; + + for_each_netdev_dump(net, dev, ctx->ifindex) { struct netdev_queue *dev_queue; + struct Qdisc *q; + + if (tcm->tcm_ifindex && ctx->ifindex != tcm->tcm_ifindex) + break; - if (idx < s_idx) - goto cont; - if (idx > s_idx) - s_q_idx = 0; q_idx = 0; netdev_lock_ops(dev); - if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc), - skb, cb, &q_idx, s_q_idx, - true, tca[TCA_DUMP_INVISIBLE]) < 0) { - netdev_unlock_ops(dev); - goto done; - } + q = rtnl_dereference(dev->qdisc); + err = tc_dump_qdisc_root(q, skb, cb, &q_idx, s_q_idx, + true, tca[TCA_DUMP_INVISIBLE]); + if (err < 0) + goto error_unlock; dev_queue = dev_ingress_queue(dev); - if (dev_queue && - tc_dump_qdisc_root(rtnl_dereference(dev_queue->qdisc_sleeping), - skb, cb, &q_idx, s_q_idx, false, - tca[TCA_DUMP_INVISIBLE]) < 0) { - netdev_unlock_ops(dev); - goto done; + if (dev_queue) { + q = rtnl_dereference(dev_queue->qdisc_sleeping); + err = tc_dump_qdisc_root(q, skb, cb, &q_idx, s_q_idx, + false, tca[TCA_DUMP_INVISIBLE]); + if (err < 0) + goto error_unlock; } netdev_unlock_ops(dev); - -cont: - idx++; + s_q_idx = 0; } - -done: - cb->args[0] = idx; - cb->args[1] = q_idx; - return skb->len; + +error_unlock: + netdev_unlock_ops(dev); + ctx->q_idx = q_idx; + + return err; } @@ -1987,15 +1998,16 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, out_nlmsg_trim: nla_put_failure: nlmsg_trim(skb, b); - return -1; + return -EMSGSIZE; } static int tclass_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct Qdisc *q, unsigned long cl, int event, struct netlink_ext_ack *extack) { - struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + struct sk_buff *skb; + int ret; if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC)) return 0; @@ -2004,9 +2016,10 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb, if (!skb) return -ENOBUFS; - if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack) < 0) { + ret = tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack); + if (ret < 0) { kfree_skb(skb); - return -EINVAL; + return ret; } return rtnetlink_send(skb, net, portid, RTNLGRP_TC, @@ -2017,17 +2030,19 @@ static int tclass_get_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct Qdisc *q, unsigned long cl, struct netlink_ext_ack *extack) { - struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + struct sk_buff *skb; + int ret; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; - if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, RTM_NEWTCLASS, - extack) < 0) { + ret = tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, + RTM_NEWTCLASS, extack); + if (ret < 0) { kfree_skb(skb); - return -EINVAL; + return ret; } return rtnetlink_send(skb, net, portid, RTNLGRP_TC, @@ -2041,7 +2056,7 @@ static int tclass_del_notify(struct net *net, struct netlink_ext_ack *extack) { u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - struct sk_buff *skb; + struct sk_buff *skb = NULL; int err = 0; if (!cops->delete) @@ -2052,13 +2067,12 @@ static int tclass_del_notify(struct net *net, if (!skb) return -ENOBUFS; - if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, - RTM_DELTCLASS, extack) < 0) { + err = tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, + RTM_DELTCLASS, extack); + if (err < 0) { kfree_skb(skb); - return -EINVAL; + return err; } - } else { - skb = NULL; } err = cops->delete(q, cl, extack); @@ -2334,7 +2348,7 @@ static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, { struct qdisc_dump_args arg; - if (tc_qdisc_dump_ignore(q, false) || + if (tc_qdisc_dump_ignore(q, false, NULL) || *t_p < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index 5862933be8d7..a3c185505afc 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1600,10 +1600,10 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free) b->unresponsive_flow_count + 1); len = qdisc_pkt_len(skb); - q->buffer_used -= skb->truesize; + qstats_backlog_sub(sch, len); + q->buffer_used -= skb->truesize; WRITE_ONCE(b->tin_backlog, b->tin_backlog - len); WRITE_ONCE(b->backlogs[idx], b->backlogs[idx] - len); - sch->qstats.backlog -= len; WRITE_ONCE(flow->dropped, flow->dropped + 1); WRITE_ONCE(b->tin_dropped, b->tin_dropped + 1); @@ -1612,7 +1612,7 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free) cake_advance_shaper(q, b, skb, now, true); qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_OVERLIMIT); - sch->q.qlen--; + qdisc_qlen_dec(sch); cake_heapify(q, 0); @@ -1822,7 +1822,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, segs); flow_queue_add(flow, segs); - sch->q.qlen++; + qdisc_qlen_inc(sch); numsegs++; slen += segs->len; q->buffer_used += segs->truesize; @@ -1830,7 +1830,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, } /* stats */ - sch->qstats.backlog += slen; + qstats_backlog_add(sch, slen); q->avg_window_bytes += slen; WRITE_ONCE(b->bytes, b->bytes + slen); WRITE_ONCE(b->tin_backlog, b->tin_backlog + slen); @@ -1851,7 +1851,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (ack) { WRITE_ONCE(b->ack_drops, b->ack_drops + 1); - sch->qstats.drops++; + qdisc_qstats_drop(sch); ack_pkt_len = qdisc_pkt_len(ack); WRITE_ONCE(b->bytes, b->bytes + ack_pkt_len); q->buffer_used += skb->truesize - ack->truesize; @@ -1861,13 +1861,13 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, qdisc_tree_reduce_backlog(sch, 1, ack_pkt_len); consume_skb(ack); } else { - sch->q.qlen++; + qdisc_qlen_inc(sch); q->buffer_used += skb->truesize; } /* stats */ WRITE_ONCE(b->packets, b->packets + 1); - sch->qstats.backlog += len - ack_pkt_len; + qstats_backlog_add(sch, len - ack_pkt_len); q->avg_window_bytes += len - ack_pkt_len; WRITE_ONCE(b->bytes, b->bytes + len - ack_pkt_len); WRITE_ONCE(b->tin_backlog, b->tin_backlog + len - ack_pkt_len); @@ -1985,9 +1985,9 @@ static struct sk_buff *cake_dequeue_one(struct Qdisc *sch) len = qdisc_pkt_len(skb); WRITE_ONCE(b->backlogs[q->cur_flow], b->backlogs[q->cur_flow] - len); WRITE_ONCE(b->tin_backlog, b->tin_backlog - len); - sch->qstats.backlog -= len; + qstats_backlog_sub(sch, len); q->buffer_used -= skb->truesize; - sch->q.qlen--; + qdisc_qlen_dec(sch); if (q->overflow_timeout) cake_heapify(q, b->overflow_idx[q->cur_flow]); diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index 0f953bd46b58..1c93469c56e3 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -96,8 +96,8 @@ static int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (err != NET_XMIT_SUCCESS) return err; - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -168,7 +168,7 @@ static struct sk_buff *cbs_child_dequeue(struct Qdisc *sch, struct Qdisc *child) qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } @@ -327,7 +327,7 @@ static void cbs_set_port_rate(struct net_device *dev, struct cbs_sched_data *q) s64 port_rate; int err; - err = __ethtool_get_link_ksettings(dev, &ecmd); + err = netif_get_link_ksettings(dev, &ecmd); if (err < 0) goto skip; @@ -340,7 +340,7 @@ static void cbs_set_port_rate(struct net_device *dev, struct cbs_sched_data *q) atomic64_set(&q->port_rate, port_rate); netdev_dbg(dev, "cbs: set %s's port_rate to: %lld, linkspeed: %d\n", dev->name, (long long)atomic64_read(&q->port_rate), - ecmd.base.speed); + speed); } static int cbs_dev_notifier(struct notifier_block *nb, unsigned long event, diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 2875bcdb18a4..73d3e673dc7b 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -123,7 +123,7 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx, if (idx == q->tail) choke_zap_tail_holes(q); - --sch->q.qlen; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb)); qdisc_drop(skb, sch, to_free); @@ -271,7 +271,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (sch->q.qlen < q->limit) { q->tab[q->tail] = skb; q->tail = (q->tail + 1) & q->tab_mask; - ++sch->q.qlen; + qdisc_qlen_inc(sch); qdisc_qstats_backlog_inc(sch, skb); return NET_XMIT_SUCCESS; } @@ -298,7 +298,7 @@ static struct sk_buff *choke_dequeue(struct Qdisc *sch) skb = q->tab[q->head]; q->tab[q->head] = NULL; choke_zap_head_holes(q); - --sch->q.qlen; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); @@ -396,7 +396,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt, } dropped += qdisc_pkt_len(skb); qdisc_qstats_backlog_dec(sch, skb); - --sch->q.qlen; + qdisc_qlen_dec(sch); rtnl_qdisc_drop(skb, sch); } qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped); diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c index 317aae0ec7bd..cacf5244958e 100644 --- a/net/sched/sch_codel.c +++ b/net/sched/sch_codel.c @@ -42,7 +42,7 @@ static struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx) struct sk_buff *skb = __qdisc_dequeue_head(&sch->q); if (skb) { - sch->qstats.backlog -= qdisc_pkt_len(skb); + qstats_backlog_sub(sch, qdisc_pkt_len(skb)); prefetch(&skb->end); /* we'll need skb_shinfo() */ } return skb; @@ -56,7 +56,7 @@ static void drop_func(struct sk_buff *skb, void *ctx) qdisc_qstats_drop(sch); } -static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) +static struct sk_buff *__codel_qdisc_dequeue(struct Qdisc *sch) { struct codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -65,13 +65,51 @@ static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) &q->stats, qdisc_pkt_len, codel_get_enqueue_time, drop_func, dequeue_func); + if (skb) + qdisc_bstats_update(sch, skb); + return skb; +} + +static void codel_dequeue_drop(struct Qdisc *sch) +{ + struct codel_sched_data *q = qdisc_priv(sch); + if (q->stats.drop_count) { - qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); + qdisc_tree_reduce_backlog(sch, q->stats.drop_count, + q->stats.drop_len); q->stats.drop_count = 0; q->stats.drop_len = 0; } - if (skb) - qdisc_bstats_update(sch, skb); +} + +static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __codel_qdisc_dequeue(sch); + + codel_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *codel_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __codel_qdisc_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + codel_dequeue_drop(sch); + } + return skb; } @@ -257,7 +295,7 @@ static struct Qdisc_ops codel_qdisc_ops __read_mostly = { .enqueue = codel_qdisc_enqueue, .dequeue = codel_qdisc_dequeue, - .peek = qdisc_peek_dequeued, + .peek = codel_peek, .init = codel_init, .reset = codel_reset, .change = codel_change, diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 01335a49e091..020657f959b5 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -97,10 +97,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, } } - sch_tree_lock(sch); if (tb[TCA_DRR_QUANTUM]) - cl->quantum = quantum; - sch_tree_unlock(sch); + WRITE_ONCE(cl->quantum, quantum); return 0; } @@ -251,7 +249,7 @@ static int drr_dump_class(struct Qdisc *sch, unsigned long arg, nest = nla_nest_start_noflag(skb, TCA_OPTIONS); if (nest == NULL) goto nla_put_failure; - if (nla_put_u32(skb, TCA_DRR_QUANTUM, cl->quantum)) + if (nla_put_u32(skb, TCA_DRR_QUANTUM, READ_ONCE(cl->quantum))) goto nla_put_failure; return nla_nest_end(skb, nest); @@ -270,7 +268,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg, memset(&xstats, 0, sizeof(xstats)); if (qlen) - xstats.deficit = cl->deficit; + xstats.deficit = READ_ONCE(cl->deficit); if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || @@ -362,11 +360,11 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!cl_is_active(cl)) { list_add_tail(&cl->alist, &q->active); - cl->deficit = cl->quantum; + WRITE_ONCE(cl->deficit, READ_ONCE(cl->quantum)); } - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); return err; } @@ -389,7 +387,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) len = qdisc_pkt_len(skb); if (len <= cl->deficit) { - cl->deficit -= len; + WRITE_ONCE(cl->deficit, cl->deficit - len); skb = qdisc_dequeue_peeked(cl->qdisc); if (unlikely(skb == NULL)) goto out; @@ -399,11 +397,11 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) bstats_update(&cl->bstats, skb); qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } - cl->deficit += cl->quantum; + WRITE_ONCE(cl->deficit, cl->deficit + READ_ONCE(cl->quantum)); list_move_tail(&cl->alist, &q->active); } out: diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index a22489c14458..d7c3254ef800 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -190,7 +190,7 @@ static bool skb_apply_step(struct sk_buff *skb, struct dualpi2_sched_data *q) static bool dualpi2_mark(struct dualpi2_sched_data *q, struct sk_buff *skb) { if (INET_ECN_set_ce(skb)) { - q->ecn_mark++; + WRITE_ONCE(q->ecn_mark, q->ecn_mark + 1); return true; } return false; @@ -198,7 +198,7 @@ static bool dualpi2_mark(struct dualpi2_sched_data *q, struct sk_buff *skb) static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q) { - q->c_protection_credit = q->c_protection_init; + WRITE_ONCE(q->c_protection_credit, q->c_protection_init); } /* This computes the initial credit value and WRR weight for the L queue (wl) @@ -403,28 +403,28 @@ static int dualpi2_enqueue_skb(struct sk_buff *skb, struct Qdisc *sch, cb = dualpi2_skb_cb(skb); cb->ts = ktime_get_ns(); - q->memory_used += skb->truesize; + WRITE_ONCE(q->memory_used, q->memory_used + skb->truesize); if (q->memory_used > q->max_memory_used) - q->max_memory_used = q->memory_used; + WRITE_ONCE(q->max_memory_used, q->memory_used); if (qdisc_qlen(sch) > q->maxq) - q->maxq = qdisc_qlen(sch); + WRITE_ONCE(q->maxq, qdisc_qlen(sch)); if (skb_in_l_queue(skb)) { /* Apply step thresh if skb is L4S && L-queue len >= min_qlen */ dualpi2_skb_cb(skb)->apply_step = skb_apply_step(skb, q); /* Keep the overall qdisc stats consistent */ - ++sch->q.qlen; + qdisc_qlen_inc(sch); qdisc_qstats_backlog_inc(sch, skb); - ++q->packets_in_l; + WRITE_ONCE(q->packets_in_l, q->packets_in_l + 1); if (!q->l_head_ts) - q->l_head_ts = cb->ts; + WRITE_ONCE(q->l_head_ts, cb->ts); return qdisc_enqueue_tail(skb, q->l_queue); } - ++q->packets_in_c; + WRITE_ONCE(q->packets_in_c, q->packets_in_c + 1); if (!q->c_head_ts) - q->c_head_ts = cb->ts; + WRITE_ONCE(q->c_head_ts, cb->ts); return qdisc_enqueue_tail(skb, sch); } @@ -530,18 +530,17 @@ static struct sk_buff *dequeue_packet(struct Qdisc *sch, qdisc_qstats_backlog_dec(q->l_queue, skb); /* Keep the global queue size consistent */ - --sch->q.qlen; - q->memory_used -= skb->truesize; + qdisc_qlen_dec(sch); } else if (c_len) { skb = __qdisc_dequeue_head(&sch->q); WRITE_ONCE(q->c_head_ts, head_enqueue_time(sch)); if (qdisc_qlen(q->l_queue)) *credit_change = ~((s32)q->c_protection_wl) + 1; - q->memory_used -= skb->truesize; } else { dualpi2_reset_c_protection(q); return NULL; } + WRITE_ONCE(q->memory_used, q->memory_used - skb->truesize); *credit_change *= qdisc_pkt_len(skb); qdisc_qstats_backlog_dec(sch, skb); return skb; @@ -564,7 +563,7 @@ static int do_step_aqm(struct dualpi2_sched_data *q, struct sk_buff *skb, } if (dualpi2_mark(q, skb)) - ++q->step_marks; + WRITE_ONCE(q->step_marks, q->step_marks + 1); } qdisc_bstats_update(q->l_queue, skb); return 0; @@ -579,7 +578,7 @@ static void drop_and_retry(struct dualpi2_sched_data *q, struct sk_buff *skb, qdisc_qstats_drop(sch); } -static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) +static struct sk_buff *__dualpi2_qdisc_dequeue(struct Qdisc *sch) { struct dualpi2_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -600,17 +599,55 @@ static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) continue; } - q->c_protection_credit += credit_change; + WRITE_ONCE(q->c_protection_credit, + q->c_protection_credit + credit_change); qdisc_bstats_update(sch, skb); break; } + return skb; +} + +static void dualpi2_dequeue_drop(struct Qdisc *sch) +{ + struct dualpi2_sched_data *q = qdisc_priv(sch); + if (q->deferred_drops_cnt) { qdisc_tree_reduce_backlog(sch, q->deferred_drops_cnt, q->deferred_drops_len); q->deferred_drops_cnt = 0; q->deferred_drops_len = 0; } +} + +static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __dualpi2_qdisc_dequeue(sch); + + dualpi2_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *dualpi2_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __dualpi2_qdisc_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + dualpi2_dequeue_drop(sch); + } + return skb; } @@ -876,7 +913,7 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, WARN_ON_ONCE(1); break; } - q->memory_used -= skb->truesize; + WRITE_ONCE(q->memory_used, q->memory_used - skb->truesize); rtnl_qdisc_drop(skb, sch); } else if (qdisc_qlen(q->l_queue)) { skb = qdisc_dequeue_internal(q->l_queue, true); @@ -888,9 +925,9 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt, * l_queue on enqueue; qdisc_dequeue_internal() * handled l_queue, so we further account for sch. */ - --sch->q.qlen; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); - q->memory_used -= skb->truesize; + WRITE_ONCE(q->memory_used, q->memory_used - skb->truesize); rtnl_qdisc_drop(skb, q->l_queue); qdisc_qstats_drop(sch); } else { @@ -1046,15 +1083,15 @@ static int dualpi2_dump_stats(struct Qdisc *sch, struct gnet_dump *d) struct dualpi2_sched_data *q = qdisc_priv(sch); struct tc_dualpi2_xstats st = { .prob = READ_ONCE(q->pi2_prob), - .packets_in_c = q->packets_in_c, - .packets_in_l = q->packets_in_l, - .maxq = q->maxq, - .ecn_mark = q->ecn_mark, - .credit = q->c_protection_credit, - .step_marks = q->step_marks, - .memory_used = q->memory_used, - .max_memory_used = q->max_memory_used, - .memory_limit = q->memory_limit, + .packets_in_c = READ_ONCE(q->packets_in_c), + .packets_in_l = READ_ONCE(q->packets_in_l), + .maxq = READ_ONCE(q->maxq), + .ecn_mark = READ_ONCE(q->ecn_mark), + .credit = READ_ONCE(q->c_protection_credit), + .step_marks = READ_ONCE(q->step_marks), + .memory_used = READ_ONCE(q->memory_used), + .max_memory_used = READ_ONCE(q->max_memory_used), + .memory_limit = READ_ONCE(q->memory_limit), }; u64 qc, ql; @@ -1074,16 +1111,16 @@ static void dualpi2_reset(struct Qdisc *sch) qdisc_reset_queue(sch); qdisc_reset_queue(q->l_queue); - q->c_head_ts = 0; - q->l_head_ts = 0; - q->pi2_prob = 0; - q->packets_in_c = 0; - q->packets_in_l = 0; - q->maxq = 0; - q->ecn_mark = 0; - q->step_marks = 0; - q->memory_used = 0; - q->max_memory_used = 0; + WRITE_ONCE(q->c_head_ts, 0); + WRITE_ONCE(q->l_head_ts, 0); + WRITE_ONCE(q->pi2_prob, 0); + WRITE_ONCE(q->packets_in_c, 0); + WRITE_ONCE(q->packets_in_l, 0); + WRITE_ONCE(q->maxq, 0); + WRITE_ONCE(q->ecn_mark, 0); + WRITE_ONCE(q->step_marks, 0); + WRITE_ONCE(q->memory_used, 0); + WRITE_ONCE(q->max_memory_used, 0); dualpi2_reset_c_protection(q); } @@ -1165,7 +1202,7 @@ static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = { .priv_size = sizeof(struct dualpi2_sched_data), .enqueue = dualpi2_qdisc_enqueue, .dequeue = dualpi2_qdisc_dequeue, - .peek = qdisc_peek_dequeued, + .peek = dualpi2_peek, .init = dualpi2_init, .destroy = dualpi2_destroy, .reset = dualpi2_reset, @@ -1174,6 +1211,7 @@ static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = { .dump_stats = dualpi2_dump_stats, .owner = THIS_MODULE, }; +MODULE_ALIAS_NET_SCH("dualpi2"); static int __init dualpi2_module_init(void) { diff --git a/net/sched/sch_etf.c b/net/sched/sch_etf.c index c74d778c32a1..ada87a81da6a 100644 --- a/net/sched/sch_etf.c +++ b/net/sched/sch_etf.c @@ -189,7 +189,7 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch, rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost); qdisc_qstats_backlog_inc(sch, nskb); - sch->q.qlen++; + qdisc_qlen_inc(sch); /* Now we may need to re-arm the qdisc watchdog for the next packet. */ reset_watchdog(sch); @@ -222,7 +222,7 @@ static void timesortedlist_drop(struct Qdisc *sch, struct sk_buff *skb, qdisc_qstats_backlog_dec(sch, skb); qdisc_drop(skb, sch, &to_free); qdisc_qstats_overlimit(sch); - sch->q.qlen--; + qdisc_qlen_dec(sch); } kfree_skb_list(to_free); @@ -247,7 +247,7 @@ static void timesortedlist_remove(struct Qdisc *sch, struct sk_buff *skb) q->last = skb->tstamp; - sch->q.qlen--; + qdisc_qlen_dec(sch); } static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch) @@ -426,7 +426,7 @@ static void timesortedlist_clear(struct Qdisc *sch) rb_erase_cached(&skb->rbnode, &q->head); rtnl_kfree_skbs(skb, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } } diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index a4b07b661b77..cb8cf437ce87 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -247,9 +247,7 @@ static int ets_class_change(struct Qdisc *sch, u32 classid, u32 parentid, if (err) return err; - sch_tree_lock(sch); - cl->quantum = quantum; - sch_tree_unlock(sch); + WRITE_ONCE(cl->quantum, quantum); ets_offload_change(sch); return 0; @@ -320,7 +318,7 @@ static int ets_class_dump(struct Qdisc *sch, unsigned long arg, if (!nest) goto nla_put_failure; if (!ets_class_is_strict(q, cl)) { - if (nla_put_u32(skb, TCA_ETS_QUANTA_BAND, cl->quantum)) + if (nla_put_u32(skb, TCA_ETS_QUANTA_BAND, READ_ONCE(cl->quantum))) goto nla_put_failure; } return nla_nest_end(skb, nest); @@ -445,11 +443,11 @@ static int ets_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!cl_is_active(cl) && !ets_class_is_strict(q, cl)) { list_add_tail(&cl->alist, &q->active); - cl->deficit = cl->quantum; + cl->deficit = READ_ONCE(cl->quantum); } - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); return err; } @@ -458,7 +456,7 @@ ets_qdisc_dequeue_skb(struct Qdisc *sch, struct sk_buff *skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } @@ -499,7 +497,7 @@ static struct sk_buff *ets_qdisc_dequeue(struct Qdisc *sch) return ets_qdisc_dequeue_skb(sch, skb); } - cl->deficit += cl->quantum; + cl->deficit += READ_ONCE(cl->quantum); list_move_tail(&cl->alist, &q->active); } out: diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index f2edcf872981..7cae082a9847 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -217,7 +217,7 @@ static void fq_flow_unset_throttled(struct fq_sched_data *q, struct fq_flow *f) fq_flow_add_tail(q, f, OLD_FLOW); } -static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f) +static void fq_flow_rb_insert(struct fq_sched_data *q, struct fq_flow *f) { struct rb_node **p = &q->delayed.rb_node, *parent = NULL; @@ -233,14 +233,18 @@ static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f) } rb_link_node(&f->rate_node, parent, p); rb_insert_color(&f->rate_node, &q->delayed); - q->throttled_flows++; - q->stat_throttled++; - f->next = &throttled; if (q->time_next_delayed_flow > f->time_next_packet) q->time_next_delayed_flow = f->time_next_packet; } +static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f) +{ + fq_flow_rb_insert(q, f); + q->throttled_flows++; + q->stat_throttled++; + f->next = &throttled; +} static struct kmem_cache *fq_flow_cachep __read_mostly; @@ -497,7 +501,7 @@ static void fq_dequeue_skb(struct Qdisc *sch, struct fq_flow *flow, fq_erase_head(sch, flow, skb); skb_mark_not_on_list(skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_bstats_update(sch, skb); } @@ -533,10 +537,58 @@ static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb) rb_insert_color(&skb->rbnode, &flow->t_root); } -static bool fq_packet_beyond_horizon(const struct sk_buff *skb, +static bool fq_packet_beyond_horizon(ktime_t time_to_send, const struct fq_sched_data *q, u64 now) { - return unlikely((s64)skb->tstamp > (s64)(now + q->horizon)); + return unlikely((s64)time_to_send > (s64)(now + q->horizon)); +} + +static void fq_flow_adjust_timer(struct fq_sched_data *q, struct fq_flow *flow, + u64 time_to_send, u64 now) +{ + if (time_to_send <= now) { + fq_flow_unset_throttled(q, flow); + if (q->time_next_delayed_flow == flow->time_next_packet) { + struct rb_node *p = rb_first(&q->delayed); + + q->time_next_delayed_flow = p ? rb_entry(p, struct fq_flow, rate_node)->time_next_packet : ~0ULL; + } + flow->time_next_packet = time_to_send; + } else { + rb_erase(&flow->rate_node, &q->delayed); + flow->time_next_packet = time_to_send; + fq_flow_rb_insert(q, flow); + } +} + +static ktime_t fq_skb_tstamp_to_mono(struct sk_buff *skb) +{ + const ktime_t mono_max = NSEC_PER_SEC * TIME_UPTIME_SEC_MAX; + + if (likely(skb->tstamp_type == SKB_CLOCK_MONOTONIC)) + return max(skb->tstamp, 1); + + if (skb->tstamp_type == SKB_CLOCK_TAI) + return max(ktime_sub(skb->tstamp, ktime_mono_to_any(0, TK_OFFS_TAI)), 1); + + if (likely(skb->tstamp > mono_max)) + return max(ktime_sub(skb->tstamp, ktime_mono_to_real(0)), 1); + + /* Handle BPF programs setting skb->stamp but not tstamp_type */ + net_warn_ratelimited("fq: likely mono tstamp with tstamp_type 0\n"); + + skb->tstamp_type = SKB_CLOCK_MONOTONIC; + return max(skb->tstamp, 1); +} + +static void fq_mono_to_skb_tstamp(struct sk_buff *skb, ktime_t time_to_send) +{ + if (skb->tstamp_type == SKB_CLOCK_MONOTONIC) + skb->tstamp = time_to_send; + else if (skb->tstamp_type == SKB_CLOCK_REALTIME) + skb->tstamp = ktime_mono_to_real(time_to_send); + else + skb->tstamp = ktime_mono_to_any(time_to_send, TK_OFFS_TAI); } static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, @@ -557,17 +609,20 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (!skb->tstamp) { fq_skb_cb(skb)->time_to_send = now; } else { + ktime_t time_to_send = fq_skb_tstamp_to_mono(skb); + /* Check if packet timestamp is too far in the future. */ - if (fq_packet_beyond_horizon(skb, q, now)) { + if (fq_packet_beyond_horizon(time_to_send, q, now)) { if (q->horizon_drop) { q->stat_horizon_drops++; return qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_HORIZON_LIMIT); } q->stat_horizon_caps++; - skb->tstamp = now + q->horizon; + time_to_send = now + q->horizon; + fq_mono_to_skb_tstamp(skb, time_to_send); } - fq_skb_cb(skb)->time_to_send = skb->tstamp; + fq_skb_cb(skb)->time_to_send = (u64)time_to_send; } f = fq_classify(sch, skb, now); @@ -596,8 +651,12 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* Note: this overwrites f->age */ flow_queue_add(f, skb); + if (fq_skb_cb(skb)->time_to_send < f->time_next_packet && skb->tstamp && + fq_flow_is_throttled(f) && q->flow_max_rate == ~0UL) + fq_flow_adjust_timer(q, f, fq_skb_cb(skb)->time_to_send, now); + qdisc_qstats_backlog_inc(sch, skb); - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -801,8 +860,8 @@ static void fq_reset(struct Qdisc *sch) struct fq_flow *f; unsigned int idx; - sch->q.qlen = 0; - sch->qstats.backlog = 0; + WRITE_ONCE(sch->q.qlen, 0); + WRITE_ONCE(sch->qstats.backlog, 0); fq_flow_purge(&q->internal); diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 24db54684e8a..cafd1f943d99 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -176,9 +176,9 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets, WRITE_ONCE(flow->cvars.count, flow->cvars.count + i); WRITE_ONCE(q->backlogs[idx], q->backlogs[idx] - len); q->memory_usage -= mem; - sch->qstats.drops += i; - sch->qstats.backlog -= len; - sch->q.qlen -= i; + __qdisc_qstats_drop(sch, i); + qstats_backlog_sub(sch, len); + WRITE_ONCE(sch->q.qlen, sch->q.qlen - i); return idx; } @@ -212,10 +212,11 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch, q->new_flow_count++; WRITE_ONCE(flow->deficit, q->quantum); } - get_codel_cb(skb)->mem_usage = skb->truesize; + get_codel_cb(skb)->mem_usage = is_skb_wmem(skb) ? 0 : skb->truesize; q->memory_usage += get_codel_cb(skb)->mem_usage; memory_limited = q->memory_usage > q->memory_limit; - if (++sch->q.qlen <= sch->limit && !memory_limited) + qdisc_qlen_inc(sch); + if (sch->q.qlen <= sch->limit && !memory_limited) return NET_XMIT_SUCCESS; prev_backlog = sch->qstats.backlog; @@ -266,8 +267,8 @@ static struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx) WRITE_ONCE(q->backlogs[flow - q->flows], q->backlogs[flow - q->flows] - qdisc_pkt_len(skb)); q->memory_usage -= get_codel_cb(skb)->mem_usage; - sch->q.qlen--; - sch->qstats.backlog -= qdisc_pkt_len(skb); + qdisc_qlen_dec(sch); + qdisc_qstats_backlog_dec(sch, skb); } return skb; } @@ -280,7 +281,7 @@ static void drop_func(struct sk_buff *skb, void *ctx) qdisc_qstats_drop(sch); } -static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) +static struct sk_buff *__fq_codel_dequeue(struct Qdisc *sch) { struct fq_codel_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; @@ -317,12 +318,49 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) qdisc_bstats_update(sch, skb); WRITE_ONCE(flow->deficit, flow->deficit - qdisc_pkt_len(skb)); + return skb; +} + +static void fq_codel_dequeue_drop(struct Qdisc *sch) +{ + struct fq_codel_sched_data *q = qdisc_priv(sch); + if (q->cstats.drop_count) { qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len); q->cstats.drop_count = 0; q->cstats.drop_len = 0; } +} + +static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) +{ + struct sk_buff *skb; + + skb = __fq_codel_dequeue(sch); + + fq_codel_dequeue_drop(sch); + + return skb; +} + +static struct sk_buff *fq_codel_peek(struct Qdisc *sch) +{ + struct sk_buff *skb = skb_peek(&sch->gso_skb); + + if (!skb) { + skb = __fq_codel_dequeue(sch); + + if (skb) { + __skb_queue_head(&sch->gso_skb, skb); + /* it's still part of the queue */ + qdisc_qstats_backlog_inc(sch, skb); + sch->q.qlen++; + } + + fq_codel_dequeue_drop(sch); + } + return skb; } @@ -725,7 +763,7 @@ static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { .priv_size = sizeof(struct fq_codel_sched_data), .enqueue = fq_codel_enqueue, .dequeue = fq_codel_dequeue, - .peek = qdisc_peek_dequeued, + .peek = fq_codel_peek, .init = fq_codel_init, .reset = fq_codel_reset, .destroy = fq_codel_destroy, diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 7becbf5362b3..72f48fa4010b 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -184,8 +184,8 @@ static int fq_pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, pkt_len = qdisc_pkt_len(skb); q->stats.packets_in++; q->memory_usage += skb->truesize; - sch->qstats.backlog += pkt_len; - sch->q.qlen++; + qstats_backlog_add(sch, pkt_len); + qdisc_qlen_inc(sch); flow_queue_add(sel_flow, skb); if (list_empty(&sel_flow->flowchain)) { list_add_tail(&sel_flow->flowchain, &q->new_flows); @@ -262,8 +262,8 @@ static struct sk_buff *fq_pie_qdisc_dequeue(struct Qdisc *sch) if (flow->head) { skb = dequeue_head(flow); pkt_len = qdisc_pkt_len(skb); - sch->qstats.backlog -= pkt_len; - sch->q.qlen--; + qstats_backlog_sub(sch, pkt_len); + qdisc_qlen_dec(sch); qdisc_bstats_update(sch, skb); } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a93321db8fd7..3f1c510df850 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -118,7 +118,7 @@ static inline struct sk_buff *__skb_dequeue_bad_txq(struct Qdisc *q) qdisc_qstats_cpu_qlen_dec(q); } else { qdisc_qstats_backlog_dec(q, skb); - q->q.qlen--; + qdisc_qlen_dec(q); } } else { skb = SKB_XOFF_MAGIC; @@ -159,7 +159,7 @@ static inline void qdisc_enqueue_skb_bad_txq(struct Qdisc *q, qdisc_qstats_cpu_qlen_inc(q); } else { qdisc_qstats_backlog_inc(q, skb); - q->q.qlen++; + qdisc_qlen_inc(q); } if (lock) @@ -188,7 +188,7 @@ static inline void dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) } else { q->qstats.requeues++; qdisc_qstats_backlog_inc(q, skb); - q->q.qlen++; + qdisc_qlen_inc(q); } skb = next; @@ -294,7 +294,7 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate, qdisc_qstats_cpu_qlen_dec(q); } else { qdisc_qstats_backlog_dec(q, skb); - q->q.qlen--; + qdisc_qlen_dec(q); } } else { skb = NULL; @@ -568,16 +568,24 @@ static void dev_watchdog(struct timer_list *t) dev->netdev_ops->ndo_tx_timeout(dev, i); netif_unfreeze_queues(dev); } - if (!mod_timer(&dev->watchdog_timer, - round_jiffies(oldest_start + - dev->watchdog_timeo))) - release = false; + spin_lock(&dev->watchdog_lock); + mod_timer(&dev->watchdog_timer, + round_jiffies(oldest_start + + dev->watchdog_timeo)); + release = false; + spin_unlock(&dev->watchdog_lock); } } spin_unlock(&dev->tx_global_lock); - if (release) + spin_lock(&dev->watchdog_lock); + if (timer_pending(&dev->watchdog_timer)) + release = false; + if (release && dev->watchdog_ref_held) { netdev_put(dev, &dev->watchdog_dev_tracker); + dev->watchdog_ref_held = false; + } + spin_unlock(&dev->watchdog_lock); } void netdev_watchdog_up(struct net_device *dev) @@ -586,18 +594,34 @@ void netdev_watchdog_up(struct net_device *dev) return; if (dev->watchdog_timeo <= 0) dev->watchdog_timeo = 5*HZ; + spin_lock_bh(&dev->tx_global_lock); + + spin_lock(&dev->watchdog_lock); if (!mod_timer(&dev->watchdog_timer, - round_jiffies(jiffies + dev->watchdog_timeo))) - netdev_hold(dev, &dev->watchdog_dev_tracker, - GFP_ATOMIC); + round_jiffies(jiffies + dev->watchdog_timeo))) { + if (!dev->watchdog_ref_held) { + netdev_hold(dev, &dev->watchdog_dev_tracker, + GFP_ATOMIC); + dev->watchdog_ref_held = true; + } + } + spin_unlock(&dev->watchdog_lock); + + spin_unlock_bh(&dev->tx_global_lock); } EXPORT_SYMBOL_GPL(netdev_watchdog_up); static void netdev_watchdog_down(struct net_device *dev) { netif_tx_lock_bh(dev); - if (timer_delete(&dev->watchdog_timer)) + + spin_lock(&dev->watchdog_lock); + if (timer_delete(&dev->watchdog_timer)) { netdev_put(dev, &dev->watchdog_dev_tracker); + dev->watchdog_ref_held = false; + } + spin_unlock(&dev->watchdog_lock); + netif_tx_unlock_bh(dev); } @@ -609,13 +633,14 @@ static void netdev_watchdog_down(struct net_device *dev) */ void netif_carrier_on(struct net_device *dev) { + if (READ_ONCE(dev->proto_down)) + return; + if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { if (dev->reg_state == NETREG_UNINITIALIZED) return; atomic_inc(&dev->carrier_up_count); linkwatch_fire_event(dev); - if (netif_running(dev)) - netdev_watchdog_up(dev); } } EXPORT_SYMBOL(netif_carrier_on); @@ -1059,8 +1084,8 @@ void qdisc_reset(struct Qdisc *qdisc) __skb_queue_purge(&qdisc->gso_skb); __skb_queue_purge(&qdisc->skb_bad_txq); - qdisc->q.qlen = 0; - qdisc->qstats.backlog = 0; + WRITE_ONCE(qdisc->q.qlen, 0); + WRITE_ONCE(qdisc->qstats.backlog, 0); } EXPORT_SYMBOL(qdisc_reset); diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index 36d0cafac206..fcc1a4c03636 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -388,8 +388,8 @@ static int gred_offload_dump_stats(struct Qdisc *sch) bytes += u64_stats_read(&hw_stats->stats.bstats[i].bytes); packets += u64_stats_read(&hw_stats->stats.bstats[i].packets); sch->qstats.qlen += hw_stats->stats.qstats[i].qlen; - sch->qstats.backlog += hw_stats->stats.qstats[i].backlog; - sch->qstats.drops += hw_stats->stats.qstats[i].drops; + qstats_backlog_add(sch, hw_stats->stats.qstats[i].backlog); + __qdisc_qstats_drop(sch, hw_stats->stats.qstats[i].drops); sch->qstats.requeues += hw_stats->stats.qstats[i].requeues; sch->qstats.overlimits += hw_stats->stats.qstats[i].overlimits; } diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 83b2ca2e37fc..7e537295b8b6 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -715,7 +715,7 @@ init_vf(struct hfsc_class *cl, unsigned int len) rtsc_min(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total); cl->cl_vtadj = 0; - cl->cl_vtperiod++; /* increment vt period */ + WRITE_ONCE(cl->cl_vtperiod, cl->cl_vtperiod + 1); /* increment vt period */ cl->cl_parentperiod = cl->cl_parent->cl_vtperiod; if (cl->cl_parent->cl_nactive == 0) cl->cl_parentperiod++; @@ -753,11 +753,11 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time) u64 f; /* , myf_bound, delta; */ int go_passive = 0; - if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC) + if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC && cl->cl_nactive) go_passive = 1; for (; cl->cl_parent != NULL; cl = cl->cl_parent) { - cl->cl_total += len; + WRITE_ONCE(cl->cl_total, cl->cl_total + len); if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0) continue; @@ -847,7 +847,7 @@ hfsc_adjust_levels(struct hfsc_class *cl) if (p->level >= level) level = p->level + 1; } - cl->level = level; + WRITE_ONCE(cl->level, level); } while ((cl = cl->cl_parent) != NULL); } @@ -1338,10 +1338,10 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg, __u32 qlen; qdisc_qstats_qlen_backlog(cl->qdisc, &qlen, &cl->qstats.backlog); - xstats.level = cl->level; - xstats.period = cl->cl_vtperiod; - xstats.work = cl->cl_total; - xstats.rtwork = cl->cl_cumul; + xstats.level = READ_ONCE(cl->level); + xstats.period = READ_ONCE(cl->cl_vtperiod); + xstats.work = READ_ONCE(cl->cl_total); + xstats.rtwork = READ_ONCE(cl->cl_cumul); if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || @@ -1452,15 +1452,15 @@ hfsc_change_qdisc(struct Qdisc *sch, struct nlattr *opt, static void hfsc_reset_class(struct hfsc_class *cl) { - cl->cl_total = 0; - cl->cl_cumul = 0; + WRITE_ONCE(cl->cl_total, 0); + WRITE_ONCE(cl->cl_cumul, 0); cl->cl_d = 0; cl->cl_e = 0; cl->cl_vt = 0; cl->cl_vtadj = 0; cl->cl_cvtmin = 0; cl->cl_cvtoff = 0; - cl->cl_vtperiod = 0; + WRITE_ONCE(cl->cl_vtperiod, 0); cl->cl_parentperiod = 0; cl->cl_f = 0; cl->cl_myf = 0; @@ -1560,8 +1560,8 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) return err; } - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); if (first && !cl_in_el_or_vttree(cl)) { if (cl->cl_flags & HFSC_RSC) @@ -1626,7 +1626,7 @@ hfsc_dequeue(struct Qdisc *sch) bstats_update(&cl->bstats, skb); update_vf(cl, qdisc_pkt_len(skb), cur_time); if (realtime) - cl->cl_cumul += qdisc_pkt_len(skb); + WRITE_ONCE(cl->cl_cumul, cl->cl_cumul + qdisc_pkt_len(skb)); if (cl->cl_flags & HFSC_RSC) { if (cl->qdisc->q.qlen != 0) { @@ -1650,7 +1650,7 @@ hfsc_dequeue(struct Qdisc *sch) qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 96021f52d835..1e25b75daae2 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -360,7 +360,7 @@ static unsigned int hhf_drop(struct Qdisc *sch, struct sk_buff **to_free) if (bucket->head) { struct sk_buff *skb = dequeue_head(bucket); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_drop(skb, sch, to_free); } @@ -400,7 +400,8 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch, } bucket->deficit = weight * q->quantum; } - if (++sch->q.qlen <= sch->limit) + qdisc_qlen_inc(sch); + if (sch->q.qlen <= sch->limit) return NET_XMIT_SUCCESS; prev_backlog = sch->qstats.backlog; @@ -443,7 +444,7 @@ static struct sk_buff *hhf_dequeue(struct Qdisc *sch) if (bucket->head) { skb = dequeue_head(bucket); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index eb12381795ce..908b9ba9ba2e 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -114,7 +114,8 @@ struct htb_class { */ struct gnet_stats_basic_sync bstats; struct gnet_stats_basic_sync bstats_bias; - struct tc_htb_xstats xstats; /* our special stats */ + u32 xstats_lends; + u32 xstats_borrows; /* token bucket parameters */ s64 tokens, ctokens;/* current number of tokens */ @@ -567,8 +568,8 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, s64 *diff) return; if (new_mode == HTB_CANT_SEND) { - cl->overlimits++; - q->overlimits++; + WRITE_ONCE(cl->overlimits, cl->overlimits + 1); + WRITE_ONCE(q->overlimits, q->overlimits + 1); } if (cl->prio_activity) { /* not necessary: speed optimization */ @@ -628,7 +629,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* enqueue to helper queue */ if (q->direct_queue.qlen < q->direct_qlen) { __qdisc_enqueue_tail(skb, &q->direct_queue); - q->direct_pkts++; + WRITE_ONCE(q->direct_pkts, q->direct_pkts + 1); } else { return qdisc_drop(skb, sch, to_free); } @@ -643,15 +644,15 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch, to_free)) != NET_XMIT_SUCCESS) { if (net_xmit_drop_count(ret)) { qdisc_qstats_drop(sch); - cl->drops++; + WRITE_ONCE(cl->drops, cl->drops + 1); } return ret; } else { htb_activate(q, cl); } - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -665,7 +666,7 @@ static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, s64 diff) if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; - cl->tokens = toks; + WRITE_ONCE(cl->tokens, toks); } static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, s64 diff) @@ -678,7 +679,7 @@ static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, s64 diff) if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; - cl->ctokens = toks; + WRITE_ONCE(cl->ctokens, toks); } /** @@ -707,11 +708,12 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl, diff = min_t(s64, q->now - cl->t_c, cl->mbuffer); if (cl->level >= level) { if (cl->level == level) - cl->xstats.lends++; + WRITE_ONCE(cl->xstats_lends, cl->xstats_lends + 1); htb_accnt_tokens(cl, bytes, diff); } else { - cl->xstats.borrows++; - cl->tokens += diff; /* we moved t_c; update tokens */ + WRITE_ONCE(cl->xstats_borrows, cl->xstats_borrows + 1); + /* we moved t_c; update tokens */ + WRITE_ONCE(cl->tokens, cl->tokens + diff); } htb_accnt_ctokens(cl, bytes, diff); cl->t_c = q->now; @@ -951,7 +953,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) ok: qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } @@ -1147,6 +1149,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt, * parts (especially calling ndo_setup_tc) on errors. */ q->offload = true; + sch->flags |= TCQ_F_OFFLOADED; return 0; } @@ -1207,17 +1210,12 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - if (q->offload) - sch->flags |= TCQ_F_OFFLOADED; - else - sch->flags &= ~TCQ_F_OFFLOADED; - - sch->qstats.overlimits = q->overlimits; + sch->qstats.overlimits = READ_ONCE(q->overlimits); /* Its safe to not acquire qdisc lock. As we hold RTNL, * no change can happen on the qdisc parameters. */ - gopt.direct_pkts = q->direct_pkts; + gopt.direct_pkts = READ_ONCE(q->direct_pkts); gopt.version = HTB_VER; gopt.rate2quantum = q->rate2quantum; gopt.defcls = q->defcls; @@ -1295,8 +1293,6 @@ static void htb_offload_aggregate_stats(struct htb_sched *q, struct htb_class *c; unsigned int i; - gnet_stats_basic_sync_init(&cl->bstats); - for (i = 0; i < q->clhash.hashsize; i++) { hlist_for_each_entry(c, &q->clhash.hash[i], common.hnode) { struct htb_class *p = c; @@ -1315,7 +1311,7 @@ static void htb_offload_aggregate_stats(struct htb_sched *q, } } } - _bstats_update(&cl->bstats, bytes, packets); + _bstats_set(&cl->bstats, bytes, packets); } static int @@ -1323,32 +1319,40 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d) { struct htb_class *cl = (struct htb_class *)arg; struct htb_sched *q = qdisc_priv(sch); + struct tc_htb_xstats xstats = { + .lends = READ_ONCE(cl->xstats_lends), + .borrows = READ_ONCE(cl->xstats_borrows), + }; struct gnet_stats_queue qs = { - .drops = cl->drops, - .overlimits = cl->overlimits, + .drops = READ_ONCE(cl->drops), + .overlimits = READ_ONCE(cl->overlimits), }; __u32 qlen = 0; if (!cl->level && cl->leaf.q) qdisc_qstats_qlen_backlog(cl->leaf.q, &qlen, &qs.backlog); - cl->xstats.tokens = clamp_t(s64, PSCHED_NS2TICKS(cl->tokens), - INT_MIN, INT_MAX); - cl->xstats.ctokens = clamp_t(s64, PSCHED_NS2TICKS(cl->ctokens), - INT_MIN, INT_MAX); + xstats.tokens = clamp_t(s64, PSCHED_NS2TICKS(READ_ONCE(cl->tokens)), + INT_MIN, INT_MAX); + xstats.ctokens = clamp_t(s64, PSCHED_NS2TICKS(READ_ONCE(cl->ctokens)), + INT_MIN, INT_MAX); if (q->offload) { + spin_lock_bh(qdisc_lock(sch)); if (!cl->level) { - if (cl->leaf.q) - cl->bstats = cl->leaf.q->bstats; - else - gnet_stats_basic_sync_init(&cl->bstats); - _bstats_update(&cl->bstats, - u64_stats_read(&cl->bstats_bias.bytes), - u64_stats_read(&cl->bstats_bias.packets)); + u64 bytes = 0, packets = 0; + + if (cl->leaf.q) { + bytes = u64_stats_read(&cl->leaf.q->bstats.bytes); + packets = u64_stats_read(&cl->leaf.q->bstats.packets); + } + bytes += u64_stats_read(&cl->bstats_bias.bytes); + packets += u64_stats_read(&cl->bstats_bias.packets); + _bstats_set(&cl->bstats, bytes, packets); } else { htb_offload_aggregate_stats(q, cl); } + spin_unlock_bh(qdisc_lock(sch)); } if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 || @@ -1356,7 +1360,7 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d) gnet_stats_copy_queue(d, NULL, &qs, qlen) < 0) return -1; - return gnet_stats_copy_app(d, &cl->xstats, sizeof(cl->xstats)); + return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); } static struct netdev_queue * @@ -1516,8 +1520,8 @@ static void htb_parent_to_leaf(struct Qdisc *sch, struct htb_class *cl, parent->level = 0; memset(&parent->inner, 0, sizeof(parent->inner)); parent->leaf.q = new_q ? new_q : &noop_qdisc; - parent->tokens = parent->buffer; - parent->ctokens = parent->cbuffer; + WRITE_ONCE(parent->tokens, parent->buffer); + WRITE_ONCE(parent->ctokens, parent->cbuffer); parent->t_c = ktime_get_ns(); parent->cmode = HTB_CAN_SEND; if (q->offload) diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index a0133a7b9d3b..5a6b14557d85 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -143,30 +143,43 @@ EXPORT_SYMBOL_NS_GPL(mq_attach, "NET_SCHED_INTERNAL"); void mq_dump_common(struct Qdisc *sch, struct sk_buff *skb) { struct net_device *dev = qdisc_dev(sch); - struct Qdisc *qdisc; + struct gnet_stats_queue qstats = { 0 }; + struct gnet_stats_basic_sync bstats; + const struct Qdisc *qdisc; + unsigned int qlen = 0; unsigned int ntx; - sch->q.qlen = 0; - gnet_stats_basic_sync_init(&sch->bstats); - memset(&sch->qstats, 0, sizeof(sch->qstats)); + gnet_stats_basic_sync_init(&bstats); /* MQ supports lockless qdiscs. However, statistics accounting needs * to account for all, none, or a mix of locked and unlocked child * qdiscs. Percpu stats are added to counters in-band and locking * qdisc totals are added at end. */ + rcu_read_lock(); for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { - qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); - spin_lock_bh(qdisc_lock(qdisc)); + qdisc = rcu_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); - gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, - &qdisc->bstats, false); - gnet_stats_add_queue(&sch->qstats, qdisc->cpu_qstats, + gnet_stats_add_basic(&bstats, qdisc->cpu_bstats, + &qdisc->bstats, true); + gnet_stats_add_queue(&qstats, qdisc->cpu_qstats, &qdisc->qstats); - sch->q.qlen += qdisc_qlen(qdisc); - - spin_unlock_bh(qdisc_lock(qdisc)); + qlen += qdisc_qlen_lockless(qdisc); } + rcu_read_unlock(); + + spin_lock_bh(qdisc_lock(sch)); + _bstats_set(&sch->bstats, u64_stats_read(&bstats.bytes), + u64_stats_read(&bstats.packets)); + spin_unlock_bh(qdisc_lock(sch)); + + WRITE_ONCE(sch->qstats.qlen, qstats.qlen); + WRITE_ONCE(sch->qstats.backlog, qstats.backlog); + WRITE_ONCE(sch->qstats.drops, qstats.drops); + WRITE_ONCE(sch->qstats.requeues, qstats.requeues); + WRITE_ONCE(sch->qstats.overlimits, qstats.overlimits); + + WRITE_ONCE(sch->q.qlen, qlen); } EXPORT_SYMBOL_NS_GPL(mq_dump_common, "NET_SCHED_INTERNAL"); diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 002add5ce9e0..ae991fc25b43 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -551,34 +551,47 @@ static int mqprio_dump_tc_entries(struct mqprio_sched *priv, static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) { - struct net_device *dev = qdisc_dev(sch); - struct mqprio_sched *priv = qdisc_priv(sch); struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb); + struct mqprio_sched *priv = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct gnet_stats_queue qstats = { 0 }; + struct gnet_stats_basic_sync bstats; struct tc_mqprio_qopt opt = { 0 }; - struct Qdisc *qdisc; + const struct Qdisc *qdisc; + unsigned int qlen = 0; unsigned int ntx; - sch->q.qlen = 0; - gnet_stats_basic_sync_init(&sch->bstats); - memset(&sch->qstats, 0, sizeof(sch->qstats)); + gnet_stats_basic_sync_init(&bstats); /* MQ supports lockless qdiscs. However, statistics accounting needs * to account for all, none, or a mix of locked and unlocked child * qdiscs. Percpu stats are added to counters in-band and locking * qdisc totals are added at end. */ + rcu_read_lock(); for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { - qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); - spin_lock_bh(qdisc_lock(qdisc)); + qdisc = rcu_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); - gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, - &qdisc->bstats, false); - gnet_stats_add_queue(&sch->qstats, qdisc->cpu_qstats, + gnet_stats_add_basic(&bstats, qdisc->cpu_bstats, + &qdisc->bstats, true); + gnet_stats_add_queue(&qstats, qdisc->cpu_qstats, &qdisc->qstats); - sch->q.qlen += qdisc_qlen(qdisc); - - spin_unlock_bh(qdisc_lock(qdisc)); + qlen += qdisc_qlen_lockless(qdisc); } + rcu_read_unlock(); + + spin_lock_bh(qdisc_lock(sch)); + _bstats_set(&sch->bstats, u64_stats_read(&bstats.bytes), + u64_stats_read(&bstats.packets)); + spin_unlock_bh(qdisc_lock(sch)); + + WRITE_ONCE(sch->qstats.qlen, qstats.qlen); + WRITE_ONCE(sch->qstats.backlog, qstats.backlog); + WRITE_ONCE(sch->qstats.drops, qstats.drops); + WRITE_ONCE(sch->qstats.requeues, qstats.requeues); + WRITE_ONCE(sch->qstats.overlimits, qstats.overlimits); + + WRITE_ONCE(sch->q.qlen, qlen); mqprio_qopt_reconstruct(dev, &opt); opt.hw = priv->hw_offload; @@ -659,58 +672,48 @@ static int mqprio_dump_class(struct Qdisc *sch, unsigned long cl, static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, struct gnet_dump *d) - __releases(d->lock) - __acquires(d->lock) { + const struct Qdisc *qdisc; + int res = 0; + + rcu_read_lock(); if (cl >= TC_H_MIN_PRIORITY) { - int i; - __u32 qlen; - struct gnet_stats_queue qstats = {0}; - struct gnet_stats_basic_sync bstats; struct net_device *dev = qdisc_dev(sch); struct netdev_tc_txq tc = dev->tc_to_txq[cl & TC_BITMASK]; + struct gnet_stats_queue qstats = {0}; + struct gnet_stats_basic_sync bstats; + u32 qlen = 0; + int i; gnet_stats_basic_sync_init(&bstats); - /* Drop lock here it will be reclaimed before touching - * statistics this is required because the d->lock we - * hold here is the look on dev_queue->qdisc_sleeping - * also acquired below. - */ - if (d->lock) - spin_unlock_bh(d->lock); for (i = tc.offset; i < tc.offset + tc.count; i++) { struct netdev_queue *q = netdev_get_tx_queue(dev, i); - struct Qdisc *qdisc = rtnl_dereference(q->qdisc); - - spin_lock_bh(qdisc_lock(qdisc)); + qdisc = rcu_dereference(q->qdisc); gnet_stats_add_basic(&bstats, qdisc->cpu_bstats, - &qdisc->bstats, false); + &qdisc->bstats, true); gnet_stats_add_queue(&qstats, qdisc->cpu_qstats, &qdisc->qstats); - sch->q.qlen += qdisc_qlen(qdisc); - - spin_unlock_bh(qdisc_lock(qdisc)); + qlen += qdisc_qlen_lockless(qdisc); } - qlen = qdisc_qlen(sch) + qstats.qlen; - /* Reclaim root sleeping lock before completing stats */ - if (d->lock) - spin_lock_bh(d->lock); + qlen = qlen + qstats.qlen; + if (gnet_stats_copy_basic(d, NULL, &bstats, false) < 0 || gnet_stats_copy_queue(d, NULL, &qstats, qlen) < 0) - return -1; + res = -1; } else { struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); - sch = rtnl_dereference(dev_queue->qdisc_sleeping); - if (gnet_stats_copy_basic(d, sch->cpu_bstats, - &sch->bstats, true) < 0 || - qdisc_qstats_copy(d, sch) < 0) - return -1; + qdisc = rcu_dereference(dev_queue->qdisc_sleeping); + if (gnet_stats_copy_basic(d, qdisc->cpu_bstats, + &qdisc->bstats, true) < 0 || + qdisc_qstats_copy(d, qdisc) < 0) + res = -1; } - return 0; + rcu_read_unlock(); + return res; } static void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 9f822fee113d..4e465d11e3d7 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -76,7 +76,7 @@ multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch, ret = qdisc_enqueue(skb, qdisc, to_free); if (ret == NET_XMIT_SUCCESS) { - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } if (net_xmit_drop_count(ret)) @@ -106,7 +106,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) skb = qdisc->dequeue(qdisc); if (skb) { qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 17a79fe2f091..8a1b89a289d8 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -27,8 +27,6 @@ #include #include -#define VERSION "1.3" - /* Network Emulation Queuing algorithm. ==================================== @@ -71,89 +69,101 @@ struct disttable { s16 table[] __counted_by(size); }; +/* Loss models */ +enum { + CLG_RANDOM, + CLG_4_STATES, + CLG_GILB_ELL, +}; + +/* States in GE model */ +enum { + GOOD_STATE = 1, + BAD_STATE, +}; + +/* States in 4 state model */ +enum { + TX_IN_GAP_PERIOD = 1, + TX_IN_BURST_PERIOD, + LOST_IN_GAP_PERIOD, + LOST_IN_BURST_PERIOD, +}; + struct netem_sched_data { - /* internal t(ime)fifo qdisc uses t_root and sch->limit */ - struct rb_root t_root; - - /* a linear queue; reduces rbtree rebalancing when jitter is low */ - struct sk_buff *t_head; - struct sk_buff *t_tail; - - u32 t_len; - - /* optional qdisc for classful handling (NULL at netem init) */ - struct Qdisc *qdisc; - - struct qdisc_watchdog watchdog; - - s64 latency; - s64 jitter; - - u32 loss; - u32 ecn; - u32 limit; - u32 counter; - u32 gap; - u32 duplicate; - u32 reorder; - u32 corrupt; - u64 rate; - s32 packet_overhead; - u32 cell_size; - struct reciprocal_value cell_size_reciprocal; - s32 cell_overhead; + /* Cacheline 0: tfifo state and per-packet enqueue/dequeue scalars. */ + struct rb_root t_root; + struct sk_buff *t_head; + struct sk_buff *t_tail; + u32 t_len; + u32 counter; + s64 latency; + s64 jitter; + u64 rate; + u32 gap; + u32 loss; + /* Cacheline 1: zero-check scalars and correlation states. */ + u32 duplicate; + u32 reorder; + u32 corrupt; + u32 ecn; struct crndstate { u32 last; u32 rho; } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor; + u8 loss_model; - struct prng { + /* Cacheline 2: PRNG, distribution tables, slot dequeue state etc. */ + struct prng { u64 seed; struct rnd_state prng_state; } prng; + struct disttable *delay_dist; + struct slotstate { + u64 slot_next; + s32 packets_left; + s32 bytes_left; + } slot; + struct disttable *slot_dist; + struct Qdisc *qdisc; - struct disttable *delay_dist; - - enum { - CLG_RANDOM, - CLG_4_STATES, - CLG_GILB_ELL, - } loss_model; - - enum { - TX_IN_GAP_PERIOD = 1, - TX_IN_BURST_PERIOD, - LOST_IN_GAP_PERIOD, - LOST_IN_BURST_PERIOD, - } _4_state_model; - - enum { - GOOD_STATE = 1, - BAD_STATE, - } GE_state_model; + /* + * Warm: rate-shaping parameters (only read when rate != 0) and + * configuration-only fields. The fast path reads sch->limit, not + * q->limit. + */ + s32 packet_overhead; + u32 cell_size; + struct reciprocal_value cell_size_reciprocal; + s32 cell_overhead; + u32 limit; /* Correlated Loss Generation models */ struct clgstate { - /* state of the Markov chain */ - u8 state; - /* 4-states and Gilbert-Elliot models */ u32 a1; /* p13 for 4-states or p for GE */ u32 a2; /* p31 for 4-states or r for GE */ u32 a3; /* p32 for 4-states or h for GE */ u32 a4; /* p14 for 4-states or 1-k for GE */ u32 a5; /* p23 used only in 4-states */ + + /* state of the Markov chain */ + u8 state; } clg; - struct tc_netem_slot slot_config; - struct slotstate { - u64 slot_next; - s32 packets_left; - s32 bytes_left; - } slot; + /* Impairment counters */ + u64 delayed; + u64 dropped; + u64 corrupted; + u64 duplicated; + u64 ecn_marked; + u64 reordered; + u64 allocation_errors; - struct disttable *slot_dist; + /* Cold tail: slot reschedule config and the watchdog timer. */ + struct tc_netem_slot slot_config; + struct qdisc_watchdog watchdog; }; /* Time stamp put into socket buffer control block @@ -416,7 +426,7 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) rb_insert_color(&nskb->rbnode, &q->t_root); } q->t_len++; - sch->q.qlen++; + qdisc_qlen_inc(sch); } /* netem can't properly corrupt a megapacket (like we get from GSO), so instead @@ -462,16 +472,21 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* Random duplication */ if (q->duplicate && skb->tc_depth == 0 && - q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) + q->duplicate >= get_crandom(&q->dup_cor, &q->prng)) { ++count; + WRITE_ONCE(q->duplicated, q->duplicated + 1); + } /* Drop packet? */ if (loss_event(q)) { - if (q->ecn && INET_ECN_set_ce(skb)) - qdisc_qstats_drop(sch); /* mark packet */ - else + if (q->ecn && INET_ECN_set_ce(skb)) { + WRITE_ONCE(q->ecn_marked, q->ecn_marked + 1); + } else { + WRITE_ONCE(q->dropped, q->dropped + 1); --count; + } } + if (count == 0) { qdisc_qstats_drop(sch); __qdisc_drop(skb, to_free); @@ -488,8 +503,11 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, * If we need to duplicate packet, then clone it before * original is modified. */ - if (count > 1) + if (count > 1) { skb2 = skb_clone(skb, GFP_ATOMIC); + if (!skb2) + WRITE_ONCE(q->allocation_errors, q->allocation_errors + 1); + } /* * Randomized packet corruption. @@ -500,8 +518,10 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor, &q->prng)) { if (skb_is_gso(skb)) { skb = netem_segment(skb, sch, to_free); - if (!skb) + if (!skb) { + WRITE_ONCE(q->allocation_errors, q->allocation_errors + 1); goto finish_segs; + } segs = skb->next; skb_mark_not_on_list(skb); @@ -510,19 +530,23 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, skb = skb_unshare(skb, GFP_ATOMIC); if (unlikely(!skb)) { + WRITE_ONCE(q->allocation_errors, q->allocation_errors + 1); qdisc_qstats_drop(sch); goto finish_segs; } - if (skb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(skb)) { + if (skb_linearize(skb) || + (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))) { + WRITE_ONCE(q->allocation_errors, q->allocation_errors + 1); qdisc_drop(skb, sch, to_free); skb = NULL; goto finish_segs; } - if (skb_headlen(skb)) - skb->data[get_random_u32_below(skb_headlen(skb))] ^= - 1 << get_random_u32_below(8); + if (skb->len) { + u32 offset = get_random_u32_below(skb->len); + skb->data[offset] ^= 1 << get_random_u32_below(8); + WRITE_ONCE(q->corrupted, q->corrupted + 1); + } } if (unlikely(sch->q.qlen >= sch->limit)) { @@ -601,12 +625,16 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, cb->time_to_send = now + delay; ++q->counter; + if (delay) + WRITE_ONCE(q->delayed, q->delayed + 1); + tfifo_enqueue(skb, sch); } else { /* * Do re-ordering by putting one out of N packets at the front * of the queue. */ + WRITE_ONCE(q->reordered, q->reordered + 1); cb->time_to_send = ktime_get_ns(); q->counter = 0; @@ -749,20 +777,20 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) if (err != NET_XMIT_SUCCESS) { if (net_xmit_drop_count(err)) qdisc_qstats_drop(sch); - sch->qstats.backlog -= pkt_len; - sch->q.qlen--; + qstats_backlog_sub(sch, pkt_len); + qdisc_qlen_dec(sch); qdisc_tree_reduce_backlog(sch, 1, pkt_len); } goto tfifo_dequeue; } - sch->q.qlen--; + qdisc_qlen_dec(sch); goto deliver; } if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) { - sch->q.qlen--; + qdisc_qlen_dec(sch); goto deliver; } } @@ -775,7 +803,7 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) { - sch->q.qlen--; + qdisc_qlen_dec(sch); goto deliver; } } @@ -919,7 +947,8 @@ static void get_rate(struct netem_sched_data *q, const struct nlattr *attr) q->cell_size_reciprocal = (struct reciprocal_value) { 0 }; } -static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr) +static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr, + struct netlink_ext_ack *extack) { const struct nlattr *la; int rem; @@ -932,7 +961,8 @@ static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr) const struct tc_netem_gimodel *gi = nla_data(la); if (nla_len(la) < sizeof(struct tc_netem_gimodel)) { - pr_info("netem: incorrect gi model size\n"); + NL_SET_ERR_MSG_ATTR(extack, la, + "netem: incorrect gi model size"); return -EINVAL; } @@ -951,7 +981,8 @@ static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr) const struct tc_netem_gemodel *ge = nla_data(la); if (nla_len(la) < sizeof(struct tc_netem_gemodel)) { - pr_info("netem: incorrect ge model size\n"); + NL_SET_ERR_MSG_ATTR(extack, la, + "netem: incorrect ge model size"); return -EINVAL; } @@ -965,7 +996,8 @@ static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr) } default: - pr_info("netem: unknown loss type %u\n", type); + NL_SET_ERR_MSG_ATTR_FMT(extack, la, + "netem: unknown loss type %u", type); return -EINVAL; } } @@ -988,19 +1020,21 @@ static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { }; static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, - const struct nla_policy *policy, int len) + const struct nla_policy *policy, int len, + struct netlink_ext_ack *extack) { int nested_len = nla_len(nla) - NLA_ALIGN(len); if (nested_len < 0) { - pr_info("netem: invalid attributes len %d\n", nested_len); + NL_SET_ERR_MSG_FMT(extack, "netem: invalid attributes len %d < %d", + nla_len(nla), NLA_ALIGN(len)); return -EINVAL; } if (nested_len >= nla_attr_size(0)) return nla_parse_deprecated(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), - nested_len, policy, NULL); + nested_len, policy, extack); memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); return 0; @@ -1020,7 +1054,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, int ret; qopt = nla_data(opt); - ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt)); + ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt), extack); if (ret < 0) return ret; @@ -1060,7 +1094,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, old_loss_model = q->loss_model; if (tb[TCA_NETEM_LOSS]) { - ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]); + ret = get_loss_clg(q, tb[TCA_NETEM_LOSS], extack); if (ret) { q->loss_model = old_loss_model; q->clg = old_clg; @@ -1139,7 +1173,6 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { struct netem_sched_data *q = qdisc_priv(sch); - int ret; qdisc_watchdog_init(&q->watchdog, sch); @@ -1150,10 +1183,7 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt, q->prng.seed = get_random_u64(); prandom_seed_state(&q->prng.prng_state, q->prng.seed); - ret = netem_change(sch, opt, extack); - if (ret) - pr_info("netem: change failed\n"); - return ret; + return netem_change(sch, opt, extack); } static void netem_destroy(struct Qdisc *sch) @@ -1303,6 +1333,22 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) return -1; } +static int netem_dump_stats(struct Qdisc *sch, struct gnet_dump *d) +{ + struct netem_sched_data *q = qdisc_priv(sch); + struct tc_netem_xstats st = { + .delayed = READ_ONCE(q->delayed), + .dropped = READ_ONCE(q->dropped), + .corrupted = READ_ONCE(q->corrupted), + .duplicated = READ_ONCE(q->duplicated), + .reordered = READ_ONCE(q->reordered), + .ecn_marked = READ_ONCE(q->ecn_marked), + .allocation_errors = READ_ONCE(q->allocation_errors), + }; + + return gnet_stats_copy_app(d, &st, sizeof(st)); +} + static int netem_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { @@ -1365,14 +1411,13 @@ static struct Qdisc_ops netem_qdisc_ops __read_mostly = { .destroy = netem_destroy, .change = netem_change, .dump = netem_dump, + .dump_stats = netem_dump_stats, .owner = THIS_MODULE, }; MODULE_ALIAS_NET_SCH("netem"); - static int __init netem_module_init(void) { - pr_info("netem: version " VERSION "\n"); return register_qdisc(&netem_qdisc_ops); } static void __exit netem_module_exit(void) diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 9e2b9a490db2..e4dd56a89072 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -85,8 +85,8 @@ prio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) ret = qdisc_enqueue(skb, qdisc, to_free); if (ret == NET_XMIT_SUCCESS) { - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } if (net_xmit_drop_count(ret)) @@ -119,7 +119,7 @@ static struct sk_buff *prio_dequeue(struct Qdisc *sch) if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } } diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 699e45873f86..cb56787e1d25 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -1152,12 +1152,12 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) if (!skb) return NULL; - sch->q.qlen--; + qdisc_qlen_dec(sch); skb = agg_dequeue(in_serv_agg, cl, len); if (!skb) { - sch->q.qlen++; + qdisc_qlen_inc(sch); return NULL; } @@ -1264,8 +1264,8 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, } _bstats_update(&cl->bstats, len, gso_segs); - sch->qstats.backlog += len; - ++sch->q.qlen; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); agg = cl->agg; /* if the class is active, then done here */ diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 4d0e44a2e7c6..d7598214270b 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -138,8 +138,8 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, len = qdisc_pkt_len(skb); ret = qdisc_enqueue(skb, child, to_free); if (likely(ret == NET_XMIT_SUCCESS)) { - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); } else if (net_xmit_drop_count(ret)) { WRITE_ONCE(q->stats.pdrop, q->stats.pdrop + 1); @@ -166,7 +166,7 @@ static struct sk_buff *red_dequeue(struct Qdisc *sch) if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); } else { if (!red_is_idling(&q->vars)) red_start_of_idle_period(&q->vars); diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index d3ee8e5479b3..b1d465094276 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -415,8 +415,8 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, memcpy(&cb, sfb_skb_cb(skb), sizeof(cb)); ret = qdisc_enqueue(skb, child, to_free); if (likely(ret == NET_XMIT_SUCCESS)) { - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); increment_qlen(&cb, q); } else if (net_xmit_drop_count(ret)) { WRITE_ONCE(q->stats.childdrop, @@ -446,7 +446,7 @@ static struct sk_buff *sfb_dequeue(struct Qdisc *sch) if (skb) { qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); decrement_qlen(skb, q); } @@ -592,7 +592,7 @@ static int sfb_dump(struct Qdisc *sch, struct sk_buff *skb) .penalty_burst = q->penalty_burst, }; - sch->qstats.backlog = q->qdisc->qstats.backlog; + WRITE_ONCE(sch->qstats.backlog, READ_ONCE(q->qdisc->qstats.backlog)); opts = nla_nest_start_noflag(skb, TCA_OPTIONS); if (opts == NULL) goto nla_put_failure; diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index f39822babf88..758b88f21865 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -302,7 +302,7 @@ static unsigned int sfq_drop(struct Qdisc *sch, struct sk_buff **to_free) len = qdisc_pkt_len(skb); WRITE_ONCE(slot->backlog, slot->backlog - len); sfq_dec(q, x); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_OVERLIMIT); return len; @@ -427,7 +427,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) /* We know we have at least one packet in queue */ head = slot_dequeue_head(slot); delta = qdisc_pkt_len(head) - qdisc_pkt_len(skb); - sch->qstats.backlog -= delta; + qstats_backlog_sub(sch, delta); WRITE_ONCE(slot->backlog, slot->backlog - delta); qdisc_drop_reason(head, sch, to_free, QDISC_DROP_FLOW_LIMIT); @@ -456,7 +456,8 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) /* We could use a bigger initial quantum for new flows */ WRITE_ONCE(slot->allot, q->quantum); } - if (++sch->q.qlen <= q->limit) + qdisc_qlen_inc(sch); + if (sch->q.qlen <= q->limit) return NET_XMIT_SUCCESS; qlen = slot->qlen; @@ -497,7 +498,7 @@ sfq_dequeue(struct Qdisc *sch) skb = slot_dequeue_head(slot); sfq_dec(q, a); qdisc_bstats_update(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); WRITE_ONCE(slot->backlog, slot->backlog - qdisc_pkt_len(skb)); /* Is the slot empty? */ @@ -596,7 +597,7 @@ static void sfq_rehash(struct Qdisc *sch) WRITE_ONCE(slot->allot, q->quantum); } } - sch->q.qlen -= dropped; + WRITE_ONCE(sch->q.qlen, sch->q.qlen - dropped); qdisc_tree_reduce_backlog(sch, dropped, drop_len); } diff --git a/net/sched/sch_skbprio.c b/net/sched/sch_skbprio.c index f485f62ab721..52abfb4015a3 100644 --- a/net/sched/sch_skbprio.c +++ b/net/sched/sch_skbprio.c @@ -93,7 +93,7 @@ static int skbprio_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (prio < q->lowest_prio) q->lowest_prio = prio; - sch->q.qlen++; + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -145,7 +145,7 @@ static struct sk_buff *skbprio_dequeue(struct Qdisc *sch) if (unlikely(!skb)) return NULL; - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 45245157e00a..558987d9b977 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -308,10 +308,10 @@ static void taprio_update_queue_max_sdu(struct taprio_sched *q, if (max_sdu != U32_MAX) { sched->max_frm_len[tc] = max_sdu + dev->hard_header_len; - sched->max_sdu[tc] = max_sdu; + WRITE_ONCE(sched->max_sdu[tc], max_sdu); } else { sched->max_frm_len[tc] = U32_MAX; /* never oversized */ - sched->max_sdu[tc] = 0; + WRITE_ONCE(sched->max_sdu[tc], 0); } } } @@ -574,7 +574,7 @@ static int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch, } qdisc_qstats_backlog_inc(sch, skb); - sch->q.qlen++; + qdisc_qlen_inc(sch); return qdisc_enqueue(skb, child, to_free); } @@ -755,7 +755,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, qdisc_bstats_update(sch, skb); qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); return skb; } @@ -1299,7 +1299,7 @@ static void taprio_set_picos_per_byte(struct net_device *dev, int picos_per_byte; int err; - err = __ethtool_get_link_ksettings(dev, &ecmd); + err = netif_get_link_ksettings(dev, &ecmd); if (err < 0) goto skip; @@ -1321,7 +1321,7 @@ static void taprio_set_picos_per_byte(struct net_device *dev, atomic64_set(&q->picos_per_byte, picos_per_byte); netdev_dbg(dev, "taprio: set %s's picos_per_byte to: %lld, linkspeed: %d\n", dev->name, (long long)atomic64_read(&q->picos_per_byte), - ecmd.base.speed); + speed); } static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event, @@ -1771,8 +1771,8 @@ static int taprio_parse_tc_entries(struct Qdisc *sch, } for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) { - q->max_sdu[tc] = max_sdu[tc]; - q->fp[tc] = fp[tc]; + WRITE_ONCE(q->max_sdu[tc], max_sdu[tc]); + WRITE_ONCE(q->fp[tc], fp[tc]); if (fp[tc] != TC_FP_EXPRESS) have_preemption = true; } @@ -1852,12 +1852,14 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt, return -EINVAL; } - if (q->flags != TAPRIO_FLAGS_INVALID && q->flags != taprio_flags) { - NL_SET_ERR_MSG_MOD(extack, - "Changing 'flags' of a running schedule is not supported"); - return -EOPNOTSUPP; + if (q->flags != taprio_flags) { + if (q->flags != TAPRIO_FLAGS_INVALID) { + NL_SET_ERR_MSG_MOD(extack, + "Changing 'flags' of a running schedule is not supported"); + return -EOPNOTSUPP; + } + WRITE_ONCE(q->flags, taprio_flags); } - q->flags = taprio_flags; /* Needed for length_to_duration() during netlink attribute parsing */ taprio_set_picos_per_byte(dev, q, extack); @@ -1940,7 +1942,8 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt, goto unlock; } - q->txtime_delay = nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]); + WRITE_ONCE(q->txtime_delay, + nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY])); } if (!TXTIME_ASSIST_IS_ENABLED(q->flags) && @@ -2283,8 +2286,8 @@ static int dump_schedule(struct sk_buff *msg, } static int taprio_dump_tc_entries(struct sk_buff *skb, - struct taprio_sched *q, - struct sched_gate_list *sched) + const struct taprio_sched *q, + const struct sched_gate_list *sched) { struct nlattr *n; int tc; @@ -2298,10 +2301,11 @@ static int taprio_dump_tc_entries(struct sk_buff *skb, goto nla_put_failure; if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_MAX_SDU, - sched->max_sdu[tc])) + READ_ONCE(sched->max_sdu[tc]))) goto nla_put_failure; - if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_FP, q->fp[tc])) + if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_FP, + READ_ONCE(q->fp[tc]))) goto nla_put_failure; nla_nest_end(skb, n); @@ -2387,6 +2391,7 @@ static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb) struct sched_gate_list *oper, *admin; struct tc_mqprio_qopt opt = { 0 }; struct nlattr *nest, *sched_nest; + u32 txtime_delay; mqprio_qopt_reconstruct(dev, &opt); @@ -2404,14 +2409,15 @@ static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb) if (q->flags && nla_put_u32(skb, TCA_TAPRIO_ATTR_FLAGS, q->flags)) goto options_error; - if (q->txtime_delay && - nla_put_u32(skb, TCA_TAPRIO_ATTR_TXTIME_DELAY, q->txtime_delay)) + txtime_delay = READ_ONCE(q->txtime_delay); + if (txtime_delay && + nla_put_u32(skb, TCA_TAPRIO_ATTR_TXTIME_DELAY, txtime_delay)) goto options_error; rcu_read_lock(); - oper = rtnl_dereference(q->oper_sched); - admin = rtnl_dereference(q->admin_sched); + oper = rcu_dereference(q->oper_sched); + admin = rcu_dereference(q->admin_sched); if (oper && taprio_dump_tc_entries(skb, q, oper)) goto options_error_rcu; diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index f2340164f579..58c9687efb0f 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -139,7 +139,8 @@ static u64 psched_ns_t2l(const struct psched_ratecfg *r, return len; } -static void tbf_offload_change(struct Qdisc *sch) +static void tbf_offload_change(struct Qdisc *sch, + struct netlink_ext_ack *extack) { struct tbf_sched_data *q = qdisc_priv(sch); struct net_device *dev = qdisc_dev(sch); @@ -148,6 +149,7 @@ static void tbf_offload_change(struct Qdisc *sch) if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) return; + qopt.extack = extack; qopt.command = TC_TBF_REPLACE; qopt.handle = sch->handle; qopt.parent = sch->parent; @@ -166,6 +168,7 @@ static void tbf_offload_destroy(struct Qdisc *sch) if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) return; + qopt.extack = NULL; qopt.command = TC_TBF_DESTROY; qopt.handle = sch->handle; qopt.parent = sch->parent; @@ -176,6 +179,7 @@ static int tbf_offload_dump(struct Qdisc *sch) { struct tc_tbf_qopt_offload qopt; + qopt.extack = NULL; qopt.command = TC_TBF_STATS; qopt.handle = sch->handle; qopt.parent = sch->parent; @@ -193,6 +197,7 @@ static void tbf_offload_graft(struct Qdisc *sch, struct Qdisc *new, .parent = sch->parent, .child_handle = new->handle, .command = TC_TBF_GRAFT, + .extack = extack, }; qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old, @@ -231,8 +236,8 @@ static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch, len += seg_len; } } - sch->q.qlen += nb; - sch->qstats.backlog += len; + WRITE_ONCE(sch->q.qlen, sch->q.qlen + nb); + qstats_backlog_add(sch, len); if (nb > 0) { qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len); consume_skb(skb); @@ -263,8 +268,8 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch, return ret; } - sch->qstats.backlog += len; - sch->q.qlen++; + qstats_backlog_add(sch, len); + qdisc_qlen_inc(sch); return NET_XMIT_SUCCESS; } @@ -309,7 +314,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc *sch) q->tokens = toks; q->ptokens = ptoks; qdisc_qstats_backlog_dec(sch, skb); - sch->q.qlen--; + qdisc_qlen_dec(sch); qdisc_bstats_update(sch, skb); return skb; } @@ -477,7 +482,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt, qdisc_put(old); err = 0; - tbf_offload_change(sch); + tbf_offload_change(sch, extack); done: return err; } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index ec4039a201a2..e7bbc9e5174d 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -107,7 +107,7 @@ teql_dequeue(struct Qdisc *sch) } else { qdisc_bstats_update(sch, skb); } - sch->q.qlen = dat->q.qlen + q->q.qlen; + WRITE_ONCE(sch->q.qlen, dat->q.qlen + READ_ONCE(q->q.qlen)); return skb; } diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 8d342b514142..dfb1719275db 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -209,7 +209,7 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep) return; } - /* SCTP-AUTH: Free up AUTH releated data such as shared keys + /* SCTP-AUTH: Free up AUTH related data such as shared keys * chunks and hmacs arrays that were allocated */ sctp_auth_destroy_keys(&ep->endpoint_shared_keys); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index cd15b695607e..ef26878f1282 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -1176,11 +1176,17 @@ void sctp_v6_protosw_exit(void) /* Register with inet6 layer. */ int sctp_v6_add_protocol(void) { - /* Register notifier for inet6 address additions/deletions. */ - register_inet6addr_notifier(&sctp_inet6addr_notifier); + int ret; - if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) + /* Register notifier for inet6 address additions/deletions. */ + ret = register_inet6addr_notifier(&sctp_inet6addr_notifier); + if (ret) + return ret; + + if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) { + unregister_inet6addr_notifier(&sctp_inet6addr_notifier); return -EAGAIN; + } return 0; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 5800e7ee7ea0..587b0017a67d 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -864,7 +864,7 @@ int sctp_udp_sock_start(struct net *net) tuncfg.encap_type = 1; tuncfg.encap_rcv = sctp_udp_rcv; tuncfg.encap_err_lookup = sctp_udp_v4_err; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); net->sctp.udp4_sock = sock->sk; #if IS_ENABLED(CONFIG_IPV6) @@ -878,7 +878,7 @@ int sctp_udp_sock_start(struct net *net) err = udp_sock_create(net, &udp_conf, &sock); if (err) { pr_err("Failed to create the SCTP UDP tunneling v6 sock\n"); - udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket); + udp_tunnel_sock_release(net->sctp.udp4_sock); net->sctp.udp4_sock = NULL; return err; } @@ -886,7 +886,7 @@ int sctp_udp_sock_start(struct net *net) tuncfg.encap_type = 1; tuncfg.encap_rcv = sctp_udp_rcv; tuncfg.encap_err_lookup = sctp_udp_v6_err; - setup_udp_tunnel_sock(net, sock, &tuncfg); + setup_udp_tunnel_sock(net, sock->sk, &tuncfg); net->sctp.udp6_sock = sock->sk; #endif @@ -896,11 +896,11 @@ int sctp_udp_sock_start(struct net *net) void sctp_udp_sock_stop(struct net *net) { if (net->sctp.udp4_sock) { - udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket); + udp_tunnel_sock_release(net->sctp.udp4_sock); net->sctp.udp4_sock = NULL; } if (net->sctp.udp6_sock) { - udp_tunnel_sock_release(net->sctp.udp6_sock->sk_socket); + udp_tunnel_sock_release(net->sctp.udp6_sock); net->sctp.udp6_sock = NULL; } } @@ -1263,12 +1263,18 @@ static void sctp_v4_protosw_exit(void) static int sctp_v4_add_protocol(void) { + int ret; + /* Register notifier for inet address additions/deletions. */ - register_inetaddr_notifier(&sctp_inetaddr_notifier); + ret = register_inetaddr_notifier(&sctp_inetaddr_notifier); + if (ret) + return ret; /* Register SCTP with inet layer. */ - if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) + if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) { + unregister_inetaddr_notifier(&sctp_inetaddr_notifier); return -EAGAIN; + } return 0; } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 1741a9f33d8c..41958b8e59fd 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2641,6 +2641,9 @@ static int sctp_process_param(struct sctp_association *asoc, goto fall_through; addr_param = param.v + sizeof(struct sctp_addip_param); + if (ntohs(addr_param->p.length) > + ntohs(param.p->length) - sizeof(struct sctp_addip_param)) + break; af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (!af) @@ -3039,13 +3042,16 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, union sctp_addr addr; struct sctp_af *af; - addr_param = (void *)asconf_param + sizeof(*asconf_param); - if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY) return SCTP_ERROR_UNKNOWN_PARAM; + addr_param = (void *)asconf_param + sizeof(*asconf_param); + if (ntohs(addr_param->p.length) > + ntohs(asconf_param->param_hdr.length) - sizeof(*asconf_param)) + return SCTP_ERROR_PROTO_VIOLATION; + switch (addr_param->p.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index dea9270f3e57..b65b356da16b 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -406,7 +406,7 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, prev = xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); if (xa_err(prev)) { NL_SET_ERR_MSG(extack, "Can't insert shaper into device store"); - kfree_rcu(cur, rcu); + kfree(cur); ret = xa_err(prev); goto free_id; } @@ -429,7 +429,6 @@ static void net_shaper_commit(struct net_shaper_binding *binding, int index; int i; - xa_lock(&hierarchy->shapers); for (i = 0; i < nr_shapers; ++i) { index = net_shaper_handle_to_index(&shapers[i].handle); @@ -442,7 +441,6 @@ static void net_shaper_commit(struct net_shaper_binding *binding, /* ... publish to lockless readers. */ smp_store_release(&cur->valid, true); } - xa_unlock(&hierarchy->shapers); } /* Rollback all the tentative inserts from the hierarchy. */ @@ -455,14 +453,12 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) if (!hierarchy) return; - xa_lock(&hierarchy->shapers); xa_for_each(&hierarchy->shapers, index, cur) { if (cur->valid) continue; - __xa_erase(&hierarchy->shapers, index); + xa_erase(&hierarchy->shapers, index); kfree_rcu(cur, rcu); } - xa_unlock(&hierarchy->shapers); } static int net_shaper_parse_handle(const struct nlattr *attr, @@ -1456,6 +1452,8 @@ int net_shaper_nl_cap_get_dumpit(struct sk_buff *skb, ret = net_shaper_cap_fill_one(skb, binding, scope, flags, info); + /* cap dumps are tiny, we expect them to fit in a single skb */ + WARN_ON_ONCE(ret == -EMSGSIZE); if (ret) return ret; } @@ -1472,12 +1470,14 @@ static void net_shaper_flush(struct net_shaper_binding *binding) if (!hierarchy) return; - xa_lock(&hierarchy->shapers); xa_for_each(&hierarchy->shapers, index, cur) { - __xa_erase(&hierarchy->shapers, index); + xa_erase(&hierarchy->shapers, index); + /* No need to use kfree_rcu(), netdev is already unpublished, + * and synchronize_rcu() has been run as part of + * unregister_netdevice(). + */ kfree(cur); } - xa_unlock(&hierarchy->shapers); kfree(hierarchy); } diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index e2d083daeb7e..cf6b620fef05 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -2440,6 +2440,10 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb) /* use socket send buffer size (w/o overhead) as start value */ bufsize = smc->sk.sk_sndbuf / 2; + /* limit bufsize for physically contiguous buffers */ + if (!is_smcd && lgr->buf_type == SMCR_PHYS_CONT_BUFS) + bufsize = min_t(int, bufsize, PAGE_SIZE << MAX_PAGE_ORDER); + for (bufsize_comp = smc_compress_bufsize(bufsize, is_smcd, is_rmb); bufsize_comp >= 0; bufsize_comp--) { if (is_rmb) { diff --git a/net/smc/smc_ism.c b/net/smc/smc_ism.c index e0dba2c7b6e3..bde938c5eb39 100644 --- a/net/smc/smc_ism.c +++ b/net/smc/smc_ism.c @@ -467,17 +467,14 @@ static struct smcd_dev *smcd_alloc_dev(const char *name, int max_dmbs) { struct smcd_dev *smcd; - smcd = kzalloc_obj(*smcd); + smcd = kzalloc_flex(*smcd, conn, max_dmbs); if (!smcd) return NULL; - smcd->conn = kzalloc_objs(struct smc_connection *, max_dmbs); - if (!smcd->conn) - goto free_smcd; smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)", WQ_MEM_RECLAIM, name); if (!smcd->event_wq) - goto free_conn; + goto free_smcd; spin_lock_init(&smcd->lock); spin_lock_init(&smcd->lgr_lock); @@ -486,8 +483,6 @@ static struct smcd_dev *smcd_alloc_dev(const char *name, int max_dmbs) init_waitqueue_head(&smcd->lgrs_deleted); return smcd; -free_conn: - kfree(smcd->conn); free_smcd: kfree(smcd); return NULL; @@ -557,7 +552,6 @@ static void smcd_unregister_dev(struct dibs_dev *dibs) list_del_init(&smcd->list); mutex_unlock(&smcd_dev_list.mutex); destroy_workqueue(smcd->event_wq); - kfree(smcd->conn); kfree(smcd); } diff --git a/net/socket.c b/net/socket.c index 9e8dc769ff7a..63c69a0fa74e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2076,7 +2076,7 @@ static int __sys_accept4_file(struct file *file, struct sockaddr __user *upeer_s * we open the socket then return an error. * * 1003.1g adds the ability to recvmsg() to query connection pending - * status to recvmsg. We need to add that support in a way thats + * status. We need to add that support in a way that's * clean when we restructure accept also. */ diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index a3bd1ef17558..05dcd2f9e887 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -482,6 +482,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b) dev = (struct net_device *)rtnl_dereference(b->media_ptr); dev_remove_pack(&b->pt); RCU_INIT_POINTER(dev->tipc_ptr, NULL); + RCU_INIT_POINTER(b->media_ptr, NULL); synchronize_net(); dev_put(dev); } diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 190b49c5cbc3..ba4f4906e13b 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -280,12 +280,21 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, u32 node, u32 dtype) { struct publication *p = NULL; + u32 lower = ntohl(i->lower); + u32 upper = ntohl(i->upper); struct tipc_socket_addr sk; - struct tipc_uaddr ua; u32 key = ntohl(i->key); + struct tipc_uaddr ua; + + /* A peer-advertised binding with lower > upper can never be matched + * or withdrawn and would leak the publication; the local bind path + * rejects such ranges, so reject ranges learned from the network too. + */ + if (lower > upper) + return false; tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_CLUSTER_SCOPE, - ntohl(i->type), ntohl(i->lower), ntohl(i->upper)); + ntohl(i->type), lower, upper); sk.ref = ntohl(i->port); sk.node = node; diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 1a9a5bdaccf4..8336a9664703 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -152,11 +152,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_DISABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_disable, }, { .cmd = TIPC_NL_BEARER_ENABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_enable, }, { @@ -168,11 +170,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_ADD, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_add, }, { .cmd = TIPC_NL_BEARER_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_set, }, { @@ -197,11 +201,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_LINK_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_link, }, { .cmd = TIPC_NL_LINK_RESET_STATS, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_reset_link_stats, }, { @@ -213,6 +219,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MEDIA_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_media_set, }, { @@ -228,6 +235,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_NET_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_net_set, }, { @@ -238,6 +246,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MON_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_monitor, }, { @@ -255,6 +264,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_PEER_REMOVE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_peer_rm, }, #ifdef CONFIG_TIPC_MEDIA_UDP @@ -269,11 +279,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_KEY_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_key, }, { .cmd = TIPC_NL_KEY_FLUSH, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_flush_key, }, #endif diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9329919fb07f..e564341e0216 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -37,6 +37,7 @@ #include #include +#include #include #include "core.h" @@ -1362,6 +1363,9 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, __skb_queue_tail(xmitq, skb); return; } else if (mtyp == CONN_ACK) { + if (tsk->snt_unacked < msg_conn_ack(hdr)) + goto exit; + was_cong = tsk_conn_cong(tsk); tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr)); tsk->snt_unacked -= msg_conn_ack(hdr); @@ -2452,17 +2456,17 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, atomic_set(dcnt, 0); lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt); if (likely(!sk_add_backlog(sk, skb, lim))) { - trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_SK_BKLGQ, "bklg & rcvq >90% allocated!"); continue; } - trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!"); + trace_tipc_sk_dump(sk, skb, TIPC_DUMP_SK_BKLGQ, "err_overload!"); /* Overload => reject message back to sender */ onode = tipc_own_addr(sock_net(sk)); sk_drops_inc(sk); if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) { - trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_SK_BKLGQ, "@sk_enqueue!"); __skb_queue_tail(xmitq, skb); } @@ -3220,8 +3224,7 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, * @sock: socket structure * @lvl: option level * @opt: option identifier - * @ov: receptacle for option value - * @ol: receptacle for length of option value + * @sopt: socket option container (input buffer length, output value/length) * * For stream sockets only, returns 0 length result for all IPPROTO_TCP options * (to ease compatibility). @@ -3229,22 +3232,22 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, * Return: 0 on success, errno otherwise */ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, - char __user *ov, int __user *ol) + sockopt_t *sopt) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_service_range seq; int len, scope; + int res = 0; u32 value; - int res; - if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) - return put_user(0, ol); + if (lvl == IPPROTO_TCP && sock->type == SOCK_STREAM) { + sopt->optlen = 0; + return 0; + } if (lvl != SOL_TIPC) return -ENOPROTOOPT; - res = get_user(len, ol); - if (res) - return res; + len = sopt->optlen; lock_sock(sk); @@ -3260,7 +3263,6 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, break; case TIPC_CONN_TIMEOUT: value = tsk->conn_timeout; - /* no need to set "res", since already 0 at this point */ break; case TIPC_NODE_RECVQ_DEPTH: value = 0; /* was tipc_queue_size, now obsolete */ @@ -3284,15 +3286,17 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, release_sock(sk); if (res) - return res; /* "get" failed */ + return res; if (len < sizeof(value)) return -EINVAL; - if (copy_to_user(ov, &value, sizeof(value))) + if (copy_to_iter(&value, sizeof(value), &sopt->iter_out) != + sizeof(value)) return -EFAULT; + sopt->optlen = sizeof(value); - return put_user(sizeof(value), ol); + return 0; } static int tipc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) @@ -3365,7 +3369,7 @@ static const struct proto_ops msg_ops = { .listen = sock_no_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, - .getsockopt = tipc_getsockopt, + .getsockopt_iter = tipc_getsockopt, .sendmsg = tipc_sendmsg, .recvmsg = tipc_recvmsg, .mmap = sock_no_mmap, @@ -3385,7 +3389,7 @@ static const struct proto_ops packet_ops = { .listen = tipc_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, - .getsockopt = tipc_getsockopt, + .getsockopt_iter = tipc_getsockopt, .sendmsg = tipc_send_packet, .recvmsg = tipc_recvmsg, .mmap = sock_no_mmap, @@ -3405,7 +3409,7 @@ static const struct proto_ops stream_ops = { .listen = tipc_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, - .getsockopt = tipc_getsockopt, + .getsockopt_iter = tipc_getsockopt, .sendmsg = tipc_sendstream, .recvmsg = tipc_recvstream, .mmap = sock_no_mmap, diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 2c66b356025a..988b8a7f953a 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -89,14 +89,14 @@ struct udp_replicast { /** * struct udp_bearer - ip/udp bearer data structure * @bearer: associated generic tipc bearer - * @ubsock: bearer associated socket + * @sk: bearer associated socket * @ifindex: local address scope * @work: used to schedule deferred work on a bearer * @rcast: associated udp_replicast container */ struct udp_bearer { struct tipc_bearer __rcu *bearer; - struct socket *ubsock; + struct sock *sk; u32 ifindex; struct work_struct work; struct udp_replicast rcast; @@ -194,7 +194,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, } ttl = ip4_dst_hoplimit(&rt->dst); - udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr, + udp_tunnel_xmit_skb(rt, ub->sk, skb, src->ipv4.s_addr, dst->ipv4.s_addr, 0, ttl, 0, src->port, dst->port, false, true, 0); #if IS_ENABLED(CONFIG_IPV6) @@ -206,7 +206,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, .saddr = src->ipv6, .flowi6_proto = IPPROTO_UDP }; - ndst = ip6_dst_lookup_flow(net, ub->ubsock->sk, + ndst = ip6_dst_lookup_flow(net, ub->sk, &fl6, NULL); if (IS_ERR(ndst)) { err = PTR_ERR(ndst); @@ -215,7 +215,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, dst_cache_set_ip6(cache, ndst, &fl6.saddr); } ttl = ip6_dst_hoplimit(ndst); - udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL, + udp_tunnel6_xmit_skb(ndst, ub->sk, skb, NULL, &src->ipv6, &dst->ipv6, 0, ttl, 0, src->port, dst->port, false, 0); #endif @@ -405,9 +405,9 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb) static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) { - int err = 0; + struct sock *sk = ub->sk; struct ip_mreqn mreqn; - struct sock *sk = ub->ubsock->sk; + int err = 0; if (ntohs(remote->proto) == ETH_P_IP) { mreqn.imr_multiaddr = remote->ipv4; @@ -670,6 +670,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; u8 node_id[NODE_ID_LEN] = {0,}; struct net_device *dev; + struct socket *sock; int rmcast = 0; ub = kzalloc_obj(*ub, GFP_ATOMIC); @@ -764,14 +765,16 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, goto err; } udp_conf.local_udp_port = local.port; - err = udp_sock_create(net, &udp_conf, &ub->ubsock); + err = udp_sock_create(net, &udp_conf, &sock); if (err) goto err; + + ub->sk = sock->sk; tuncfg.sk_user_data = ub; tuncfg.encap_type = 1; tuncfg.encap_rcv = tipc_udp_recv; tuncfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg); + setup_udp_tunnel_sock(net, ub->sk, &tuncfg); err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC); if (err) @@ -793,7 +796,8 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, free: dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock); + udp_tunnel_sock_release(ub->sk); + synchronize_rcu(); err: kfree(ub); return err; @@ -812,10 +816,10 @@ static void cleanup_bearer(struct work_struct *work) kfree_rcu(rcast, rcu); } - tn = tipc_net(sock_net(ub->ubsock->sk)); + tn = tipc_net(sock_net(ub->sk)); dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock); + udp_tunnel_sock_release(ub->sk); /* Note: could use a call_rcu() to avoid another synchronize_net() */ synchronize_net(); @@ -833,11 +837,11 @@ static void tipc_udp_disable(struct tipc_bearer *b) pr_err("UDP bearer instance not found\n"); return; } - sock_set_flag(ub->ubsock->sk, SOCK_DEAD); + sock_set_flag(ub->sk, SOCK_DEAD); RCU_INIT_POINTER(ub->bearer, NULL); /* sock_release need to be done outside of rtnl lock */ - atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count); + atomic_inc(&tipc_net(sock_net(ub->sk))->wq_count); INIT_WORK(&ub->work, cleanup_bearer); schedule_work(&ub->work); } diff --git a/net/tls/Kconfig b/net/tls/Kconfig index a25bf57f2673..4f4d5973a28f 100644 --- a/net/tls/Kconfig +++ b/net/tls/Kconfig @@ -27,13 +27,3 @@ config TLS_DEVICE Enable kernel support for HW offload of the TLS protocol. If unsure, say N. - -config TLS_TOE - bool "Transport Layer Security TCP stack bypass" - depends on TLS - default n - help - Enable kernel support for legacy HW offload of the TLS protocol, - which is incompatible with the Linux networking stack semantics. - - If unsure, say N. diff --git a/net/tls/Makefile b/net/tls/Makefile index e41c800489ac..4c7d296081ee 100644 --- a/net/tls/Makefile +++ b/net/tls/Makefile @@ -9,5 +9,4 @@ obj-$(CONFIG_TLS) += tls.o tls-y := tls_main.o tls_sw.o tls_proc.o trace.o tls_strp.o -tls-$(CONFIG_TLS_TOE) += tls_toe.o tls-$(CONFIG_TLS_DEVICE) += tls_device.o tls_device_fallback.o diff --git a/net/tls/tls.h b/net/tls/tls.h index 12f44cb649c9..60a37bdaaa25 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -193,11 +193,11 @@ void tls_strp_stop(struct tls_strparser *strp); int tls_strp_init(struct tls_strparser *strp, struct sock *sk); void tls_strp_data_ready(struct tls_strparser *strp); -void tls_strp_check_rcv(struct tls_strparser *strp); -void tls_strp_msg_done(struct tls_strparser *strp); +void tls_strp_check_rcv(struct tls_strparser *strp, bool announce); +void tls_strp_msg_consume(struct tls_strparser *strp); int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb); -void tls_rx_msg_ready(struct tls_strparser *strp); +void tls_rx_msg_maybe_announce(struct tls_strparser *strp); bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh); int tls_strp_msg_cow(struct tls_sw_context_rx *ctx); diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 99c8eff9783e..741aef09bfd3 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1387,16 +1387,15 @@ static int tls_dev_event(struct notifier_block *this, unsigned long event, case NETDEV_FEAT_CHANGE: if (netif_is_bond_master(dev)) return NOTIFY_DONE; + if (!dev->tlsdev_ops || + !dev->tlsdev_ops->tls_dev_add || + !dev->tlsdev_ops->tls_dev_del) + return NOTIFY_BAD; if ((dev->features & NETIF_F_HW_TLS_RX) && !dev->tlsdev_ops->tls_dev_resync) return NOTIFY_BAD; - if (dev->tlsdev_ops && - dev->tlsdev_ops->tls_dev_add && - dev->tlsdev_ops->tls_dev_del) - return NOTIFY_DONE; - else - return NOTIFY_BAD; + return NOTIFY_DONE; case NETDEV_DOWN: return tls_device_down(dev); } diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index fd39acf41a61..8c588cdab733 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -43,8 +43,6 @@ #include #include -#include - #include "tls.h" MODULE_AUTHOR("Mellanox Technologies"); @@ -404,7 +402,6 @@ static __poll_t tls_sk_poll(struct file *file, struct socket *sock, struct tls_sw_context_rx *ctx; struct tls_context *tls_ctx; struct sock *sk = sock->sk; - struct sk_psock *psock; __poll_t mask = 0; u8 shutdown; int state; @@ -418,17 +415,12 @@ static __poll_t tls_sk_poll(struct file *file, struct socket *sock, tls_ctx = tls_get_ctx(sk); ctx = tls_sw_ctx_rx(tls_ctx); - psock = sk_psock_get(sk); if ((skb_queue_empty_lockless(&ctx->rx_list) && - !tls_strp_msg_ready(ctx) && - sk_psock_queue_empty(psock)) || + !tls_strp_msg_ready(ctx)) || READ_ONCE(ctx->key_update_pending)) mask &= ~(EPOLLIN | EPOLLRDNORM); - if (psock) - sk_psock_put(sk, psock); - return mask; } @@ -645,6 +637,17 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, int rc = 0; int conf; + /* TLS and sockmap are mutually exclusive. A socket already in a + * sockmap (i.e. with a psock attached) cannot be upgraded to TLS. + * sockmap rejects TLS sockets already (see sk_psock_init()). + */ + rcu_read_lock(); + if (sk_psock(sk)) { + rcu_read_unlock(); + return -EINVAL; + } + rcu_read_unlock(); + if (sockptr_is_null(optval) || (optlen < sizeof(*crypto_info))) return -EINVAL; @@ -769,7 +772,7 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, } else { struct tls_sw_context_rx *rx_ctx = tls_sw_ctx_rx(ctx); - tls_strp_check_rcv(&rx_ctx->strp); + tls_strp_check_rcv(&rx_ctx->strp, true); } return 0; @@ -963,9 +966,6 @@ static void build_proto_ops(struct proto_ops ops[TLS_NUM_CONFIG][TLS_NUM_CONFIG] ops[TLS_HW ][TLS_HW ] = ops[TLS_HW ][TLS_SW ]; #endif -#ifdef CONFIG_TLS_TOE - ops[TLS_HW_RECORD][TLS_HW_RECORD] = *base; -#endif } static void tls_build_proto(struct sock *sk) @@ -1037,11 +1037,6 @@ static void build_protos(struct proto prot[TLS_NUM_CONFIG][TLS_NUM_CONFIG], prot[TLS_HW][TLS_HW] = prot[TLS_HW][TLS_SW]; #endif -#ifdef CONFIG_TLS_TOE - prot[TLS_HW_RECORD][TLS_HW_RECORD] = *base; - prot[TLS_HW_RECORD][TLS_HW_RECORD].hash = tls_toe_hash; - prot[TLS_HW_RECORD][TLS_HW_RECORD].unhash = tls_toe_unhash; -#endif } static int tls_init(struct sock *sk) @@ -1051,11 +1046,6 @@ static int tls_init(struct sock *sk) tls_build_proto(sk); -#ifdef CONFIG_TLS_TOE - if (tls_toe_bypass(sk)) - return 0; -#endif - /* The TLS ulp is currently supported only for TCP sockets * in ESTABLISHED state. * Supporting sockets in LISTEN state will require us @@ -1111,8 +1101,6 @@ static u16 tls_user_config(struct tls_context *ctx, bool tx) return TLS_CONF_SW; case TLS_HW: return TLS_CONF_HW; - case TLS_HW_RECORD: - return TLS_CONF_HW_RECORD; } return 0; } diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index c72e88317627..61b10c697ecc 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -368,7 +368,6 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, desc->count = 0; WRITE_ONCE(strp->msg_ready, 1); - tls_rx_msg_ready(strp); } return ret; @@ -492,6 +491,7 @@ bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh) if (!strp->copy_mode && force_refresh) { if (unlikely(tcp_inq(strp->sk) < strp->stm.full_len)) { WRITE_ONCE(strp->msg_ready, 0); + strp->msg_announced = 0; memset(&strp->stm, 0, sizeof(strp->stm)); return false; } @@ -539,18 +539,24 @@ static int tls_strp_read_sock(struct tls_strparser *strp) return tls_strp_read_copy(strp, false); WRITE_ONCE(strp->msg_ready, 1); - tls_rx_msg_ready(strp); return 0; } -void tls_strp_check_rcv(struct tls_strparser *strp) +/* Parse queued data. When @announce is true and parsing produces a + * newly-ready record, fire the consumer notification. Callers that + * need to notify a waiter about a record parsed by another path + * should invoke tls_rx_msg_maybe_announce() directly. + */ +void tls_strp_check_rcv(struct tls_strparser *strp, bool announce) { if (unlikely(strp->stopped) || strp->msg_ready) return; if (tls_strp_read_sock(strp) == -ENOMEM) queue_work(tls_strp_wq, &strp->work); + else if (announce && strp->msg_ready) + tls_rx_msg_maybe_announce(strp); } /* Lower sock lock held */ @@ -568,7 +574,7 @@ void tls_strp_data_ready(struct tls_strparser *strp) return; } - tls_strp_check_rcv(strp); + tls_strp_check_rcv(strp, true); } static void tls_strp_work(struct work_struct *w) @@ -577,11 +583,16 @@ static void tls_strp_work(struct work_struct *w) container_of(w, struct tls_strparser, work); lock_sock(strp->sk); - tls_strp_check_rcv(strp); + tls_strp_check_rcv(strp, true); release_sock(strp->sk); } -void tls_strp_msg_done(struct tls_strparser *strp) +/* Release the current record without triggering a check for the + * next record. Callers must invoke tls_strp_check_rcv() before + * releasing the socket lock, or queued data will stall until the + * next tls_strp_data_ready() event. + */ +void tls_strp_msg_consume(struct tls_strparser *strp) { WARN_ON(!strp->stm.full_len); @@ -591,9 +602,8 @@ void tls_strp_msg_done(struct tls_strparser *strp) tls_strp_flush_anchor_copy(strp); WRITE_ONCE(strp->msg_ready, 0); + strp->msg_announced = 0; memset(&strp->stm, 0, sizeof(strp->stm)); - - tls_strp_check_rcv(strp); } void tls_strp_stop(struct tls_strparser *strp) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 964ebc268ee4..9324e4ed20a3 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -614,136 +614,17 @@ static int tls_do_encryption(struct sock *sk, return rc; } -static int tls_split_open_record(struct sock *sk, struct tls_rec *from, - struct tls_rec **to, struct sk_msg *msg_opl, - struct sk_msg *msg_oen, u32 split_point, - u32 tx_overhead_size, u32 *orig_end) -{ - u32 i, j, bytes = 0, apply = msg_opl->apply_bytes; - struct scatterlist *sge, *osge, *nsge; - u32 orig_size = msg_opl->sg.size; - struct scatterlist tmp = { }; - struct sk_msg *msg_npl; - struct tls_rec *new; - int ret; - - new = tls_get_rec(sk); - if (!new) - return -ENOMEM; - ret = sk_msg_alloc(sk, &new->msg_encrypted, msg_opl->sg.size + - tx_overhead_size, 0); - if (ret < 0) { - tls_free_rec(sk, new); - return ret; - } - - *orig_end = msg_opl->sg.end; - i = msg_opl->sg.start; - sge = sk_msg_elem(msg_opl, i); - while (apply && sge->length) { - if (sge->length > apply) { - u32 len = sge->length - apply; - - get_page(sg_page(sge)); - sg_set_page(&tmp, sg_page(sge), len, - sge->offset + apply); - sge->length = apply; - bytes += apply; - apply = 0; - } else { - apply -= sge->length; - bytes += sge->length; - } - - sk_msg_iter_var_next(i); - if (i == msg_opl->sg.end) - break; - sge = sk_msg_elem(msg_opl, i); - } - - msg_opl->sg.end = i; - msg_opl->sg.curr = i; - msg_opl->sg.copybreak = 0; - msg_opl->apply_bytes = 0; - msg_opl->sg.size = bytes; - - msg_npl = &new->msg_plaintext; - msg_npl->apply_bytes = apply; - msg_npl->sg.size = orig_size - bytes; - - j = msg_npl->sg.start; - nsge = sk_msg_elem(msg_npl, j); - if (tmp.length) { - memcpy(nsge, &tmp, sizeof(*nsge)); - sk_msg_iter_var_next(j); - nsge = sk_msg_elem(msg_npl, j); - } - - osge = sk_msg_elem(msg_opl, i); - while (osge->length) { - memcpy(nsge, osge, sizeof(*nsge)); - sg_unmark_end(nsge); - sk_msg_iter_var_next(i); - sk_msg_iter_var_next(j); - if (i == *orig_end) - break; - osge = sk_msg_elem(msg_opl, i); - nsge = sk_msg_elem(msg_npl, j); - } - - msg_npl->sg.end = j; - msg_npl->sg.curr = j; - msg_npl->sg.copybreak = 0; - - *to = new; - return 0; -} - -static void tls_merge_open_record(struct sock *sk, struct tls_rec *to, - struct tls_rec *from, u32 orig_end) -{ - struct sk_msg *msg_npl = &from->msg_plaintext; - struct sk_msg *msg_opl = &to->msg_plaintext; - struct scatterlist *osge, *nsge; - u32 i, j; - - i = msg_opl->sg.end; - sk_msg_iter_var_prev(i); - j = msg_npl->sg.start; - - osge = sk_msg_elem(msg_opl, i); - nsge = sk_msg_elem(msg_npl, j); - - if (sg_page(osge) == sg_page(nsge) && - osge->offset + osge->length == nsge->offset) { - osge->length += nsge->length; - put_page(sg_page(nsge)); - } - - msg_opl->sg.end = orig_end; - msg_opl->sg.curr = orig_end; - msg_opl->sg.copybreak = 0; - msg_opl->apply_bytes = msg_opl->sg.size + msg_npl->sg.size; - msg_opl->sg.size += msg_npl->sg.size; - - sk_msg_free(sk, &to->msg_encrypted); - sk_msg_xfer_full(&to->msg_encrypted, &from->msg_encrypted); - - kfree(from); -} - static int tls_push_record(struct sock *sk, int flags, unsigned char record_type) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_prot_info *prot = &tls_ctx->prot_info; struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx); - struct tls_rec *rec = ctx->open_rec, *tmp = NULL; - u32 i, split_point, orig_end; + struct tls_rec *rec = ctx->open_rec; struct sk_msg *msg_pl, *msg_en; struct aead_request *req; - bool split; int rc; + u32 i; if (!rec) return 0; @@ -751,38 +632,6 @@ static int tls_push_record(struct sock *sk, int flags, msg_pl = &rec->msg_plaintext; msg_en = &rec->msg_encrypted; - split_point = msg_pl->apply_bytes; - split = split_point && split_point < msg_pl->sg.size; - if (unlikely((!split && - msg_pl->sg.size + - prot->overhead_size > msg_en->sg.size) || - (split && - split_point + - prot->overhead_size > msg_en->sg.size))) { - split = true; - split_point = msg_en->sg.size; - } - if (split) { - rc = tls_split_open_record(sk, rec, &tmp, msg_pl, msg_en, - split_point, prot->overhead_size, - &orig_end); - if (rc < 0) - return rc; - /* This can happen if above tls_split_open_record allocates - * a single large encryption buffer instead of two smaller - * ones. In this case adjust pointers and continue without - * split. - */ - if (!msg_pl->sg.size) { - tls_merge_open_record(sk, rec, tmp, orig_end); - msg_pl = &rec->msg_plaintext; - msg_en = &rec->msg_encrypted; - split = false; - } - sk_msg_trim(sk, msg_en, msg_pl->sg.size + - prot->overhead_size); - } - rec->tx_flags = flags; req = &rec->aead_req; @@ -840,155 +689,26 @@ static int tls_push_record(struct sock *sk, int flags, rc = tls_do_encryption(sk, tls_ctx, ctx, req, msg_pl->sg.size + prot->tail_size, i); if (rc < 0) { - if (rc != -EINPROGRESS) { + if (rc != -EINPROGRESS) tls_err_abort(sk, -EBADMSG); - if (split) { - tls_ctx->pending_open_record_frags = true; - tls_merge_open_record(sk, rec, tmp, orig_end); - } - } ctx->async_capable = 1; return rc; - } else if (split) { - msg_pl = &tmp->msg_plaintext; - msg_en = &tmp->msg_encrypted; - sk_msg_trim(sk, msg_en, msg_pl->sg.size + prot->overhead_size); - tls_ctx->pending_open_record_frags = true; - ctx->open_rec = tmp; } return tls_tx_records(sk, flags); } static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk, - bool full_record, u8 record_type, - ssize_t *copied, int flags) + u8 record_type, ssize_t *copied, int flags) { - struct tls_context *tls_ctx = tls_get_ctx(sk); - struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx); - struct sk_msg msg_redir = { }; - struct sk_psock *psock; - struct sock *sk_redir; - struct tls_rec *rec; - bool enospc, policy, redir_ingress; - int err = 0, send; - u32 delta = 0; + int err; - policy = !(flags & MSG_SENDPAGE_NOPOLICY); - psock = sk_psock_get(sk); - if (!psock || !policy) { - err = tls_push_record(sk, flags, record_type); - if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { - *copied -= sk_msg_free(sk, msg); - tls_free_open_rec(sk); - err = -sk->sk_err; - } - if (psock) - sk_psock_put(sk, psock); - return err; + err = tls_push_record(sk, flags, record_type); + if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { + *copied -= sk_msg_free(sk, msg); + tls_free_open_rec(sk); + err = -sk->sk_err; } -more_data: - enospc = sk_msg_full(msg); - if (psock->eval == __SK_NONE) { - delta = msg->sg.size; - psock->eval = sk_psock_msg_verdict(sk, psock, msg); - delta -= msg->sg.size; - - if ((s32)delta > 0) { - /* It indicates that we executed bpf_msg_pop_data(), - * causing the plaintext data size to decrease. - * Therefore the encrypted data size also needs to - * correspondingly decrease. We only need to subtract - * delta to calculate the new ciphertext length since - * ktls does not support block encryption. - */ - struct sk_msg *enc = &ctx->open_rec->msg_encrypted; - - sk_msg_trim(sk, enc, enc->sg.size - delta); - } - } - if (msg->cork_bytes && msg->cork_bytes > msg->sg.size && - !enospc && !full_record) { - err = -ENOSPC; - goto out_err; - } - msg->cork_bytes = 0; - send = msg->sg.size; - if (msg->apply_bytes && msg->apply_bytes < send) - send = msg->apply_bytes; - - switch (psock->eval) { - case __SK_PASS: - err = tls_push_record(sk, flags, record_type); - if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { - *copied -= sk_msg_free(sk, msg); - tls_free_open_rec(sk); - err = -sk->sk_err; - goto out_err; - } - break; - case __SK_REDIRECT: - redir_ingress = psock->redir_ingress; - sk_redir = psock->sk_redir; - memcpy(&msg_redir, msg, sizeof(*msg)); - if (msg->apply_bytes < send) - msg->apply_bytes = 0; - else - msg->apply_bytes -= send; - sk_msg_return_zero(sk, msg, send); - msg->sg.size -= send; - release_sock(sk); - err = tcp_bpf_sendmsg_redir(sk_redir, redir_ingress, - &msg_redir, send, flags); - lock_sock(sk); - if (err < 0) { - /* Regardless of whether the data represented by - * msg_redir is sent successfully, we have already - * uncharged it via sk_msg_return_zero(). The - * msg->sg.size represents the remaining unprocessed - * data, which needs to be uncharged here. - */ - sk_mem_uncharge(sk, msg->sg.size); - *copied -= sk_msg_free_nocharge(sk, &msg_redir); - msg->sg.size = 0; - } - if (msg->sg.size == 0) - tls_free_open_rec(sk); - break; - case __SK_DROP: - default: - sk_msg_free_partial(sk, msg, send); - if (msg->apply_bytes < send) - msg->apply_bytes = 0; - else - msg->apply_bytes -= send; - if (msg->sg.size == 0) - tls_free_open_rec(sk); - *copied -= (send + delta); - err = -EACCES; - } - - if (likely(!err)) { - bool reset_eval = !ctx->open_rec; - - rec = ctx->open_rec; - if (rec) { - msg = &rec->msg_plaintext; - if (!msg->apply_bytes) - reset_eval = true; - } - if (reset_eval) { - psock->eval = __SK_NONE; - if (psock->sk_redir) { - sock_put(psock->sk_redir); - psock->sk_redir = NULL; - } - } - if (rec) - goto more_data; - } - out_err: - sk_psock_put(sk, psock); return err; } @@ -1008,7 +728,7 @@ static int tls_sw_push_pending_record(struct sock *sk, int flags) if (!copied) return 0; - return bpf_exec_tx_verdict(msg_pl, sk, true, TLS_RECORD_TYPE_DATA, + return bpf_exec_tx_verdict(msg_pl, sk, TLS_RECORD_TYPE_DATA, &copied, flags); } @@ -1156,7 +876,7 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, copied += try_to_copy; sk_msg_sg_copy_set(msg_pl, first); - ret = bpf_exec_tx_verdict(msg_pl, sk, full_record, + ret = bpf_exec_tx_verdict(msg_pl, sk, record_type, &copied, msg->msg_flags); if (ret) { @@ -1164,13 +884,7 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, num_async++; else if (ret == -ENOMEM) goto wait_for_memory; - else if (ctx->open_rec && ret == -ENOSPC) { - if (msg_pl->cork_bytes) { - ret = 0; - goto send_end; - } - goto rollback_iter; - } else if (ret != -EAGAIN) + else if (ret != -EAGAIN) goto send_end; } @@ -1181,11 +895,6 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, } continue; -rollback_iter: - copied -= try_to_copy; - sk_msg_sg_copy_clear(msg_pl, first); - iov_iter_revert(&msg->msg_iter, - msg_pl->sg.size - orig_size); fallback_to_reg_send: sk_msg_trim(sk, msg_pl, orig_size); } @@ -1221,7 +930,7 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, copied += try_to_copy; copied: if (full_record || eor) { - ret = bpf_exec_tx_verdict(msg_pl, sk, full_record, + ret = bpf_exec_tx_verdict(msg_pl, sk, record_type, &copied, msg->msg_flags); if (ret) { @@ -1229,11 +938,8 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, num_async++; else if (ret == -ENOMEM) goto wait_for_memory; - else if (ret != -EAGAIN) { - if (ret == -ENOSPC) - ret = 0; + else if (ret != -EAGAIN) goto send_end; - } } /* Transmit if any encryptions have completed */ @@ -1335,8 +1041,8 @@ void tls_sw_splice_eof(struct socket *sock) if (msg_pl->sg.size == 0) goto unlock; - /* Check the BPF advisor and perform transmission. */ - ret = bpf_exec_tx_verdict(msg_pl, sk, false, TLS_RECORD_TYPE_DATA, + /* Perform transmission. */ + ret = bpf_exec_tx_verdict(msg_pl, sk, TLS_RECORD_TYPE_DATA, &copied, 0); switch (ret) { case 0: @@ -1372,8 +1078,7 @@ void tls_sw_splice_eof(struct socket *sock) * consumed via sock_error(). */ static int -tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, - bool released, bool has_copied) +tls_rx_rec_wait(struct sock *sk, bool nonblock, bool released, bool has_copied) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); @@ -1388,9 +1093,6 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, timeo = sock_rcvtimeo(sk, nonblock); while (!tls_strp_msg_ready(ctx)) { - if (!sk_psock_queue_empty(psock)) - return 0; - if (sk->sk_err) { if (has_copied) return -READ_ONCE(sk->sk_err); @@ -1400,12 +1102,27 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, if (ret < 0) return ret; + if (sk_flush_backlog(sk)) + released = true; if (!skb_queue_empty(&sk->sk_receive_queue)) { - tls_strp_check_rcv(&ctx->strp); + /* Defer notification to the exit point; this thread + * will consume the record directly. + */ + tls_strp_check_rcv(&ctx->strp, false); if (tls_strp_msg_ready(ctx)) break; } + /* sk_flush_backlog() can run tcp_reset(), which sets + * sk_err and then sk_shutdown via tcp_done(). Recheck + * sk_err here so a connection abort surfaces as the + * actual error rather than a clean EOF. + */ + if (sk->sk_err) { + if (has_copied) + return -READ_ONCE(sk->sk_err); + return sock_error(sk); + } if (sk->sk_shutdown & RCV_SHUTDOWN) return 0; @@ -1419,9 +1136,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, add_wait_queue(sk_sleep(sk), &wait); sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); ret = sk_wait_event(sk, &timeo, - tls_strp_msg_ready(ctx) || - !sk_psock_queue_empty(psock), - &wait); + tls_strp_msg_ready(ctx), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); remove_wait_queue(sk_sleep(sk), &wait); @@ -1431,7 +1146,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, } if (unlikely(!tls_strp_msg_load(&ctx->strp, released))) - return tls_rx_rec_wait(sk, psock, nonblock, false, has_copied); + return tls_rx_rec_wait(sk, nonblock, false, has_copied); return 1; } @@ -1827,6 +1542,9 @@ static int tls_check_pending_rekey(struct sock *sk, struct tls_context *ctx, return 0; } +/* On decrypt failure the connection is aborted (sk_err set) before + * returning a negative errno. + */ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg, struct tls_decrypt_arg *darg) { @@ -1838,8 +1556,10 @@ static int tls_rx_one_record(struct sock *sk, struct msghdr *msg, err = tls_decrypt_device(sk, msg, tls_ctx, darg); if (!err) err = tls_decrypt_sw(sk, tls_ctx, msg, darg); - if (err < 0) + if (err < 0) { + tls_err_abort(sk, -EBADMSG); return err; + } rxm = strp_msg(darg->skb); rxm->offset += prot->prepend_size; @@ -1882,9 +1602,13 @@ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm, return 1; } +/* The deferred announce is fired once on reader exit by + * tls_rx_reader_release(). + */ static void tls_rx_rec_done(struct tls_sw_context_rx *ctx) { - tls_strp_msg_done(&ctx->strp); + tls_strp_msg_consume(&ctx->strp); + tls_strp_check_rcv(&ctx->strp, false); } /* This function traverses the rx_list in tls receive context to copies the @@ -2039,6 +1763,12 @@ static int tls_rx_reader_lock(struct sock *sk, struct tls_sw_context_rx *ctx, static void tls_rx_reader_release(struct sock *sk, struct tls_sw_context_rx *ctx) { + /* Fire any deferred announce once per reader so that a record + * parsed but not yet announced becomes visible to the next + * reader. The call is idempotent through msg_announced. + */ + tls_rx_msg_maybe_announce(&ctx->strp); + if (unlikely(ctx->reader_contended)) { if (wq_has_sleeper(&ctx->wq)) wake_up(&ctx->wq); @@ -2066,7 +1796,6 @@ int tls_sw_recvmsg(struct sock *sk, struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); struct tls_prot_info *prot = &tls_ctx->prot_info; ssize_t decrypted = 0, async_copy_bytes = 0; - struct sk_psock *psock; unsigned char control = 0; size_t flushed_at = 0; struct strp_msg *rxm; @@ -2079,7 +1808,6 @@ int tls_sw_recvmsg(struct sock *sk, bool is_peek = flags & MSG_PEEK; bool rx_more = false; bool released = true; - bool bpf_strp_enabled; bool zc_capable; if (unlikely(flags & MSG_ERRQUEUE)) @@ -2088,8 +1816,6 @@ int tls_sw_recvmsg(struct sock *sk, err = tls_rx_reader_lock(sk, ctx, flags & MSG_DONTWAIT); if (err < 0) return err; - psock = sk_psock_get(sk); - bpf_strp_enabled = sk_psock_strp_enabled(psock); /* If crypto failed the connection is broken */ err = ctx->async_wait.err; @@ -2110,27 +1836,16 @@ int tls_sw_recvmsg(struct sock *sk, target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); len = len - copied; - zc_capable = !bpf_strp_enabled && !is_kvec && !is_peek && - ctx->zc_capable; + zc_capable = !is_kvec && !is_peek && ctx->zc_capable; decrypted = 0; while (len && (decrypted + copied < target || tls_strp_msg_ready(ctx))) { struct tls_decrypt_arg darg; int to_decrypt, chunk; - err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, + err = tls_rx_rec_wait(sk, flags & MSG_DONTWAIT, released, !!(decrypted + copied)); - if (err <= 0) { - if (psock) { - chunk = sk_msg_recvmsg(sk, psock, msg, len, - flags); - if (chunk > 0) { - decrypted += chunk; - len -= chunk; - continue; - } - } + if (err <= 0) goto recv_end; - } memset(&darg.inargs, 0, sizeof(darg.inargs)); @@ -2144,16 +1859,14 @@ int tls_sw_recvmsg(struct sock *sk, darg.zc = true; /* Do not use async mode if record is non-data */ - if (tlm->control == TLS_RECORD_TYPE_DATA && !bpf_strp_enabled) + if (tlm->control == TLS_RECORD_TYPE_DATA) darg.async = ctx->async_capable; else darg.async = false; err = tls_rx_one_record(sk, msg, &darg); - if (err < 0) { - tls_err_abort(sk, -EBADMSG); + if (err < 0) goto recv_end; - } async |= darg.async; @@ -2202,18 +1915,6 @@ int tls_sw_recvmsg(struct sock *sk, continue; } - if (bpf_strp_enabled) { - released = true; - err = sk_psock_tls_strp_read(psock, skb); - if (err != __SK_PASS) { - rxm->offset = rxm->offset + rxm->full_len; - rxm->full_len = 0; - if (err == __SK_DROP) - consume_skb(skb); - continue; - } - } - if (partially_consumed) chunk = len; @@ -2276,8 +1977,6 @@ int tls_sw_recvmsg(struct sock *sk, end: tls_rx_reader_unlock(sk, ctx); - if (psock) - sk_psock_put(sk, psock); return copied ? : err; } @@ -2304,7 +2003,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, } else { struct tls_decrypt_arg darg; - err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, + err = tls_rx_rec_wait(sk, flags & SPLICE_F_NONBLOCK, true, false); if (err <= 0) goto splice_read_end; @@ -2312,10 +2011,8 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, memset(&darg.inargs, 0, sizeof(darg.inargs)); err = tls_rx_one_record(sk, NULL, &darg); - if (err < 0) { - tls_err_abort(sk, -EBADMSG); + if (err < 0) goto splice_read_end; - } tls_rx_rec_done(ctx); skb = darg.skb; @@ -2383,7 +2080,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_end; decrypted = 0; - do { + while (desc->count) { if (!skb_queue_empty(&ctx->rx_list)) { skb = __skb_dequeue(&ctx->rx_list); rxm = strp_msg(skb); @@ -2391,17 +2088,15 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, } else { struct tls_decrypt_arg darg; - err = tls_rx_rec_wait(sk, NULL, true, released, !!copied); + err = tls_rx_rec_wait(sk, true, released, !!copied); if (err <= 0) goto read_sock_end; memset(&darg.inargs, 0, sizeof(darg.inargs)); err = tls_rx_one_record(sk, NULL, &darg); - if (err < 0) { - tls_err_abort(sk, -EBADMSG); + if (err < 0) goto read_sock_end; - } released = tls_read_flush_backlog(sk, prot, INT_MAX, 0, decrypted, @@ -2430,14 +2125,11 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, if (used < rxm->full_len) { rxm->offset += used; rxm->full_len -= used; - if (!desc->count) - goto read_sock_requeue; + __skb_queue_head(&ctx->rx_list, skb); } else { consume_skb(skb); - if (!desc->count) - skb = NULL; } - } while (skb); + } read_sock_end: tls_rx_reader_release(sk, ctx); @@ -2452,16 +2144,8 @@ bool tls_sw_sock_is_readable(struct sock *sk) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - bool ingress_empty = true; - struct sk_psock *psock; - rcu_read_lock(); - psock = sk_psock(sk); - if (psock) - ingress_empty = list_empty(&psock->ingress_msg); - rcu_read_unlock(); - - return !ingress_empty || tls_strp_msg_ready(ctx) || + return tls_strp_msg_ready(ctx) || !skb_queue_empty(&ctx->rx_list); } @@ -2524,10 +2208,18 @@ int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb) return ret; } -void tls_rx_msg_ready(struct tls_strparser *strp) +/* Fire saved_data_ready() at most once per parsed record. The + * msg_announced bit is cleared by tls_strp_msg_consume() when the + * record is consumed, arming the next announcement. + */ +void tls_rx_msg_maybe_announce(struct tls_strparser *strp) { struct tls_sw_context_rx *ctx; + if (!READ_ONCE(strp->msg_ready) || strp->msg_announced) + return; + strp->msg_announced = 1; + ctx = container_of(strp, struct tls_sw_context_rx, strp); ctx->saved_data_ready(strp->sk); } @@ -2536,7 +2228,6 @@ static void tls_data_ready(struct sock *sk) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - struct sk_psock *psock; gfp_t alloc_save; trace_sk_data_ready(sk); @@ -2545,13 +2236,6 @@ static void tls_data_ready(struct sock *sk) sk->sk_allocation = GFP_ATOMIC; tls_strp_data_ready(&ctx->strp); sk->sk_allocation = alloc_save; - - psock = sk_psock_get(sk); - if (psock) { - if (!list_empty(&psock->ingress_msg)) - ctx->saved_data_ready(sk); - sk_psock_put(sk, psock); - } } void tls_sw_cancel_work_tx(struct tls_context *tls_ctx) diff --git a/net/tls/tls_toe.c b/net/tls/tls_toe.c deleted file mode 100644 index 825669e1ab47..000000000000 --- a/net/tls/tls_toe.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. - * Copyright (c) 2016-2017, Dave Watson . All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include "tls.h" - -static LIST_HEAD(device_list); -static DEFINE_SPINLOCK(device_spinlock); - -static void tls_toe_sk_destruct(struct sock *sk) -{ - struct inet_connection_sock *icsk = inet_csk(sk); - struct tls_context *ctx = tls_get_ctx(sk); - - ctx->sk_destruct(sk); - /* Free ctx */ - rcu_assign_pointer(icsk->icsk_ulp_data, NULL); - tls_ctx_free(sk, ctx); -} - -int tls_toe_bypass(struct sock *sk) -{ - struct tls_toe_device *dev; - struct tls_context *ctx; - int rc = 0; - - spin_lock_bh(&device_spinlock); - list_for_each_entry(dev, &device_list, dev_list) { - if (dev->feature && dev->feature(dev)) { - ctx = tls_ctx_create(sk); - if (!ctx) - goto out; - - ctx->sk_destruct = sk->sk_destruct; - sk->sk_destruct = tls_toe_sk_destruct; - ctx->rx_conf = TLS_HW_RECORD; - ctx->tx_conf = TLS_HW_RECORD; - update_sk_prot(sk, ctx); - rc = 1; - break; - } - } -out: - spin_unlock_bh(&device_spinlock); - return rc; -} - -void tls_toe_unhash(struct sock *sk) -{ - struct tls_context *ctx = tls_get_ctx(sk); - struct tls_toe_device *dev; - - spin_lock_bh(&device_spinlock); - list_for_each_entry(dev, &device_list, dev_list) { - if (dev->unhash) { - kref_get(&dev->kref); - spin_unlock_bh(&device_spinlock); - dev->unhash(dev, sk); - kref_put(&dev->kref, dev->release); - spin_lock_bh(&device_spinlock); - } - } - spin_unlock_bh(&device_spinlock); - ctx->sk_proto->unhash(sk); -} - -int tls_toe_hash(struct sock *sk) -{ - struct tls_context *ctx = tls_get_ctx(sk); - struct tls_toe_device *dev; - int err; - - err = ctx->sk_proto->hash(sk); - spin_lock_bh(&device_spinlock); - list_for_each_entry(dev, &device_list, dev_list) { - if (dev->hash) { - kref_get(&dev->kref); - spin_unlock_bh(&device_spinlock); - err |= dev->hash(dev, sk); - kref_put(&dev->kref, dev->release); - spin_lock_bh(&device_spinlock); - } - } - spin_unlock_bh(&device_spinlock); - - if (err) - tls_toe_unhash(sk); - return err; -} - -void tls_toe_register_device(struct tls_toe_device *device) -{ - spin_lock_bh(&device_spinlock); - list_add_tail(&device->dev_list, &device_list); - spin_unlock_bh(&device_spinlock); -} -EXPORT_SYMBOL(tls_toe_register_device); - -void tls_toe_unregister_device(struct tls_toe_device *device) -{ - spin_lock_bh(&device_spinlock); - list_del(&device->dev_list); - spin_unlock_bh(&device_spinlock); -} -EXPORT_SYMBOL(tls_toe_unregister_device); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 0d9cd977c7b7..f7a9d55eee8a 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1142,8 +1142,6 @@ static int unix_create(struct net *net, struct socket *sock, int protocol, if (protocol && protocol != PF_UNIX) return -EPROTONOSUPPORT; - sock->state = SS_UNCONNECTED; - switch (sock->type) { case SOCK_STREAM: set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags); @@ -1775,7 +1773,6 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr_unsized *uad /* Set credentials */ copy_peercred(sk, other); - sock->state = SS_CONNECTED; WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED); sock_hold(newsk); @@ -1831,8 +1828,7 @@ static int unix_socketpair(struct socket *socka, struct socket *sockb) ska->sk_state = TCP_ESTABLISHED; skb->sk_state = TCP_ESTABLISHED; - socka->state = SS_CONNECTED; - sockb->state = SS_CONNECTED; + return 0; } @@ -1874,7 +1870,6 @@ static int unix_accept(struct socket *sock, struct socket *newsock, /* attach accepted sock to socket */ unix_state_lock(tsk); unix_update_edges(unix_sk(tsk)); - newsock->state = SS_CONNECTED; sock_graft(tsk, newsock); unix_state_unlock(tsk); return 0; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 2ce1063d4a67..622dbd046799 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -155,6 +155,7 @@ #include #include #include +#include #include #include #include @@ -483,6 +484,7 @@ void vsock_add_pending(struct sock *listener, struct sock *pending) sock_hold(pending); sock_hold(listener); list_add_tail(&vpending->pending_links, &vlistener->pending_links); + sk_acceptq_added(listener); } EXPORT_SYMBOL_GPL(vsock_add_pending); @@ -493,9 +495,20 @@ void vsock_remove_pending(struct sock *listener, struct sock *pending) list_del_init(&vpending->pending_links); sock_put(listener); sock_put(pending); + sk_acceptq_removed(listener); } EXPORT_SYMBOL_GPL(vsock_remove_pending); +void vsock_pending_to_accept(struct sock *listener, struct sock *pending) +{ + struct vsock_sock *vpending = vsock_sk(pending); + struct vsock_sock *vlistener = vsock_sk(listener); + + list_del_init(&vpending->pending_links); + list_add_tail(&vpending->accept_queue, &vlistener->accept_queue); +} +EXPORT_SYMBOL_GPL(vsock_pending_to_accept); + void vsock_enqueue_accept(struct sock *listener, struct sock *connected) { struct vsock_sock *vlistener; @@ -507,6 +520,7 @@ void vsock_enqueue_accept(struct sock *listener, struct sock *connected) sock_hold(connected); sock_hold(listener); list_add_tail(&vconnected->accept_queue, &vlistener->accept_queue); + sk_acceptq_added(listener); } EXPORT_SYMBOL_GPL(vsock_enqueue_accept); @@ -760,8 +774,6 @@ static void vsock_pending_work(struct work_struct *work) if (vsock_is_pending(sk)) { vsock_remove_pending(listener, sk); - - sk_acceptq_removed(listener); } else if (!vsk->rejected) { /* We are not on the pending list and accept() did not reject * us, so we must have been accepted by our user process. We @@ -2106,8 +2118,7 @@ static int vsock_connectible_setsockopt(struct socket *sock, static int vsock_connectible_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, - int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct vsock_sock *vsk = vsock_sk(sk); @@ -2125,8 +2136,7 @@ static int vsock_connectible_getsockopt(struct socket *sock, if (level != AF_VSOCK) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; memset(&v, 0, sizeof(v)); @@ -2157,11 +2167,10 @@ static int vsock_connectible_getsockopt(struct socket *sock, return -EINVAL; if (len > lv) len = lv; - if (copy_to_user(optval, &v, len)) + if (copy_to_iter(&v, len, &opt->iter_out) != len) return -EFAULT; - if (put_user(len, optlen)) - return -EFAULT; + opt->optlen = len; return 0; } @@ -2646,7 +2655,7 @@ static const struct proto_ops vsock_stream_ops = { .listen = vsock_listen, .shutdown = vsock_shutdown, .setsockopt = vsock_connectible_setsockopt, - .getsockopt = vsock_connectible_getsockopt, + .getsockopt_iter = vsock_connectible_getsockopt, .sendmsg = vsock_connectible_sendmsg, .recvmsg = vsock_connectible_recvmsg, .mmap = sock_no_mmap, @@ -2668,7 +2677,7 @@ static const struct proto_ops vsock_seqpacket_ops = { .listen = vsock_listen, .shutdown = vsock_shutdown, .setsockopt = vsock_connectible_setsockopt, - .getsockopt = vsock_connectible_getsockopt, + .getsockopt_iter = vsock_connectible_getsockopt, .sendmsg = vsock_connectible_sendmsg, .recvmsg = vsock_connectible_recvmsg, .mmap = sock_no_mmap, diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c index b3394946b2ed..5405c2680dec 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -323,7 +323,7 @@ static void hvs_open_connection(struct vmbus_channel *chan) goto out; if (conn_from_host) { - if (sk->sk_ack_backlog >= sk->sk_max_ack_backlog) + if (sk_acceptq_is_full(sk)) goto out; new = vsock_create_connected(sk); @@ -410,7 +410,6 @@ static void hvs_open_connection(struct vmbus_channel *chan) if (conn_from_host) { new->sk_state = TCP_ESTABLISHED; - sk_acceptq_added(sk); hvs_new->vm_srv_id = *if_type; hvs_new->host_srv_id = *if_instance; diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index b10666937c49..09475007165b 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -423,7 +423,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, u32 len) { - u64 skb_overhead = ((u64)skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0); + u64 skb_overhead = (skb_queue_len(&vvs->rx_queue) + 1) * SKB_TRUESIZE(0); /* Allow at most buf_alloc * 2 total budget (payload + overhead), * similar to how SO_RCVBUF is doubled to reserve space for sk_buff @@ -1582,7 +1582,6 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb, return ret; } - sk_acceptq_added(sk); if (virtio_transport_space_update(child, skb)) child->sk_write_space(child); diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 91516488a742..1c4ee039c166 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -980,10 +980,8 @@ static int vmci_transport_recv_listen(struct sock *sk, err = -EINVAL; } - if (err < 0) { + if (err < 0) vsock_remove_pending(sk, pending); - sk_acceptq_removed(sk); - } release_sock(pending); vmci_transport_release_pending(pending); @@ -1010,7 +1008,7 @@ static int vmci_transport_recv_listen(struct sock *sk, * reset. Otherwise we create and initialize a child socket and reply * with a connection negotiation. */ - if (sk->sk_ack_backlog >= sk->sk_max_ack_backlog) { + if (sk_acceptq_is_full(sk)) { vmci_transport_reply_reset(pkt); return -ECONNREFUSED; } @@ -1109,7 +1107,6 @@ static int vmci_transport_recv_listen(struct sock *sk, } vsock_add_pending(sk, pending); - sk_acceptq_added(sk); pending->sk_state = TCP_SYN_SENT; vmci_trans(vpending)->produce_size = @@ -1258,8 +1255,7 @@ vmci_transport_recv_connecting_server(struct sock *listener, * listener's pending list to the accept queue so callers of accept() * can find it. */ - vsock_remove_pending(listener, pending); - vsock_enqueue_accept(listener, pending); + vsock_pending_to_accept(listener, pending); /* Callers of accept() will be waiting on the listening socket, not * the pending socket. diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 8b94c0de80ad..7f6af1790736 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -138,9 +138,10 @@ static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = { CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320) }; -static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef) +static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef, + u32 primary_center, u32 punctured) { - u32 idx, i, start_freq, primary_center = chandef->chan->center_freq; + u32 idx, i, start_freq; switch (chandef->width) { case NL80211_CHAN_WIDTH_80: @@ -156,18 +157,18 @@ static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef) start_freq = chandef->center_freq1 - 160; break; default: - return chandef->punctured == 0; + return punctured == 0; } - if (!chandef->punctured) + if (!punctured) return true; /* check if primary channel is punctured */ - if (chandef->punctured & (u16)BIT((primary_center - start_freq) / 20)) + if (punctured & (u16)BIT((primary_center - start_freq) / 20)) return false; for (i = 0; i < per_bw_puncturing[idx].len; i++) { - if (per_bw_puncturing[idx].valid_values[i] == chandef->punctured) + if (per_bw_puncturing[idx].valid_values[i] == punctured) return true; } @@ -279,12 +280,6 @@ int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width) case NL80211_CHAN_WIDTH_16: mhz = 16; break; - case NL80211_CHAN_WIDTH_5: - mhz = 5; - break; - case NL80211_CHAN_WIDTH_10: - mhz = 10; - break; case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: mhz = 20; @@ -346,8 +341,6 @@ cfg80211_chandef_valid_control_freq(const struct cfg80211_chan_def *chandef, u32 control_freq) { switch (chandef->width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_1: @@ -414,8 +407,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) return false; switch (chandef->width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: if (ieee80211_chandef_to_khz(chandef) != @@ -458,6 +449,19 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) if (!cfg80211_chandef_valid_control_freq(chandef, control_freq)) return false; + if (chandef->npca_chan) { + switch (chandef->width) { + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_320: + break; + default: + return false; + } + } else if (chandef->npca_punctured) { + return false; + } + if (!cfg80211_valid_center_freq(chandef->center_freq1, chandef->width)) return false; @@ -477,7 +481,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz) return false; - return valid_puncturing_bitmap(chandef); + return valid_puncturing_bitmap(chandef, control_freq, + chandef->punctured); } EXPORT_SYMBOL(cfg80211_chandef_valid); @@ -520,6 +525,220 @@ int cfg80211_chandef_primary(const struct cfg80211_chan_def *c, } EXPORT_SYMBOL(cfg80211_chandef_primary); +bool cfg80211_chandef_npca_valid(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + const struct ieee80211_uhr_npca_info *npca) +{ + struct cfg80211_chan_def tmp = *chandef; + bool pri_upper, npca_upper; + u32 cf1; + + if (chandef->npca_chan || chandef->npca_punctured) + return false; + + if (!npca) + return true; + + if (cfg80211_chandef_add_npca(wiphy, &tmp, npca)) + return false; + + if (!cfg80211_chandef_valid_control_freq(&tmp, + tmp.npca_chan->center_freq)) + return false; + + cf1 = tmp.center_freq1; + pri_upper = tmp.chan->center_freq > cf1; + npca_upper = tmp.npca_chan->center_freq > cf1; + + if (pri_upper == npca_upper) + return false; + + if (!valid_puncturing_bitmap(&tmp, + tmp.npca_chan->center_freq, + tmp.npca_punctured) || + (tmp.punctured & tmp.npca_punctured) != tmp.punctured) + return false; + + return true; +} +EXPORT_SYMBOL(cfg80211_chandef_npca_valid); + +int cfg80211_chandef_add_npca(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + const struct ieee80211_uhr_npca_info *npca) +{ + struct cfg80211_chan_def new_chandef = *chandef; + u32 width, npca_freq; + u8 offs; + + if (chandef->npca_chan || chandef->npca_punctured) + return -EINVAL; + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return -EINVAL; + + if (!npca) + return 0; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_320: + break; + default: + return -EINVAL; + } + + offs = le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS); + + width = cfg80211_chandef_get_width(chandef); + npca_freq = chandef->center_freq1 - width / 2 + 10 + 20 * offs; + new_chandef.npca_chan = ieee80211_get_channel(wiphy, npca_freq); + if (!new_chandef.npca_chan) + return -EINVAL; + + if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)) + new_chandef.npca_punctured = le16_to_cpu(npca->dis_subch_bmap[0]); + + if (!cfg80211_chandef_valid(&new_chandef)) + return -EINVAL; + + *chandef = new_chandef; + return 0; +} +EXPORT_SYMBOL(cfg80211_chandef_add_npca); + +int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef, + const struct ieee80211_uhr_dbe_info *dbe) +{ + struct cfg80211_chan_def new_chandef = *chandef; + u16 starting_freq, bw_mhz, start_old, start_new; + u8 bw, punct_shift; + int offset, index; + + if (!dbe) + return 0; + + if (!cfg80211_chandef_valid(chandef)) + return -EINVAL; + + if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT) + return -EINVAL; + + bw = u8_get_bits(dbe->params, IEEE80211_UHR_DBE_OPER_BANDWIDTH); + + switch (chandef->chan->band) { + case NL80211_BAND_5GHZ: + if (bw > IEEE80211_UHR_DBE_OPER_BW_160) + return -EINVAL; + if (chandef->chan->center_freq < 5745) + starting_freq = 5180; /* channel 36 */ + else + starting_freq = 5745; /* channel 149 */ + break; + case NL80211_BAND_6GHZ: + starting_freq = 5955; /* channel 1 center */ + break; + default: + return -EINVAL; + } + + switch (bw) { + case IEEE80211_UHR_DBE_OPER_BW_320_2: + case IEEE80211_UHR_DBE_OPER_BW_320_1: + if (chandef->width == NL80211_CHAN_WIDTH_160) + break; + fallthrough; + case IEEE80211_UHR_DBE_OPER_BW_160: + if (chandef->width == NL80211_CHAN_WIDTH_80) + break; + fallthrough; + case IEEE80211_UHR_DBE_OPER_BW_80: + if (chandef->width == NL80211_CHAN_WIDTH_40) + break; + fallthrough; + case IEEE80211_UHR_DBE_OPER_BW_40: + if (chandef->width == NL80211_CHAN_WIDTH_20) + break; + fallthrough; + default: + return -EINVAL; + } + + switch (bw) { + case IEEE80211_UHR_DBE_OPER_BW_320_2: + /* 320-2 starts shifted by 160 */ + starting_freq += 160; + fallthrough; + case IEEE80211_UHR_DBE_OPER_BW_320_1: + new_chandef.width = NL80211_CHAN_WIDTH_320; + bw_mhz = 320; + break; + case IEEE80211_UHR_DBE_OPER_BW_160: + new_chandef.width = NL80211_CHAN_WIDTH_160; + bw_mhz = 160; + break; + case IEEE80211_UHR_DBE_OPER_BW_80: + new_chandef.width = NL80211_CHAN_WIDTH_80; + bw_mhz = 80; + break; + case IEEE80211_UHR_DBE_OPER_BW_40: + new_chandef.width = NL80211_CHAN_WIDTH_40; + bw_mhz = 40; + break; + } + + /* this should only happen for 320-2 and misconfigured AP */ + if (chandef->chan->center_freq < starting_freq) + return -EINVAL; + + offset = chandef->chan->center_freq - starting_freq; + index = offset / bw_mhz; + start_new = starting_freq - 10 + index * bw_mhz; + new_chandef.center_freq1 = start_new + bw_mhz / 2; + + start_old = chandef->center_freq1 - + cfg80211_chandef_get_width(chandef) / 2; + + /* + * If the DBE channel extends downward below the lower + * edge of the BSS channel, we need to shift puncturing + * bitmaps up to adjust for that. + */ + if (start_new < start_old) + punct_shift = (start_old - start_new) / 20; + else + punct_shift = 0; + + new_chandef.punctured <<= punct_shift; + new_chandef.npca_punctured <<= punct_shift; + + if (dbe->params & IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES) { + u16 punct_mask = ((1 << (bw_mhz / 40)) - 1) << punct_shift; + u16 punctured = le16_to_cpu(dbe->dis_subch_bmap[0]); + + if ((punctured & punct_mask) != (new_chandef.punctured & punct_mask)) + return -EINVAL; + + new_chandef.punctured = punctured; + } + + if (!cfg80211_chandef_valid(&new_chandef)) + return -EINVAL; + + /* + * If e.g. a 40 MHz BSS channel (erroneously) occupies the center of the + * DBE 80 MHz channel, they would be incompatible; check and reject. + */ + if (!cfg80211_chandef_compatible(&new_chandef, chandef)) + return -EINVAL; + + *chandef = new_chandef; + return 0; +} +EXPORT_SYMBOL(cfg80211_chandef_add_dbe); + static const struct cfg80211_chan_def * check_chandef_primary_compat(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2, @@ -565,18 +784,25 @@ _cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, return NULL; /* - * can't be compatible if one of them is 5/10 MHz or S1G + * We need NPCA to be compatible for some scenarios such as + * multiple APs, but in this case userspace should configure + * identical chandefs including NPCA, even if perhaps one of + * the AP interfaces doesn't even advertise it. + */ + if (c1->npca_chan || c2->npca_chan) + return NULL; + + /* + * can't be compatible if one of them is S1G * but they don't have the same width. */ -#define NARROW_OR_S1G(width) ((width) == NL80211_CHAN_WIDTH_5 || \ - (width) == NL80211_CHAN_WIDTH_10 || \ - (width) == NL80211_CHAN_WIDTH_1 || \ - (width) == NL80211_CHAN_WIDTH_2 || \ - (width) == NL80211_CHAN_WIDTH_4 || \ - (width) == NL80211_CHAN_WIDTH_8 || \ - (width) == NL80211_CHAN_WIDTH_16) +#define IS_S1G(width) ((width) == NL80211_CHAN_WIDTH_1 || \ + (width) == NL80211_CHAN_WIDTH_2 || \ + (width) == NL80211_CHAN_WIDTH_4 || \ + (width) == NL80211_CHAN_WIDTH_8 || \ + (width) == NL80211_CHAN_WIDTH_16) - if (NARROW_OR_S1G(c1->width) || NARROW_OR_S1G(c2->width)) + if (IS_S1G(c1->width) || IS_S1G(c2->width)) return NULL; /* @@ -817,6 +1043,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN_DATA: + case NL80211_IFTYPE_PD: break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: @@ -941,6 +1168,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) /* Can NAN type be considered as beaconing interface? */ case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_NAN_DATA: + case NL80211_IFTYPE_PD: break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: @@ -1266,13 +1494,6 @@ bool _cfg80211_chandef_usable(struct wiphy *wiphy, control_freq = chandef->chan->center_freq; switch (chandef->width) { - case NL80211_CHAN_WIDTH_5: - width = 5; - break; - case NL80211_CHAN_WIDTH_10: - prohibited_flags |= IEEE80211_CHAN_NO_10MHZ; - width = 10; - break; case NL80211_CHAN_WIDTH_20: if (!ht_cap->ht_supported && chandef->chan->band != NL80211_BAND_6GHZ) diff --git a/net/wireless/core.c b/net/wireless/core.c index 6783e0672dcb..3dcf63b04c41 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -322,6 +322,28 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, return 0; } +void cfg80211_stop_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + lockdep_assert_held(&rdev->wiphy.mtx); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_PD)) + return; + + if (!rdev->ops->stop_pd) + return; + + if (!wdev_running(wdev)) + return; + + cfg80211_pmsr_wdev_down(wdev); + + rdev_stop_pd(rdev, wdev); + wdev->is_running = false; + + rdev->opencount--; +} + void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); @@ -351,6 +373,9 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) case NL80211_IFTYPE_NAN: cfg80211_stop_nan(rdev, wdev); break; + case NL80211_IFTYPE_PD: + cfg80211_stop_pd(rdev, wdev); + break; default: break; } @@ -804,6 +829,24 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) return ret; } +static bool wiphy_cipher_suites_valid(const struct wiphy *wiphy) +{ + int i, j; + + if (wiphy->n_cipher_suites && !wiphy->cipher_suites) + return false; + + for (i = 0; i < wiphy->n_cipher_suites; i++) { + for (j = 0; j < i; j++) { + if (wiphy->cipher_suites[i] == + wiphy->cipher_suites[j]) + return false; + } + } + + return true; +} + int wiphy_register(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); @@ -828,6 +871,9 @@ int wiphy_register(struct wiphy *wiphy) (!rdev->ops->tdls_channel_switch || !rdev->ops->tdls_cancel_channel_switch))) return -EINVAL; + if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_PD)) && + (!rdev->ops->start_pd || !rdev->ops->stop_pd))) + return -EINVAL; if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) && (!rdev->ops->start_nan || !rdev->ops->stop_nan || @@ -836,7 +882,7 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN_DATA)) && - !wiphy->nan_capa.phy.ht.ht_supported)) + (!wiphy->nan_capa.phy.ht.ht_supported || wiphy->n_radio > 1))) return -EINVAL; if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))) @@ -871,9 +917,7 @@ int wiphy_register(struct wiphy *wiphy) BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_80P80) | BIT(NL80211_CHAN_WIDTH_160) | - BIT(NL80211_CHAN_WIDTH_320) | - BIT(NL80211_CHAN_WIDTH_5) | - BIT(NL80211_CHAN_WIDTH_10)))) + BIT(NL80211_CHAN_WIDTH_320)))) return -EINVAL; } @@ -940,6 +984,9 @@ int wiphy_register(struct wiphy *wiphy) if (res) return res; + if (!wiphy_cipher_suites_valid(wiphy)) + return -EINVAL; + /* sanity check supported bands/channels */ for (band = 0; band < NUM_NL80211_BANDS; band++) { const struct ieee80211_sband_iftype_data *iftd; @@ -1387,6 +1434,9 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev, case NL80211_IFTYPE_NAN: cfg80211_stop_nan(rdev, wdev); break; + case NL80211_IFTYPE_PD: + cfg80211_stop_pd(rdev, wdev); + break; default: break; } @@ -1495,6 +1545,9 @@ void cfg80211_leave_locked(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_NAN: cfg80211_stop_nan(rdev, wdev); break; + case NL80211_IFTYPE_PD: + cfg80211_stop_pd(rdev, wdev); + break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_NAN_DATA: diff --git a/net/wireless/core.h b/net/wireless/core.h index ae2d56d3ad90..df47ed6208a5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,7 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H @@ -446,6 +446,7 @@ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev, int key_idx, bool pairwise); int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr); void __cfg80211_scan_done(struct wiphy *wiphy, struct wiphy_work *wk); @@ -556,6 +557,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); +void cfg80211_stop_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index bd72317c4964..2a2c173058ba 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -38,6 +38,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, u.assoc_resp.variable), .status = le16_to_cpu(mgmt->u.assoc_resp.status_code), .ap_mld_addr = data->ap_mld_addr, + .assoc_encrypted = data->assoc_encrypted, }; unsigned int link_id; @@ -360,17 +361,18 @@ cfg80211_mlme_check_mlo_compat(const struct ieee80211_multi_link_elem *mle_a, * reserved when included in a unicast Probe Response frame and may * also change when the AP adds/removes links. The BTM MLD * Recommendation For Multiple APs Support subfield is reserved when - * transmitted by an AP. All other bits are currently reserved. - * See IEEE P802.11be/D7.0, Table 9-417o. + * transmitted by an AP. */ if ((ieee80211_mle_get_ext_mld_capa_op((const u8 *)mle_a) & (IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | - IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK)) != + IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK | + IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM)) != (ieee80211_mle_get_ext_mld_capa_op((const u8 *)mle_b) & (IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | - IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK))) { + IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK | + IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM))) { NL_SET_ERR_MSG(extack, "extended link MLD capabilities/ops mismatch"); return -EINVAL; @@ -944,6 +946,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, * fall through, P2P device only supports * public action frames */ + case NL80211_IFTYPE_PD: default: err = -EOPNOTSUPP; break; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 76c537a6e8b5..53b4b3f76697 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -476,6 +476,18 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK] = { .type = NLA_FLAG }, [NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR] = { .type = NLA_U8 }, [NL80211_PMSR_FTM_REQ_ATTR_RSTA] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS] = { + .type = NLA_U32 + }, + [NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS] = { + .type = NLA_U32 + }, + [NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION] = NLA_POLICY_MAX(NLA_U32, 255), + [NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS] = { .type = NLA_U32 }, + [NL80211_PMSR_FTM_REQ_ATTR_INGRESS] = { .type = NLA_U64 }, + [NL80211_PMSR_FTM_REQ_ATTR_EGRESS] = { .type = NLA_U64 }, + [NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS] = { .type = NLA_FLAG }, }; static const struct nla_policy @@ -498,6 +510,8 @@ nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = { [NL80211_PMSR_PEER_ATTR_REQ] = NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy), [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT }, + [NL80211_PMSR_PEER_ATTR_REQ_TYPE] = + NLA_POLICY_MAX(NLA_U32, NL80211_PMSR_FTM_REQ_TYPE_MAX), }; static const struct nla_policy @@ -1047,7 +1061,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NL80211_MAX_SUPP_SELECTORS), [NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 }, [NL80211_ATTR_EPCS] = { .type = NLA_FLAG }, - [NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 }, + [NL80211_ATTR_EXT_MLD_CAPA_AND_OPS] = { .type = NLA_U16 }, [NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 }, [NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2), [NL80211_ATTR_S1G_SHORT_BEACON] = @@ -1076,6 +1090,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME] = { .type = NLA_U16 }, [NL80211_ATTR_NAN_PEER_MAPS] = NLA_POLICY_NESTED_ARRAY(nl80211_nan_peer_map_policy), + [NL80211_ATTR_NPCA_PRIMARY_FREQ] = { .type = NLA_U32 }, + [NL80211_ATTR_NPCA_PUNCT_BITMAP] = + NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range), }; /* policy for the key attributes */ @@ -1089,6 +1106,10 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { [NL80211_KEY_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES - 1), [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, [NL80211_KEY_MODE] = NLA_POLICY_RANGE(NLA_U8, 0, NL80211_KEY_SET_TX), + [NL80211_KEY_LTF_SEED] = { + .type = NLA_BINARY, + .len = WLAN_MAX_SECURE_LTF_KEYSEED_LEN, + }, }; /* policy for the key default flags */ @@ -1632,6 +1653,11 @@ static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key, if (tb[NL80211_KEY_MODE]) k->p.mode = nla_get_u8(tb[NL80211_KEY_MODE]); + if (tb[NL80211_KEY_LTF_SEED]) { + k->p.ltf_keyseed = nla_data(tb[NL80211_KEY_LTF_SEED]); + k->p.ltf_keyseed_len = nla_len(tb[NL80211_KEY_LTF_SEED]); + } + return 0; } @@ -1745,6 +1771,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) static struct cfg80211_cached_keys * nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, struct genl_info *info, bool *no_ht) { struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS]; @@ -1794,7 +1821,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, goto error; } else if (parse.defmgmt) goto error; - err = cfg80211_validate_key_settings(rdev, &parse.p, + err = cfg80211_validate_key_settings(rdev, wdev, &parse.p, parse.idx, false, NULL); if (err) goto error; @@ -1853,6 +1880,11 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) NL80211_EXT_FEATURE_SECURE_NAN)) return 0; return -EINVAL; + case NL80211_IFTYPE_PD: + if (wiphy_ext_feature_isset(wdev->wiphy, + NL80211_EXT_FEATURE_SECURE_RTT)) + return 0; + return -EINVAL; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MONITOR: @@ -2509,10 +2541,117 @@ nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap, nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX, cap->ftm.max_total_ltf_rx)) return -ENOBUFS; - if (cap->ftm.support_rsta && - nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT)) + + if (cap->ftm.ista.support_ntb || cap->ftm.ista.support_tb || + cap->ftm.ista.support_edca) { + struct nlattr *ista_caps; + + ista_caps = nla_nest_start_noflag(msg, + NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS); + if (!ista_caps) + return -ENOBUFS; + if (cap->ftm.ista.support_ntb && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB)) + return -ENOBUFS; + if (cap->ftm.ista.support_tb && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB)) + return -ENOBUFS; + if (cap->ftm.ista.support_edca && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA)) + return -ENOBUFS; + if (cap->ftm.ista.max_peers && + nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE, + cap->ftm.ista.max_peers)) + return -ENOBUFS; + nla_nest_end(msg, ista_caps); + } + + if (cap->ftm.rsta.support_ntb || cap->ftm.rsta.support_tb || + cap->ftm.rsta.support_edca) { + struct nlattr *rsta_caps; + + /* + * Set the generic RSTA_SUPPORT flag if any of the specific + * ranging modes is supported to maintain the backward + * compatibility. + */ + if (nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT)) + return -ENOBUFS; + + rsta_caps = nla_nest_start_noflag(msg, + NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS); + if (!rsta_caps) + return -ENOBUFS; + if (cap->ftm.rsta.support_ntb && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB)) + return -ENOBUFS; + if (cap->ftm.rsta.support_tb && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB)) + return -ENOBUFS; + if (cap->ftm.rsta.support_edca && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA)) + return -ENOBUFS; + if (cap->ftm.rsta.max_peers && + nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE, + cap->ftm.rsta.max_peers)) + return -ENOBUFS; + nla_nest_end(msg, rsta_caps); + } + + if (cap->ftm.max_no_of_tx_antennas && + nla_put_u8(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS, + cap->ftm.max_no_of_tx_antennas)) return -ENOBUFS; + if (cap->ftm.max_no_of_rx_antennas && + nla_put_u8(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS, + cap->ftm.max_no_of_rx_antennas)) + return -ENOBUFS; + + if (cap->ftm.min_allowed_ranging_interval_edca && + nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA, + cap->ftm.min_allowed_ranging_interval_edca)) + return -ENOBUFS; + + if (cap->ftm.min_allowed_ranging_interval_ntb && + nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB, + cap->ftm.min_allowed_ranging_interval_ntb)) + return -ENOBUFS; + + if (cap->ftm.type.infra_support || cap->ftm.type.pd_support) { + struct nlattr *pd_caps; + + pd_caps = nla_nest_start_noflag(msg, + NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS); + if (!pd_caps) + return -ENOBUFS; + + if (cap->ftm.type.infra_support && + nla_put_flag(msg, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT)) + return -ENOBUFS; + + if (cap->ftm.type.pd_support && + nla_put_flag(msg, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT)) + return -ENOBUFS; + + nla_nest_end(msg, pd_caps); + } + + if (cap->ftm.concurrent_ista_rsta_support && + nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT)) + return -ENOBUFS; + + if (cap->ftm.type.pd_support) { + if (cap->ftm.pd_preambles && + nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES, + cap->ftm.pd_preambles)) + return -ENOBUFS; + if (cap->ftm.pd_bandwidths && + nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS, + cap->ftm.pd_bandwidths)) + return -ENOBUFS; + } + nla_nest_end(msg, ftm); return 0; } @@ -3311,11 +3450,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (nl80211_send_coalesce(msg, rdev)) goto nla_put_failure; - if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && - (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || - nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) - goto nla_put_failure; - if (rdev->wiphy.max_ap_assoc_sta && nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA, rdev->wiphy.max_ap_assoc_sta)) @@ -3472,6 +3606,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, NL80211_ATTR_MLD_CAPA_AND_OPS, capab->mld_capa_and_ops))) goto nla_put_failure; + if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO && + capab->ext_mld_capa_and_ops && + nla_put_u16(msg, + NL80211_ATTR_EXT_MLD_CAPA_AND_OPS, + capab->ext_mld_capa_and_ops)) + goto nla_put_failure; nla_nest_end(msg, nested_ext_capab); if (state->split) @@ -3783,7 +3923,8 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct netlink_ext_ack *extack, struct nlattr **attrs, bool monitor, - struct cfg80211_chan_def *chandef) + struct cfg80211_chan_def *chandef, + bool permit_npca) { u32 control_freq; @@ -3897,6 +4038,34 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, } } + if (attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ]) { + if (!permit_npca) { + NL_SET_ERR_MSG_ATTR(extack, + attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ], + "NPCA not supported"); + return -EINVAL; + } + + chandef->npca_chan = + ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ])); + if (!chandef->npca_chan) { + NL_SET_ERR_MSG_ATTR(extack, + attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ], + "invalid NPCA primary channel"); + return -EINVAL; + } + + chandef->npca_punctured = + nla_get_u32_default(attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP], + chandef->punctured); + } else if (attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP]) { + NL_SET_ERR_MSG_ATTR(extack, + attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP], + "NPCA puncturing only valid with NPCA"); + return -EINVAL; + } + if (!cfg80211_chandef_valid(chandef)) { NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], "invalid channel definition"); @@ -3911,10 +4080,9 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, return -EINVAL; } - if ((chandef->width == NL80211_CHAN_WIDTH_5 || - chandef->width == NL80211_CHAN_WIDTH_10) && - !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ)) { - NL_SET_ERR_MSG(extack, "5/10 MHz not supported"); + if (chandef->width == NL80211_CHAN_WIDTH_5 || + chandef->width == NL80211_CHAN_WIDTH_10) { + NL_SET_ERR_MSG(extack, "5/10 MHz not supported any more"); return -EINVAL; } @@ -3924,9 +4092,11 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct netlink_ext_ack *extack, struct nlattr **attrs, - struct cfg80211_chan_def *chandef) + struct cfg80211_chan_def *chandef, + bool permit_npca) { - return _nl80211_parse_chandef(rdev, extack, attrs, false, chandef); + return _nl80211_parse_chandef(rdev, extack, attrs, false, chandef, + permit_npca); } static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, @@ -3939,6 +4109,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; struct wireless_dev *wdev = NULL; int link_id = _link_id; + bool permit_npca; if (dev) wdev = dev->ieee80211_ptr; @@ -3953,9 +4124,13 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, link_id = 0; } + /* allow parsing it - will check on start_ap or below */ + permit_npca = iftype == NL80211_IFTYPE_AP || + iftype == NL80211_IFTYPE_P2P_GO; + result = _nl80211_parse_chandef(rdev, info->extack, info->attrs, iftype == NL80211_IFTYPE_MONITOR, - &chandef); + &chandef, permit_npca); if (result) return result; @@ -3974,6 +4149,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, return -EBUSY; /* Only allow dynamic channel width changes */ + cur_chan = wdev->links[link_id].ap.chandef.npca_chan; + if (chandef.npca_chan != cur_chan) + return -EBUSY; cur_chan = wdev->links[link_id].ap.chandef.chan; if (chandef.chan != cur_chan) return -EBUSY; @@ -4458,6 +4636,15 @@ int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *ch nla_put_flag(msg, NL80211_ATTR_S1G_PRIMARY_2MHZ)) return -ENOBUFS; + if (chandef->npca_chan && + nla_put_u32(msg, NL80211_ATTR_NPCA_PRIMARY_FREQ, + chandef->npca_chan->center_freq)) + return -ENOBUFS; + if (chandef->npca_punctured && + nla_put_u32(msg, NL80211_ATTR_NPCA_PUNCT_BITMAP, + chandef->npca_punctured)) + return -ENOBUFS; + return 0; } EXPORT_SYMBOL(nl80211_send_chandef); @@ -4949,6 +5136,7 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return -EOPNOTSUPP; if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN || + type == NL80211_IFTYPE_PD || rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) && info->attrs[NL80211_ATTR_MAC]) { nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], @@ -5005,8 +5193,9 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) break; case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_PD: /* - * P2P Device and NAN do not have a netdev, so don't go + * P2P Device, NAN and PD do not have a netdev, so don't go * through the netdev notifier and must be added here */ cfg80211_init_wdev(wdev); @@ -5112,7 +5301,7 @@ static int nl80211_validate_key_link_id(struct genl_info *info, if (wdev->valid_links) { if (link_id == -1) { GENL_SET_ERR_MSG(info, - "link ID must for MLO group key"); + "link ID must be set for MLO group key"); return -EINVAL; } if (!(wdev->valid_links & BIT(link_id))) { @@ -5419,7 +5608,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->add_key) return -EOPNOTSUPP; - if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, + if (cfg80211_validate_key_settings(rdev, wdev, &key.p, key.idx, key.type == NL80211_KEYTYPE_PAIRWISE, mac_addr)) { GENL_SET_ERR_MSG(info, "key setting validation failed"); @@ -6409,9 +6598,104 @@ static int nl80211_parse_he_bss_color(struct nlattr *attrs, return 0; } +static void nl80211_check_ap_rate_selectors(struct cfg80211_beacon_data *bcn, + const struct element *rates) +{ + int i; + + if (!rates) + return; + + for (i = 0; i < rates->datalen; i++) { + if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY) + bcn->ht_required = true; + if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY) + bcn->vht_required = true; + } +} + +/* + * Since the nl80211 API didn't include, from the beginning, attributes about + * HT/VHT/... operation, we parse them out of the elements and check for + * validity for use by drivers/mac80211. + */ +static int nl80211_calculate_ap_operation(struct nlattr *attrs[], + struct cfg80211_beacon_data *bcn, + struct netlink_ext_ack *extack) +{ + size_t ies_len = bcn->tail_len; + const u8 *ies = bcn->tail; + const struct element *rates; + const struct element *op; + + rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len); + nl80211_check_ap_rate_selectors(bcn, rates); + + rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len); + nl80211_check_ap_rate_selectors(bcn, rates); + + op = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len); + if (op) { + if (op->datalen < sizeof(*bcn->he_oper) + 1) { + NL_SET_ERR_MSG(extack, "bad HE operation in beacon"); + return -EINVAL; + } + bcn->he_oper = (void *)(op->data + 1); + /* takes extension ID into account */ + if (op->datalen < ieee80211_he_oper_size((void *)bcn->he_oper)) { + NL_SET_ERR_MSG(extack, "bad HE operation in beacon"); + return -EINVAL; + } + } + + op = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ies, ies_len); + if (op) { + if (op->datalen < sizeof(*bcn->ht_oper)) { + NL_SET_ERR_MSG(extack, "bad HT operation in beacon"); + return -EINVAL; + } + bcn->ht_oper = (void *)op->data; + } + + op = cfg80211_find_elem(WLAN_EID_VHT_OPERATION, ies, ies_len); + if (op) { + if (op->datalen < sizeof(*bcn->vht_oper)) { + NL_SET_ERR_MSG(extack, "bad VHT operation in beacon"); + return -EINVAL; + } + bcn->vht_oper = (void *)op->data; + } + + op = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len); + if (op) { + if (!ieee80211_eht_oper_size_ok(op->data + 1, + op->datalen - 1)) { + NL_SET_ERR_MSG(extack, "bad EHT operation in beacon"); + return -EINVAL; + } + bcn->eht_oper = (void *)(op->data + 1); + } + + op = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len); + if (op) { + /* need full UHR operation separately */ + if (!attrs[NL80211_ATTR_UHR_OPERATION]) { + NL_SET_ERR_MSG(extack, "missing UHR operation"); + return -EINVAL; + } + bcn->uhr_oper = nla_data(attrs[NL80211_ATTR_UHR_OPERATION]); + } else if (attrs[NL80211_ATTR_UHR_OPERATION]) { + NL_SET_ERR_MSG(extack, "unexpected UHR operation"); + return -EINVAL; + } + + return 0; +} + static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, struct nlattr *attrs[], struct cfg80211_beacon_data *bcn, + struct ieee80211_channel *chan, struct netlink_ext_ack *extack) { bool haveinfo = false; @@ -6526,6 +6810,19 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, } } + err = nl80211_calculate_ap_operation(attrs, bcn, extack); + if (err) + return err; + + if (bcn->he_oper && (chan->flags & IEEE80211_CHAN_NO_HE)) + return -EOPNOTSUPP; + + if (bcn->eht_oper && (chan->flags & IEEE80211_CHAN_NO_EHT)) + return -EOPNOTSUPP; + + if (bcn->uhr_oper && (chan->flags & IEEE80211_CHAN_NO_UHR)) + return -EOPNOTSUPP; + return 0; } @@ -6643,81 +6940,60 @@ nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, return 0; } -static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params, - const struct element *rates) -{ - int i; - - if (!rates) - return; - - for (i = 0; i < rates->datalen; i++) { - if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY) - params->ht_required = true; - if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY) - params->vht_required = true; - if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HE_PHY) - params->he_required = true; - if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_SAE_H2E) - params->sae_h2e_required = true; - } -} - /* * Since the nl80211 API didn't include, from the beginning, attributes about - * HT/VHT requirements/capabilities, we parse them out of the IEs for the - * benefit of drivers that rebuild IEs in the firmware. + * HT/VHT/... capabilities, we parse them out of the elements and check for + * validity for use by drivers/mac80211. */ -static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params) +static int nl80211_calculate_ap_capabilities(struct genl_info *info, + struct cfg80211_ap_settings *params) { - const struct cfg80211_beacon_data *bcn = ¶ms->beacon; - size_t ies_len = bcn->tail_len; - const u8 *ies = bcn->tail; - const struct element *rates; + size_t ies_len = params->beacon.tail_len; + const u8 *ies = params->beacon.tail; const struct element *cap; - rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len); - nl80211_check_ap_rate_selectors(params, rates); - - rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len); - nl80211_check_ap_rate_selectors(params, rates); - cap = cfg80211_find_elem(WLAN_EID_HT_CAPABILITY, ies, ies_len); - if (cap && cap->datalen >= sizeof(*params->ht_cap)) + if (cap) { + if (cap->datalen < sizeof(*params->ht_cap)) { + GENL_SET_ERR_MSG(info, "bad HT capability in beacon"); + return -EINVAL; + } params->ht_cap = (void *)cap->data; + } + cap = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY, ies, ies_len); - if (cap && cap->datalen >= sizeof(*params->vht_cap)) + if (cap) { + if (cap->datalen < sizeof(*params->vht_cap)) { + GENL_SET_ERR_MSG(info, "bad VHT capability in beacon"); + return -EINVAL; + } params->vht_cap = (void *)cap->data; + } + cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ies, ies_len); - if (cap && cap->datalen >= sizeof(*params->he_cap) + 1) + if (cap) { + if (cap->datalen < sizeof(*params->he_cap) + 1) { + GENL_SET_ERR_MSG(info, "bad HE capability in beacon"); + return -EINVAL; + } params->he_cap = (void *)(cap->data + 1); - cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len); - if (cap && cap->datalen >= sizeof(*params->he_oper) + 1) - params->he_oper = (void *)(cap->data + 1); + } + cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_CAPABILITY, ies, ies_len); if (cap) { - if (!cap->datalen) - return -EINVAL; params->eht_cap = (void *)(cap->data + 1); if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_cap, (const u8 *)params->eht_cap, - cap->datalen - 1, true)) - return -EINVAL; - } - cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len); - if (cap) { - if (!cap->datalen) - return -EINVAL; - params->eht_oper = (void *)(cap->data + 1); - if (!ieee80211_eht_oper_size_ok((const u8 *)params->eht_oper, - cap->datalen - 1)) + cap->datalen - 1, true)) { + GENL_SET_ERR_MSG(info, "bad EHT capability in beacon"); return -EINVAL; + } } - if (!!params->he_cap != !!params->he_oper) + if (!!params->he_cap != !!params->beacon.he_oper) return -EINVAL; - if (!!params->eht_cap != !!params->eht_oper) + if (!!params->eht_cap != !!params->beacon.eht_oper) return -EINVAL; return 0; @@ -6847,24 +7123,6 @@ static void nl80211_send_ap_started(struct wireless_dev *wdev, nlmsg_free(msg); } -static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params) -{ - struct ieee80211_channel *channel = params->chandef.chan; - - if ((params->he_cap || params->he_oper) && - (channel->flags & IEEE80211_CHAN_NO_HE)) - return -EOPNOTSUPP; - - if ((params->eht_cap || params->eht_oper) && - (channel->flags & IEEE80211_CHAN_NO_EHT)) - return -EOPNOTSUPP; - - if (params->uhr_oper && (channel->flags & IEEE80211_CHAN_NO_UHR)) - return -EOPNOTSUPP; - - return 0; -} - static int nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev, struct nlattr *attrs, @@ -6900,6 +7158,28 @@ nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev, return 0; } +static int nl80211_check_npca(struct cfg80211_registered_device *rdev, + const struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype, + struct netlink_ext_ack *extack) +{ + const struct ieee80211_supported_band *sband; + const struct ieee80211_sta_uhr_cap *uhr_cap; + + if (!chandef->npca_chan) + return 0; + + sband = rdev->wiphy.bands[chandef->chan->band]; + uhr_cap = ieee80211_get_uhr_iftype_cap(sband, iftype); + + if (uhr_cap && + (uhr_cap->mac.mac_cap[0] & IEEE80211_UHR_MAC_CAP0_NPCA_SUPP)) + return 0; + + NL_SET_ERR_MSG(extack, "NPCA not supported"); + return -EINVAL; +} + static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6937,11 +7217,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (!params) return -ENOMEM; - err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, - info->extack); - if (err) - goto out; - params->beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); params->dtim_period = @@ -7044,7 +7319,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - ¶ms->chandef); + ¶ms->chandef, true); if (err) goto out; } else if (wdev->valid_links) { @@ -7058,6 +7333,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) goto out; } + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, + params->chandef.chan, info->extack); + if (err) + goto out; + + err = nl80211_check_npca(rdev, ¶ms->chandef, wdev->iftype, + info->extack); + if (err) + goto out; + beacon_check.iftype = wdev->iftype; beacon_check.relax = true; beacon_check.reg_power = @@ -7157,14 +7442,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) goto out; } - err = nl80211_calculate_ap_params(params); - if (err) - goto out; - - if (info->attrs[NL80211_ATTR_UHR_OPERATION]) - params->uhr_oper = nla_data(info->attrs[NL80211_ATTR_UHR_OPERATION]); - - err = nl80211_validate_ap_phy_operation(params); + err = nl80211_calculate_ap_capabilities(info, params); if (err) goto out; @@ -7235,6 +7513,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, + wdev->links[link_id].ap.chandef.chan, info->extack); if (err) goto out; @@ -10826,7 +11105,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) wiphy = &rdev->wiphy; - if (wdev->iftype == NL80211_IFTYPE_NAN) + if (wdev->iftype == NL80211_IFTYPE_NAN || + wdev->iftype == NL80211_IFTYPE_PD) return -EOPNOTSUPP; if (!rdev->ops->scan) @@ -11614,7 +11894,8 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (dfs_region == NL80211_DFS_UNSET) return -EINVAL; - err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef); + err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef, + false); if (err) return err; @@ -11703,7 +11984,8 @@ static int nl80211_notify_radar_detection(struct sk_buff *skb, return -EINVAL; } - err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef); + err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef, + false); if (err) { GENL_SET_ERR_MSG(info, "Unable to extract chandef info"); return err; @@ -11786,6 +12068,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) int err; bool need_new_beacon = false; bool need_handle_dfs_flag = true; + bool permit_npca = false; u32 cs_count; if (!rdev->ops->channel_switch || @@ -11803,6 +12086,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) */ need_handle_dfs_flag = false; + permit_npca = true; + /* useless if AP is not running */ if (!wdev->links[link_id].ap.beacon_interval) return -ENOTCONN; @@ -11839,11 +12124,21 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) params.count = cs_count; + err = nl80211_parse_chandef(rdev, info->extack, info->attrs, + ¶ms.chandef, permit_npca); + if (err) + goto free; + + err = nl80211_check_npca(rdev, ¶ms.chandef, wdev->iftype, + info->extack); + if (err) + goto free; + if (!need_new_beacon) goto skip_beacons; err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after, - info->extack); + params.chandef.chan, info->extack); if (err) goto free; @@ -11860,6 +12155,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) goto free; err = nl80211_parse_beacon(rdev, csa_attrs, ¶ms.beacon_csa, + wdev->links[link_id].ap.chandef.chan, info->extack); if (err) goto free; @@ -11888,11 +12184,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) goto free; skip_beacons: - err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - ¶ms.chandef); - if (err) - goto free; - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef, wdev->iftype)) { err = -EINVAL; @@ -12877,9 +13168,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto free; } - if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]) req.ext_mld_capa_ops = - nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); + nla_get_u16(info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]); } else { if (req.link_id >= 0) return -EINVAL; @@ -12890,7 +13181,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(req.bss); ap_addr = req.bss->bssid; - if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]) return -EINVAL; } @@ -13113,7 +13404,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) } err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - &ibss.chandef); + &ibss.chandef, false); if (err) return err; @@ -13122,8 +13413,6 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) return -EINVAL; switch (ibss.chandef.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: break; case NL80211_CHAN_WIDTH_20: @@ -13184,7 +13473,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { bool no_ht = false; - connkeys = nl80211_parse_connkeys(rdev, info, &no_ht); + connkeys = nl80211_parse_connkeys(rdev, dev->ieee80211_ptr, + info, &no_ht); if (IS_ERR(connkeys)) return PTR_ERR(connkeys); @@ -13441,6 +13731,16 @@ static int nl80211_testmode_dump(struct sk_buff *skb, err = -ENOENT; goto out_err; } + + /* + * The wiphy may have moved netns between dumpit + * invocations (via NL80211_CMD_SET_WIPHY_NETNS), so + * re-check that it still matches the caller's netns. + */ + if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) { + err = -ENODEV; + goto out_err; + } } else { attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR); if (!attrbuf) { @@ -13626,7 +13926,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { - connkeys = nl80211_parse_connkeys(rdev, info, NULL); + connkeys = nl80211_parse_connkeys(rdev, dev->ieee80211_ptr, + info, NULL); if (IS_ERR(connkeys)) return PTR_ERR(connkeys); } @@ -14100,6 +14401,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, unsigned int link_id = nl80211_link_id(info->attrs); struct wireless_dev *wdev = info->user_ptr[1]; struct cfg80211_chan_def chandef; + const u8 *rx_addr = NULL; struct sk_buff *msg; void *hdr; u64 cookie; @@ -14112,6 +14414,14 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + if (info->attrs[NL80211_ATTR_MAC]) + rx_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (rx_addr && + !wiphy_ext_feature_isset(wdev->wiphy, + NL80211_EXT_FEATURE_ROC_ADDR_FILTER)) + return -EOPNOTSUPP; + if (!rdev->ops->remain_on_channel || !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) return -EOPNOTSUPP; @@ -14124,7 +14434,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, duration > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; - err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef); + err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef, + false); if (err) return err; @@ -14159,7 +14470,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, } err = rdev_remain_on_channel(rdev, wdev, chandef.chan, - duration, &cookie); + duration, &cookie, rx_addr); if (err) goto free_msg; @@ -14248,6 +14559,11 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) WIPHY_NAN_FLAGS_USERSPACE_DE)) return -EOPNOTSUPP; break; + case NL80211_IFTYPE_PD: + if (!wiphy_ext_feature_isset(wdev->wiphy, + NL80211_EXT_FEATURE_SECURE_RTT)) + return -EOPNOTSUPP; + break; default: return -EOPNOTSUPP; } @@ -14312,6 +14628,11 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) WIPHY_NAN_FLAGS_USERSPACE_DE)) return -EOPNOTSUPP; break; + case NL80211_IFTYPE_PD: + if (!wiphy_ext_feature_isset(wdev->wiphy, + NL80211_EXT_FEATURE_SECURE_RTT)) + return -EOPNOTSUPP; + break; default: return -EOPNOTSUPP; } @@ -14343,7 +14664,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) chandef.chan = NULL; if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - &chandef); + &chandef, false); if (err) return err; } @@ -14437,6 +14758,11 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in NL80211_EXT_FEATURE_SECURE_NAN)) return -EOPNOTSUPP; break; + case NL80211_IFTYPE_PD: + if (!wiphy_ext_feature_isset(wdev->wiphy, + NL80211_EXT_FEATURE_SECURE_RTT)) + return -EOPNOTSUPP; + break; default: return -EOPNOTSUPP; } @@ -14746,7 +15072,7 @@ static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info) int err; err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - &setup.chandef); + &setup.chandef, false); if (err) return err; @@ -14822,7 +15148,7 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - &setup.chandef); + &setup.chandef, false); if (err) return err; } else { @@ -16573,6 +16899,46 @@ static int nl80211_nan_change_config(struct sk_buff *skb, return rdev_nan_change_conf(rdev, wdev, &conf, changed); } +static int nl80211_start_pd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + int err; + + if (wdev->iftype != NL80211_IFTYPE_PD) + return -EOPNOTSUPP; + + if (wdev_running(wdev)) + return -EEXIST; + + if (rfkill_blocked(rdev->wiphy.rfkill)) + return -ERFKILL; + + if (!rdev->ops->start_pd) + return -EOPNOTSUPP; + + err = rdev_start_pd(rdev, wdev); + if (err) + return err; + wdev->is_running = true; + rdev->opencount++; + + return 0; +} + +static int nl80211_stop_pd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + + if (wdev->iftype != NL80211_IFTYPE_PD) + return -EOPNOTSUPP; + + cfg80211_stop_pd(rdev, wdev); + + return 0; +} + void cfg80211_nan_match(struct wireless_dev *wdev, struct cfg80211_nan_match_params *match, gfp_t gfp) { @@ -16786,7 +17152,7 @@ static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev, return ret; ret = nl80211_parse_chandef(rdev, info->extack, channel_parsed, - &chandef); + &chandef, false); if (ret) return ret; @@ -17416,6 +17782,15 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, if (!wiphy) return -ENODEV; + + /* + * The wiphy may have moved netns between dumpit + * invocations (via NL80211_CMD_SET_WIPHY_NETNS), so + * re-check that it still matches the caller's netns. + */ + if (!net_eq(wiphy_net(wiphy), sock_net(skb->sk))) + return -ENODEV; + *rdev = wiphy_to_rdev(wiphy); *wdev = NULL; @@ -17785,7 +18160,8 @@ static int nl80211_tdls_channel_switch(struct sk_buff *skb, !info->attrs[NL80211_ATTR_OPER_CLASS]) return -EINVAL; - err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef); + err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef, + false); if (err) return err; @@ -18358,7 +18734,12 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) params.count = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT]); params.color = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]); + params.link_id = nl80211_link_id(info->attrs); + if (!wdev->links[params.link_id].ap.beacon_interval) + return -EINVAL; + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_next, + wdev->links[params.link_id].ap.chandef.chan, info->extack); if (err) return err; @@ -18374,6 +18755,7 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) goto out; err = nl80211_parse_beacon(rdev, tb, ¶ms.beacon_color_change, + wdev->links[params.link_id].ap.chandef.chan, info->extack); if (err) goto out; @@ -18431,7 +18813,6 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) goto out; } - params.link_id = nl80211_link_id(info->attrs); err = rdev_color_change(rdev, dev, ¶ms); out: @@ -18760,9 +19141,9 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) goto out; } - if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]) req.ext_mld_capa_ops = - nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); + nla_get_u16(info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]); err = cfg80211_assoc_ml_reconf(rdev, dev, &req); @@ -19735,6 +20116,20 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), }, + { + .cmd = NL80211_CMD_START_PD, + .doit = nl80211_start_pd, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | + NL80211_FLAG_NEED_RTNL), + }, + { + .cmd = NL80211_CMD_STOP_PD, + .doit = nl80211_stop_pd, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | + NL80211_FLAG_NEED_RTNL), + }, { .cmd = NL80211_CMD_SET_MCAST_RATE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, @@ -20624,7 +21019,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, (cr->fils.pmk && nla_put(msg, NL80211_ATTR_PMK, cr->fils.pmk_len, cr->fils.pmk)) || (cr->fils.pmkid && - nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid))))) + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid)))) || + (cr->assoc_encrypted && + nla_put_flag(msg, NL80211_ATTR_ASSOC_ENCRYPTED))) goto nla_put_failure; if (cr->valid_links) { @@ -20967,7 +21364,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, } void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, - const u8 *ie, u8 ie_len, + const u8 *ie, size_t ie_len, int sig_dbm, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 048ba92c3e42..bdb065d14054 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Portions of this file - * Copyright (C) 2018, 2020-2025 Intel Corporation + * Copyright (C) 2018, 2020-2026 Intel Corporation */ #ifndef __NET_WIRELESS_NL80211_H #define __NET_WIRELESS_NL80211_H @@ -25,7 +25,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev) int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, struct netlink_ext_ack *extack, struct nlattr **attrs, - struct cfg80211_chan_def *chandef); + struct cfg80211_chan_def *chandef, + bool npca_permitted); int nl80211_parse_random_mac(struct nlattr **attrs, u8 *mac_addr, u8 *mac_addr_mask); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index d6cd0de64d1f..c8447448f3a5 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -17,11 +17,19 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */ /* validate existing data */ - if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) { + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA && + !(capa->ftm.bandwidths & BIT(out->chandef.width))) { NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth"); return -EINVAL; } + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD && + !(capa->ftm.pd_bandwidths & BIT(out->chandef.width))) { + NL_SET_ERR_MSG(info->extack, + "FTM: unsupported bandwidth for PD request"); + return -EINVAL; + } + /* no validation needed - was already done via nested policy */ nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, NULL, NULL); @@ -44,13 +52,22 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, } } - if (!(capa->ftm.preambles & BIT(preamble))) { + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA && + !(capa->ftm.preambles & BIT(preamble))) { NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], "FTM: invalid preamble"); return -EINVAL; } + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD && + !(capa->ftm.pd_preambles & BIT(preamble))) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], + "FTM: invalid preamble for PD request"); + return -EINVAL; + } + out->ftm.preamble = preamble; out->ftm.burst_period = 0; @@ -91,11 +108,10 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); if (capa->ftm.max_ftms_per_burst && - (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst || - out->ftm.ftms_per_burst == 0)) { + out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst) { NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], - "FTM: FTMs per burst must be set lower than the device limit but non-zero"); + "FTM: FTMs per burst must be set lower than the device limit"); return -EINVAL; } @@ -128,6 +144,14 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, return -EINVAL; } + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD && + out->ftm.trigger_based) { + NL_SET_ERR_MSG_ATTR(info->extack, + ftmreq, + "FTM: TB ranging is not supported for PD request type"); + return -EINVAL; + } + out->ftm.non_trigger_based = !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED]; if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) { @@ -143,6 +167,14 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, return -EINVAL; } + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD && + out->ftm.non_trigger_based && out->ftm.ftms_per_burst > 4) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], + "FTM: FTMs per burst must not exceed 4 for PD NTB ranging"); + return -ERANGE; + } + if (out->ftm.ftms_per_burst > 31 && !out->ftm.non_trigger_based && !out->ftm.trigger_based) { NL_SET_ERR_MSG_ATTR(info->extack, @@ -188,20 +220,86 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, } out->ftm.rsta = !!tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA]; - if (out->ftm.rsta && !capa->ftm.support_rsta) { + if (out->ftm.rsta && out->ftm.non_trigger_based && + !capa->ftm.rsta.support_ntb) { NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA], - "FTM: RSTA not supported by device"); + "FTM: NTB RSTA not supported by device"); return -EOPNOTSUPP; } - if (out->ftm.rsta && !out->ftm.lmr_feedback) { + if (out->ftm.rsta && out->ftm.trigger_based && + !capa->ftm.rsta.support_tb) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA], + "FTM: TB RSTA not supported by device"); + return -EOPNOTSUPP; + } + + if (out->ftm.rsta && !out->ftm.non_trigger_based && + !out->ftm.trigger_based && + !capa->ftm.rsta.support_edca) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA], + "FTM: EDCA RSTA not supported by device"); + return -EOPNOTSUPP; + } + + if (out->ftm.rsta && + (out->ftm.non_trigger_based || out->ftm.trigger_based) && + !out->ftm.lmr_feedback) { NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA], "FTM: RSTA set without LMR feedback"); return -EINVAL; } + if (out->ftm.non_trigger_based) { + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD && + !tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]) { + NL_SET_ERR_MSG(info->extack, + "FTM: nominal time is required for PD NTB ranging"); + return -EINVAL; + } + out->ftm.nominal_time = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]) + out->ftm.min_time_between_measurements = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]) + out->ftm.max_time_between_measurements = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION]) + out->ftm.availability_window = + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS]) + out->ftm.num_measurements = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS]); + } + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]) + out->ftm.ingress_distance = + nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]) + out->ftm.egress_distance = + nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]); + + out->ftm.pd_suppress_range_results = + nla_get_flag(tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS]); + + if (out->ftm.request_type != NL80211_PMSR_FTM_REQ_TYPE_PD && + out->ftm.pd_suppress_range_results) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS], + "FTM: suppress range result flag only valid for PD requests"); + return -EINVAL; + } + return 0; } @@ -229,6 +327,19 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN); + if (tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE]) + out->ftm.request_type = + nla_get_u32(tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE]); + else + out->ftm.request_type = NL80211_PMSR_FTM_REQ_TYPE_INFRA; + + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD && + !rdev->wiphy.pmsr_capa->ftm.type.pd_support) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE], + "FTM: PD request type not supported by device"); + return -EINVAL; + } /* reuse info->attrs */ memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1)); err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX, @@ -238,7 +349,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, return err; err = nl80211_parse_chandef(rdev, info->extack, info->attrs, - &out->chandef); + &out->chandef, false); if (err) return err; @@ -286,12 +397,15 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) { struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS]; struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int count, rem, err, idx, peer_count; struct wireless_dev *wdev = info->user_ptr[1]; + const struct cfg80211_pmsr_capabilities *capa; struct cfg80211_pmsr_request *req; struct nlattr *peers, *peer; - int count, rem, err, idx; - if (!rdev->wiphy.pmsr_capa) + capa = rdev->wiphy.pmsr_capa; + + if (!capa) return -EOPNOTSUPP; if (!reqattr) @@ -306,7 +420,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(peer, peers, rem) { count++; - if (count > rdev->wiphy.pmsr_capa->max_peers) { + if (count > capa->max_peers) { NL_SET_ERR_MSG_ATTR(info->extack, peer, "Too many peers used"); return -EINVAL; @@ -322,7 +436,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]); if (info->attrs[NL80211_ATTR_MAC]) { - if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) { + if (!capa->randomize_mac_addr) { NL_SET_ERR_MSG_ATTR(info->extack, info->attrs[NL80211_ATTR_MAC], "device cannot randomize MAC address"); @@ -347,6 +461,41 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) goto out_err; idx++; } + + /* Validate per-role peer limits if advertised */ + if (capa->ftm.ista.max_peers) { + peer_count = 0; + + for (idx = 0; idx < req->n_peers; idx++) { + if (!req->peers[idx].ftm.rsta) { + peer_count++; + + if (peer_count > capa->ftm.ista.max_peers) { + NL_SET_ERR_MSG(info->extack, + "Too many ISTA peers for device limit"); + err = -EINVAL; + goto out_err; + } + } + } + } + + if (capa->ftm.rsta.max_peers) { + peer_count = 0; + + for (idx = 0; idx < req->n_peers; idx++) { + if (req->peers[idx].ftm.rsta) { + peer_count++; + + if (peer_count > capa->ftm.rsta.max_peers) { + NL_SET_ERR_MSG(info->extack, + "Too many RSTA peers for device limit"); + err = -EINVAL; + goto out_err; + } + } + } + } req->cookie = cfg80211_assign_cookie(rdev); req->nl_portid = info->snd_portid; @@ -487,6 +636,21 @@ static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg, PUTOPT_U64(DIST_AVG, dist_avg); PUTOPT_U64(DIST_VARIANCE, dist_variance); PUTOPT_U64(DIST_SPREAD, dist_spread); + PUTOPT(u32, TX_LTF_REPETITION_COUNT, tx_ltf_repetition_count); + PUTOPT(u32, RX_LTF_REPETITION_COUNT, rx_ltf_repetition_count); + PUTOPT(u32, MAX_TIME_BETWEEN_MEASUREMENTS, + max_time_between_measurements); + PUTOPT(u32, MIN_TIME_BETWEEN_MEASUREMENTS, + min_time_between_measurements); + PUTOPT(u8, NUM_TX_SPATIAL_STREAMS, num_tx_spatial_streams); + PUTOPT(u8, NUM_RX_SPATIAL_STREAMS, num_rx_spatial_streams); + PUTOPT(u32, NOMINAL_TIME, nominal_time); + PUTOPT(u8, AVAILABILITY_WINDOW, availability_window); + PUTOPT(u32, CHANNEL_WIDTH, chan_width); + PUTOPT(u32, PREAMBLE, preamble); + if (res->ftm.is_delayed_lmr && + nla_put_flag(msg, NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR)) + goto error; if (res->ftm.lci && res->ftm.lci_len && nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI, res->ftm.lci_len, res->ftm.lci)) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index bba239a068f6..63c26e8b1139 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -736,12 +736,13 @@ static inline int rdev_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie) + unsigned int duration, u64 *cookie, const u8 *rx_addr) { int ret; - trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration); + trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration, + rx_addr); ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, - duration, cookie); + duration, cookie, rx_addr); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } @@ -1092,6 +1093,25 @@ rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_start_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int ret; + + trace_rdev_start_pd(&rdev->wiphy, wdev); + ret = rdev->ops->start_pd(&rdev->wiphy, wdev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void rdev_stop_pd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + trace_rdev_stop_pd(&rdev->wiphy, wdev); + rdev->ops->stop_pd(&rdev->wiphy, wdev); + trace_rdev_return_void(&rdev->wiphy); +} + static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_acl_data *params) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 5db2121c0b57..1e8214d6b6d8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2412,6 +2412,9 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) case NL80211_IFTYPE_NAN_DATA: /* NAN channels are checked in NL80211_IFTYPE_NAN interface */ break; + case NL80211_IFTYPE_PD: + /* we have no info, but PD is also pretty universal */ + continue; default: /* others not implemented for now */ WARN_ON_ONCE(1); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 27a56ee2e8f0..05b7dc6b766c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,7 +5,7 @@ * Copyright 2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include @@ -2610,7 +2610,9 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies, ssize_t copied; u8 elem_datalen; - if (!elem) + if (!elem || (const u8 *)elem < ies || + (const u8 *)elem + sizeof(*elem) > ies + ieslen || + (const u8 *)elem + sizeof(*elem) + elem->datalen > ies + ieslen) return -EINVAL; /* elem might be invalid after the memmove */ diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 86e2ccaa678c..b451df3096dd 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1066,6 +1066,7 @@ void cfg80211_connect_done(struct net_device *dev, } ev->cr.status = params->status; ev->cr.timeout_reason = params->timeout_reason; + ev->cr.assoc_encrypted = params->assoc_encrypted; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); diff --git a/net/wireless/tests/chan.c b/net/wireless/tests/chan.c index 7b97b731993c..65eb18c498de 100644 --- a/net/wireless/tests/chan.c +++ b/net/wireless/tests/chan.c @@ -2,13 +2,18 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2023-2024 Intel Corporation + * Copyright (C) 2023-2024, 2026 Intel Corporation */ #include #include MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +static struct ieee80211_channel chan_2ghz_1 = { + .band = NL80211_BAND_2GHZ, + .center_freq = 2412, +}; + static struct ieee80211_channel chan_6ghz_1 = { .band = NL80211_BAND_6GHZ, .center_freq = 5955, @@ -215,14 +220,189 @@ static void test_chandef_compat(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, ret, expect); } -static struct kunit_case chandef_compat_test_cases[] = { +static const struct chandef_dbe_case { + const char *desc; + struct cfg80211_chan_def c; + u8 dbe[3]; + bool fails; + u16 cf1; +} chandef_dbe_cases[] = { + { + .desc = "non-HT failure", + .c = { + .width = NL80211_CHAN_WIDTH_20_NOHT, + .chan = &chan_6ghz_1, + .center_freq1 = 5955, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40, + .fails = true, + }, + { + .desc = "2.4 GHz fails", + .c = { + .width = NL80211_CHAN_WIDTH_20, + .chan = &chan_2ghz_1, + .center_freq1 = 2412, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40, + .fails = true, + }, + { + .desc = "DBE narrower", + .c = { + .width = NL80211_CHAN_WIDTH_320, + .chan = &chan_6ghz_1, + .center_freq1 = 5955 + 10 + 20 + 40 + 80, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_160, + .fails = true, + }, + { + .desc = "DBE to 320-1", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1, + .cf1 = 6425, + }, + { + .desc = "DBE to 320-2", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2, + .cf1 = 6585, + }, + { + .desc = "bad disabled subchannel bitmap - not enough in BSS (1)", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0001, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0 */ + .fails = true, + }, + { + .desc = "bad disabled subchannel bitmap - too much in BSS (1)", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0001, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0x0300 */ + .dbe[2] = 0x03, + .fails = true, + }, + { + .desc = "bad disabled subchannel bitmap - not enough in BSS (2)", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0001, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0 */ + .fails = true, + }, + { + .desc = "bad disabled subchannel bitmap - too much in BSS (2)", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0001, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0x03 */ + .dbe[1] = 0x03, + .fails = true, + }, + { + .desc = "bad disabled subchannel bitmap - bad bitmap", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0001, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0x1100 */ + .dbe[2] = 0x11, + .fails = true, + }, + { + .desc = "good disabled subchannel bitmap (1)", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0003, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0x0300 */ + .dbe[2] = 0x03, + .cf1 = 6425, + }, + { + .desc = "good disabled subchannel bitmap (2)", + .c = { + .width = NL80211_CHAN_WIDTH_160, + .chan = &chan_6ghz_105, + .center_freq1 = 6475 + 30, + .punctured = 0x0003, + }, + .dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 | + IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES, + /* DBE disabled subchannel bitmap == 0x0003 */ + .dbe[1] = 0x03, + .cf1 = 6585, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(chandef_dbe, chandef_dbe_cases, desc) + +static void test_chandef_dbe(struct kunit *test) +{ + const struct chandef_dbe_case *params = test->param_value; + struct cfg80211_chan_def c = params->c; + int ret; + + KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(¶ms->c), true); + + ret = cfg80211_chandef_add_dbe(&c, (void *)params->dbe); + KUNIT_EXPECT_EQ(test, ret != 0, params->fails); + + if (params->fails) + return; + + KUNIT_EXPECT_EQ(test, c.center_freq1, params->cf1); +} + +static struct kunit_case chandef_test_cases[] = { KUNIT_CASE_PARAM(test_chandef_compat, chandef_compat_gen_params), + KUNIT_CASE_PARAM(test_chandef_dbe, chandef_dbe_gen_params), {} }; -static struct kunit_suite chandef_compat = { - .name = "cfg80211-chandef-compat", - .test_cases = chandef_compat_test_cases, +static struct kunit_suite chandef = { + .name = "cfg80211-chandef", + .test_cases = chandef_test_cases, }; -kunit_test_suite(chandef_compat); +kunit_test_suite(chandef); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index eb5bedf9c92a..94944f2a39a4 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -141,7 +141,9 @@ __field(u32, center_freq1) \ __field(u32, freq1_offset) \ __field(u32, center_freq2) \ - __field(u16, punctured) + __field(u16, punctured) \ + __field(u32, npca_pri_freq) \ + __field(u16, npca_punctured) #define CHAN_DEF_ASSIGN(chandef) \ do { \ if ((chandef) && (chandef)->chan) { \ @@ -155,6 +157,11 @@ __entry->freq1_offset = (chandef)->freq1_offset;\ __entry->center_freq2 = (chandef)->center_freq2;\ __entry->punctured = (chandef)->punctured; \ + __entry->npca_pri_freq = \ + (chandef)->npca_chan ? \ + (chandef)->npca_chan->center_freq : 0; \ + __entry->npca_punctured = \ + (chandef)->npca_punctured; \ } else { \ __entry->band = 0; \ __entry->control_freq = 0; \ @@ -164,14 +171,17 @@ __entry->freq1_offset = 0; \ __entry->center_freq2 = 0; \ __entry->punctured = 0; \ + __entry->npca_pri_freq = 0; \ + __entry->npca_punctured = 0; \ } \ } while (0) #define CHAN_DEF_PR_FMT \ - "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x" + "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x, npca:%u, npca_punct:0x%x" #define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \ __entry->freq_offset, __entry->width, \ __entry->center_freq1, __entry->freq1_offset, \ - __entry->center_freq2, __entry->punctured + __entry->center_freq2, __entry->punctured, \ + __entry->npca_pri_freq, __entry->npca_punctured #define FILS_AAD_ASSIGN(fa) \ do { \ @@ -2155,22 +2165,26 @@ DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa, TRACE_EVENT(rdev_remain_on_channel, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - unsigned int duration), - TP_ARGS(wiphy, wdev, chan, duration), + unsigned int duration, const u8 *rx_addr), + TP_ARGS(wiphy, wdev, chan, duration, rx_addr), TP_STRUCT__entry( WIPHY_ENTRY WDEV_ENTRY CHAN_ENTRY __field(unsigned int, duration) + MAC_ENTRY(rx_addr) ), TP_fast_assign( WIPHY_ASSIGN; WDEV_ASSIGN; CHAN_ASSIGN(chan); __entry->duration = duration; + MAC_ASSIGN(rx_addr, rx_addr); ), - TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u", - WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration) + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT + ", duration: %u, %pM", + WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration, + __entry->rx_addr) ); TRACE_EVENT(rdev_return_int_cookie, @@ -2371,6 +2385,16 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_nan, TP_ARGS(wiphy, wdev) ); +DEFINE_EVENT(wiphy_wdev_evt, rdev_start_pd, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_pd, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + TRACE_EVENT(rdev_add_nan_func, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, const struct cfg80211_nan_func *func), diff --git a/net/wireless/util.c b/net/wireless/util.c index cff5a1bd95cc..24527bf321b2 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -284,6 +284,7 @@ bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev, } int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr) { @@ -344,6 +345,15 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, break; } + /* + * Per Wi-Fi Aware v4.0 section 7.1.2, NAN Data interfaces + * shall only use CCMP-128 or GCMP-256. + */ + if (wdev->iftype == NL80211_IFTYPE_NAN_DATA && + params->cipher != WLAN_CIPHER_SUITE_CCMP && + params->cipher != WLAN_CIPHER_SUITE_GCMP_256) + return -EINVAL; + switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: if (params->key_len != WLAN_KEY_LEN_WEP40) @@ -424,6 +434,21 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher)) return -EINVAL; + if (params->ltf_keyseed) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SECURE_LTF) || + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SET_KEY_LTF_SEED)) + return -EOPNOTSUPP; + + /* + * LTF key seed is pairwise key material and must only be + * used with a pairwise key + */ + if (!pairwise) + return -EINVAL; + } + return 0; } @@ -1201,7 +1226,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, /* cannot change into P2P device or NAN */ if (ntype == NL80211_IFTYPE_P2P_DEVICE || - ntype == NL80211_IFTYPE_NAN) + ntype == NL80211_IFTYPE_NAN || + ntype == NL80211_IFTYPE_PD) return -EOPNOTSUPP; if (!rdev->ops->change_virtual_intf || @@ -1266,6 +1292,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_PD: WARN_ON(1); break; } @@ -2277,9 +2304,6 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, case NL80211_CHAN_WIDTH_80P80: vht_opclass = 130; break; - case NL80211_CHAN_WIDTH_10: - case NL80211_CHAN_WIDTH_5: - return false; /* unsupported for now */ default: vht_opclass = 0; break; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 63d145b524c9..5dbf3ef4b257 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ int cfg80211_wext_giwname(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - strcpy(wrqu->name, "IEEE 802.11"); + strscpy(wrqu->name, "IEEE 802.11"); return 0; } @@ -489,7 +490,8 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, if (addr) tx_key = false; - if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) + if (cfg80211_validate_key_settings(rdev, wdev, params, idx, + pairwise, addr)) return -EINVAL; err = 0; diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index af8762b24039..c31d2af5dd22 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -448,7 +449,7 @@ static int x25_setsockopt(struct socket *sock, int level, int optname, } static int x25_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; int val, len, rc = -ENOPROTOOPT; @@ -456,22 +457,17 @@ static int x25_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_X25 || optname != X25_QBITINCL) goto out; - rc = -EFAULT; - if (get_user(len, optlen)) - goto out; + len = opt->optlen; rc = -EINVAL; if (len < 0) goto out; len = min_t(unsigned int, len, sizeof(int)); - - rc = -EFAULT; - if (put_user(len, optlen)) - goto out; + opt->optlen = len; val = test_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); - rc = copy_to_user(optval, &val, len) ? -EFAULT : 0; + rc = copy_to_iter(&val, len, &opt->iter_out) != len ? -EFAULT : 0; out: return rc; } @@ -1753,7 +1749,7 @@ static const struct proto_ops x25_proto_ops = { .listen = x25_listen, .shutdown = sock_no_shutdown, .setsockopt = x25_setsockopt, - .getsockopt = x25_getsockopt, + .getsockopt_iter = x25_getsockopt, .sendmsg = x25_sendmsg, .recvmsg = x25_recvmsg, .mmap = sock_no_mmap, diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index f8c8a8c9dfba..b970f30ea9b9 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -1732,7 +1733,7 @@ struct xdp_statistics_v1 { }; static int xsk_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) + sockopt_t *opt) { struct sock *sk = sock->sk; struct xdp_sock *xs = xdp_sk(sk); @@ -1741,8 +1742,7 @@ static int xsk_getsockopt(struct socket *sock, int level, int optname, if (level != SOL_XDP) return -ENOPROTOOPT; - if (get_user(len, optlen)) - return -EFAULT; + len = opt->optlen; if (len < 0) return -EINVAL; @@ -1776,10 +1776,10 @@ static int xsk_getsockopt(struct socket *sock, int level, int optname, stats.tx_invalid_descs = xskq_nb_invalid_descs(xs->tx); mutex_unlock(&xs->mutex); - if (copy_to_user(optval, &stats, stats_size)) - return -EFAULT; - if (put_user(stats_size, optlen)) + if (copy_to_iter(&stats, stats_size, &opt->iter_out) != + stats_size) return -EFAULT; + opt->optlen = stats_size; return 0; } @@ -1828,10 +1828,9 @@ static int xsk_getsockopt(struct socket *sock, int level, int optname, to_copy = &off_v1; } - if (copy_to_user(optval, to_copy, len)) - return -EFAULT; - if (put_user(len, optlen)) + if (copy_to_iter(to_copy, len, &opt->iter_out) != len) return -EFAULT; + opt->optlen = len; return 0; } @@ -1848,10 +1847,9 @@ static int xsk_getsockopt(struct socket *sock, int level, int optname, mutex_unlock(&xs->mutex); len = sizeof(opts); - if (copy_to_user(optval, &opts, len)) - return -EFAULT; - if (put_user(len, optlen)) + if (copy_to_iter(&opts, len, &opt->iter_out) != len) return -EFAULT; + opt->optlen = len; return 0; } @@ -1952,7 +1950,7 @@ static const struct proto_ops xsk_proto_ops = { .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = xsk_setsockopt, - .getsockopt = xsk_getsockopt, + .getsockopt_iter = xsk_getsockopt, .sendmsg = xsk_sendmsg, .recvmsg = xsk_recvmsg, .mmap = xsk_mmap, diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index d981cfdd8535..1f28a9641571 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -239,7 +239,7 @@ int xp_assign_dev(struct xsk_buff_pool *pool, bpf.xsk.pool = pool; bpf.xsk.queue_id = queue_id; - netdev_ops_assert_locked(netdev); + netdev_assert_locked_ops_compat(netdev); err = netdev->netdev_ops->ndo_bpf(netdev, &bpf); if (err) goto err_unreg_pool; diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c index b8d2e6930041..670e3512fc09 100644 --- a/net/xfrm/xfrm_compat.c +++ b/net/xfrm/xfrm_compat.c @@ -94,7 +94,8 @@ static const int compat_msg_min[XFRM_NR_MSGTYPES] = { [XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32), [XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32), [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32), - [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping) + [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping), + [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state), }; static const struct nla_policy compat_policy[XFRMA_MAX+1] = { @@ -162,6 +163,7 @@ static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb, case XFRM_MSG_NEWAE: case XFRM_MSG_REPORT: case XFRM_MSG_MIGRATE: + case XFRM_MSG_MIGRATE_STATE: case XFRM_MSG_NEWSADINFO: case XFRM_MSG_NEWSPDINFO: case XFRM_MSG_MAPPING: @@ -498,6 +500,7 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src, case XFRM_MSG_GETAE: case XFRM_MSG_REPORT: case XFRM_MSG_MIGRATE: + case XFRM_MSG_MIGRATE_STATE: case XFRM_MSG_NEWSADINFO: case XFRM_MSG_GETSADINFO: case XFRM_MSG_NEWSPDINFO: diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 550457e4c4f0..630f3dd31cc5 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -229,7 +229,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur EXPORT_SYMBOL_GPL(validate_xmit_xfrm); int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, - struct xfrm_user_offload *xuo, + const struct xfrm_user_offload *xuo, struct netlink_ext_ack *extack) { int err; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 959544425692..0ea015e1880b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4530,7 +4530,7 @@ static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tm int match = 0; if (t->mode == m->mode && t->id.proto == m->proto && - (m->reqid == 0 || t->reqid == m->reqid)) { + (m->old_reqid == 0 || t->reqid == m->old_reqid)) { switch (t->mode) { case XFRM_MODE_TUNNEL: case XFRM_MODE_BEET: @@ -4624,7 +4624,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate, sizeof(m[i].old_saddr)) && m[i].proto == m[j].proto && m[i].mode == m[j].mode && - m[i].reqid == m[j].reqid && + m[i].old_reqid == m[j].old_reqid && m[i].old_family == m[j].old_family) { NL_SET_ERR_MSG(extack, "Entries in the MIGRATE attribute's list must be unique"); return -EINVAL; @@ -4635,6 +4635,21 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate, return 0; } +/* + * Fill migrate fields that are invariant in XFRM_MSG_MIGRATE: inherited + * from the existing SA unchanged. XFRM_MSG_MIGRATE_STATE can update these. + */ +static void xfrm_migrate_copy_old(const struct xfrm_state *x, + struct xfrm_migrate *mp) +{ + mp->msg_type = XFRM_MSG_MIGRATE; + mp->smark = x->props.smark; + mp->new_reqid = x->props.reqid; + mp->nat_keepalive_interval = x->nat_keepalive_interval; + mp->mapping_maxage = x->mapping_maxage; + mp->new_mark = &x->mark; +} + int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate, struct xfrm_kmaddress *k, struct net *net, @@ -4672,7 +4687,11 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, if ((x = xfrm_migrate_state_find(mp, net, if_id))) { x_cur[nx_cur] = x; nx_cur++; - xc = xfrm_state_migrate(x, mp, encap, net, xuo, extack); + mp->encap = encap; + mp->xuo = xuo; + xfrm_migrate_copy_old(x, mp); + + xc = xfrm_state_migrate(x, mp, net, extack); if (xc) { x_new[nx_new] = xc; nx_new++; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 589c3b6e4679..1df017871651 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1966,8 +1966,7 @@ static inline int clone_security(struct xfrm_state *x, struct xfrm_sec_ctx *secu } static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, - struct xfrm_encap_tmpl *encap, - struct xfrm_migrate *m) + const struct xfrm_migrate *m) { struct net *net = xs_net(orig); struct xfrm_state *x = xfrm_state_alloc(net); @@ -1975,13 +1974,25 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, goto out; memcpy(&x->id, &orig->id, sizeof(x->id)); - memcpy(&x->sel, &orig->sel, sizeof(x->sel)); + if (m->msg_type == XFRM_MSG_MIGRATE_STATE) { + if (m->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) { + u8 prefixlen = (m->new_family == AF_INET6) ? 128 : 32; + + x->sel = orig->sel; + x->sel.family = m->new_family; + x->sel.prefixlen_d = prefixlen; + x->sel.prefixlen_s = prefixlen; + x->sel.daddr = m->new_daddr; + x->sel.saddr = m->new_saddr; + } else { + x->sel = *m->new_sel; + } + } else { + x->sel = orig->sel; + } memcpy(&x->lft, &orig->lft, sizeof(x->lft)); x->props.mode = orig->props.mode; x->props.replay_window = orig->props.replay_window; - x->props.reqid = orig->props.reqid; - x->props.family = orig->props.family; - x->props.saddr = orig->props.saddr; if (orig->aalg) { x->aalg = xfrm_algo_auth_clone(orig->aalg); @@ -2010,16 +2021,12 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, } x->props.calgo = orig->props.calgo; - if (encap || orig->encap) { - if (encap) - x->encap = kmemdup(encap, sizeof(*x->encap), - GFP_KERNEL); - else - x->encap = kmemdup(orig->encap, sizeof(*x->encap), - GFP_KERNEL); - + if (m->encap) { + x->encap = kmemdup(m->encap, sizeof(*x->encap), GFP_KERNEL); if (!x->encap) goto error; + x->mapping_maxage = m->mapping_maxage; + x->nat_keepalive_interval = m->nat_keepalive_interval; } if (orig->security) @@ -2033,13 +2040,12 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, goto error; } - if (orig->replay_esn) { - if (xfrm_replay_clone(x, orig)) - goto error; - } + if (xfrm_replay_clone(x, orig)) + goto error; - memcpy(&x->mark, &orig->mark, sizeof(x->mark)); - memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark)); + x->mark = m->new_mark ? *m->new_mark : m->old_mark; + + x->props.smark = m->smark; x->props.flags = orig->props.flags; x->props.extra_flags = orig->props.extra_flags; @@ -2049,12 +2055,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, x->tfcpad = orig->tfcpad; x->replay_maxdiff = orig->replay_maxdiff; x->replay_maxage = orig->replay_maxage; - memcpy(&x->curlft, &orig->curlft, sizeof(x->curlft)); x->km.state = orig->km.state; x->km.seq = orig->km.seq; - x->replay = orig->replay; - x->preplay = orig->preplay; - x->mapping_maxage = orig->mapping_maxage; x->lastused = orig->lastused; x->new_mapping = 0; x->new_mapping_sport = 0; @@ -2066,7 +2068,7 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig, goto error; } - + x->props.reqid = m->new_reqid; x->props.family = m->new_family; memcpy(&x->id.daddr, &m->new_daddr, sizeof(x->id.daddr)); memcpy(&x->props.saddr, &m->new_saddr, sizeof(x->props.saddr)); @@ -2088,14 +2090,14 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n spin_lock_bh(&net->xfrm.xfrm_state_lock); - if (m->reqid) { + if (m->old_reqid) { h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr, - m->reqid, m->old_family); + m->old_reqid, m->old_family); hlist_for_each_entry(x, xfrm_state_deref_prot(net->xfrm.state_bydst, net) + h, bydst) { if (x->props.mode != m->mode || x->id.proto != m->proto) continue; - if (m->reqid && x->props.reqid != m->reqid) + if (m->old_reqid && x->props.reqid != m->old_reqid) continue; if (if_id != 0 && x->if_id != if_id) continue; @@ -2132,45 +2134,81 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n } EXPORT_SYMBOL(xfrm_migrate_state_find); -struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, - struct xfrm_migrate *m, - struct xfrm_encap_tmpl *encap, - struct net *net, - struct xfrm_user_offload *xuo, - struct netlink_ext_ack *extack) +struct xfrm_state *xfrm_state_migrate_create(struct xfrm_state *x, + const struct xfrm_migrate *m, + struct net *net, + struct netlink_ext_ack *extack) { struct xfrm_state *xc; - xc = xfrm_state_clone_and_setup(x, encap, m); - if (!xc) + xc = xfrm_state_clone_and_setup(x, m); + if (!xc) { + NL_SET_ERR_MSG(extack, "Failed to clone and setup state"); return NULL; - - if (xfrm_init_state(xc) < 0) - goto error; - - /* configure the hardware if offload is requested */ - if (xuo && xfrm_dev_state_add(net, xc, xuo, extack)) - goto error; - - /* add state */ - if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) { - /* a care is needed when the destination address of the - state is to be updated as it is a part of triplet */ - xfrm_state_insert(xc); - } else { - if (xfrm_state_add(xc) < 0) - goto error_add; } + if (xfrm_init_state(xc, extack) < 0) { + NL_SET_ERR_MSG_WEAK(extack, "Failed to initialize migrated state"); + goto error; + } + + /* configure the hardware if offload is requested */ + if (m->xuo && xfrm_dev_state_add(net, xc, m->xuo, extack)) + goto error; + return xc; -error_add: - if (xuo) - xfrm_dev_state_delete(xc); error: xc->km.state = XFRM_STATE_DEAD; xfrm_state_put(xc); return NULL; } +EXPORT_SYMBOL(xfrm_state_migrate_create); + +int xfrm_state_migrate_install(const struct xfrm_state *x, + struct xfrm_state *xc, + const struct xfrm_migrate *m, + struct netlink_ext_ack *extack) +{ + if (m->new_family == m->old_family && + xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) { + /* + * Care is needed when the destination address of the state is + * to be updated as it is a part of triplet. + */ + xfrm_state_insert(xc); + } else { + if (xfrm_state_add(xc) < 0) { + NL_SET_ERR_MSG(extack, "Failed to add migrated state"); + if (m->xuo) + xfrm_dev_state_delete(xc); + xc->km.state = XFRM_STATE_DEAD; + xfrm_state_put(xc); + return -EEXIST; + } + } + + return 0; +} +EXPORT_SYMBOL(xfrm_state_migrate_install); + +struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, + struct xfrm_migrate *m, + struct net *net, + struct netlink_ext_ack *extack) +{ + struct xfrm_state *xc; + + xc = xfrm_state_migrate_create(x, m, net, extack); + if (!xc) + return NULL; + + xfrm_migrate_sync(xc, x); + + if (xfrm_state_migrate_install(x, xc, m, extack) < 0) + return NULL; + + return xc; +} EXPORT_SYMBOL(xfrm_state_migrate); #endif @@ -3253,11 +3291,11 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack) EXPORT_SYMBOL(__xfrm_init_state); -int xfrm_init_state(struct xfrm_state *x) +int xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack) { int err; - err = __xfrm_init_state(x, NULL); + err = __xfrm_init_state(x, extack); if (err) return err; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 71a4b7278eba..5975849e5893 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -248,6 +248,72 @@ static inline int verify_replay(struct xfrm_usersa_info *p, return 0; } +static int verify_mtimer_thresh(bool has_encap, u8 dir, + struct netlink_ext_ack *extack) +{ + if (!has_encap) { + NL_SET_ERR_MSG(extack, + "MTIMER_THRESH requires encapsulation"); + return -EINVAL; + } + if (dir == XFRM_SA_DIR_OUT) { + NL_SET_ERR_MSG(extack, + "MTIMER_THRESH should not be set on output SA"); + return -EINVAL; + } + return 0; +} + +static int verify_xfrm_family(u16 family, struct netlink_ext_ack *extack) +{ + switch (family) { + case AF_INET: + return 0; + case AF_INET6: +#if IS_ENABLED(CONFIG_IPV6) + return 0; +#else + NL_SET_ERR_MSG(extack, "IPv6 support disabled"); + return -EAFNOSUPPORT; +#endif + default: + NL_SET_ERR_MSG(extack, "Invalid address family"); + return -EINVAL; + } +} + +static int verify_selector_prefixlen(u16 family, + const struct xfrm_selector *sel, + struct netlink_ext_ack *extack) +{ + switch (family) { + case AF_UNSPEC: + return 0; + case AF_INET: + if (sel->prefixlen_d > 32 || sel->prefixlen_s > 32) { + NL_SET_ERR_MSG(extack, + "Invalid prefix length in selector (must be <= 32 for IPv4)"); + return -EINVAL; + } + return 0; + case AF_INET6: +#if IS_ENABLED(CONFIG_IPV6) + if (sel->prefixlen_d > 128 || sel->prefixlen_s > 128) { + NL_SET_ERR_MSG(extack, + "Invalid prefix length in selector (must be <= 128 for IPv6)"); + return -EINVAL; + } + return 0; +#else + NL_SET_ERR_MSG(extack, "IPv6 support disabled"); + return -EAFNOSUPPORT; +#endif + default: + NL_SET_ERR_MSG(extack, "Invalid address family in selector"); + return -EINVAL; + } +} + static int verify_newsa_info(struct xfrm_usersa_info *p, struct nlattr **attrs, struct netlink_ext_ack *extack) @@ -256,58 +322,16 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, u8 sa_dir = nla_get_u8_default(attrs[XFRMA_SA_DIR], 0); u16 family = p->sel.family; - err = -EINVAL; - switch (p->family) { - case AF_INET: - break; - - case AF_INET6: -#if IS_ENABLED(CONFIG_IPV6) - break; -#else - err = -EAFNOSUPPORT; - NL_SET_ERR_MSG(extack, "IPv6 support disabled"); + err = verify_xfrm_family(p->family, extack); + if (err) goto out; -#endif - - default: - NL_SET_ERR_MSG(extack, "Invalid address family"); - goto out; - } if (!family && !(p->flags & XFRM_STATE_AF_UNSPEC)) family = p->family; - switch (family) { - case AF_UNSPEC: - break; - - case AF_INET: - if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32) { - NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 32 for IPv4)"); - goto out; - } - - break; - - case AF_INET6: -#if IS_ENABLED(CONFIG_IPV6) - if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128) { - NL_SET_ERR_MSG(extack, "Invalid prefix length in selector (must be <= 128 for IPv6)"); - goto out; - } - - break; -#else - NL_SET_ERR_MSG(extack, "IPv6 support disabled"); - err = -EAFNOSUPPORT; + err = verify_selector_prefixlen(family, &p->sel, extack); + if (err) goto out; -#endif - - default: - NL_SET_ERR_MSG(extack, "Invalid address family in selector"); - goto out; - } err = -EINVAL; switch (p->id.proto) { @@ -455,18 +479,9 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, err = 0; if (attrs[XFRMA_MTIMER_THRESH]) { - if (!attrs[XFRMA_ENCAP]) { - NL_SET_ERR_MSG(extack, "MTIMER_THRESH attribute can only be set on ENCAP states"); - err = -EINVAL; + err = verify_mtimer_thresh(!!attrs[XFRMA_ENCAP], sa_dir, extack); + if (err) goto out; - } - - if (sa_dir == XFRM_SA_DIR_OUT) { - NL_SET_ERR_MSG(extack, - "MTIMER_THRESH attribute should not be set on output SA"); - err = -EINVAL; - goto out; - } } if (sa_dir == XFRM_SA_DIR_OUT) { @@ -937,8 +952,14 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, attrs[XFRMA_ALG_COMP], extack))) goto error; - if (attrs[XFRMA_TFCPAD]) + if (attrs[XFRMA_TFCPAD]) { x->tfcpad = nla_get_u32(attrs[XFRMA_TFCPAD]); + if (x->tfcpad > IP_MAX_MTU) { + NL_SET_ERR_MSG(extack, "Excessive TFC padding"); + err = -EINVAL; + goto error; + } + } xfrm_mark_get(attrs, &x->mark); @@ -1180,6 +1201,16 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) return 0; } +static void xso_to_xuo(const struct xfrm_dev_offload *xso, + struct xfrm_user_offload *xuo) +{ + xuo->ifindex = xso->dev->ifindex; + if (xso->dir == XFRM_DEV_OFFLOAD_IN) + xuo->flags = XFRM_OFFLOAD_INBOUND; + if (xso->type == XFRM_DEV_OFFLOAD_PACKET) + xuo->flags |= XFRM_OFFLOAD_PACKET; +} + static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb) { struct xfrm_user_offload *xuo; @@ -1191,11 +1222,7 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb) xuo = nla_data(attr); memset(xuo, 0, sizeof(*xuo)); - xuo->ifindex = xso->dev->ifindex; - if (xso->dir == XFRM_DEV_OFFLOAD_IN) - xuo->flags = XFRM_OFFLOAD_INBOUND; - if (xso->type == XFRM_DEV_OFFLOAD_PACKET) - xuo->flags |= XFRM_OFFLOAD_PACKET; + xso_to_xuo(xso, xuo); return 0; } @@ -1320,7 +1347,7 @@ static int copy_to_user_encap(struct xfrm_encap_tmpl *ep, struct sk_buff *skb) return 0; } -static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m) +static int xfrm_smark_put(struct sk_buff *skb, const struct xfrm_mark *m) { int ret = 0; @@ -2267,9 +2294,8 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) { xfrm_dev_policy_delete(xp); - xfrm_dev_policy_free(xp); - security_xfrm_policy_free(xp->security); - kfree(xp); + xp->walk.dead = 1; + xfrm_policy_destroy(xp); return err; } @@ -3070,6 +3096,25 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh, } #ifdef CONFIG_XFRM_MIGRATE +static void copy_from_user_migrate_state(struct xfrm_migrate *ma, + const struct xfrm_user_migrate_state *um) +{ + memcpy(&ma->old_daddr, &um->id.daddr, sizeof(ma->old_daddr)); + memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr)); + memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr)); + + ma->proto = um->id.proto; + ma->new_reqid = um->new_reqid; + + ma->old_family = um->id.family; + ma->new_family = um->new_family; + + ma->old_mark = um->old_mark; + ma->flags = um->flags; + ma->new_sel = &um->new_sel; + ma->msg_type = XFRM_MSG_MIGRATE_STATE; +} + static int copy_from_user_migrate(struct xfrm_migrate *ma, struct xfrm_kmaddress *k, struct nlattr **attrs, int *num, @@ -3105,10 +3150,11 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma, ma->proto = um->proto; ma->mode = um->mode; - ma->reqid = um->reqid; + ma->old_reqid = um->reqid; ma->old_family = um->old_family; ma->new_family = um->new_family; + ma->msg_type = XFRM_MSG_MIGRATE; } *num = i; @@ -3119,7 +3165,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs, struct netlink_ext_ack *extack) { struct xfrm_userpolicy_id *pi = nlmsg_data(nlh); - struct xfrm_migrate m[XFRM_MAX_DEPTH]; + struct xfrm_migrate m[XFRM_MAX_DEPTH] = {}; struct xfrm_kmaddress km, *kmp; u8 type; int err; @@ -3172,7 +3218,298 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, kfree(xuo); return err; } + +static int build_migrate_state(struct sk_buff *skb, + const struct xfrm_user_migrate_state *um, + const struct xfrm_migrate *m, + u8 dir, u32 portid, u32 seq) +{ + int err; + struct nlmsghdr *nlh; + struct xfrm_user_migrate_state *hdr; + + nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_MIGRATE_STATE, + sizeof(struct xfrm_user_migrate_state), 0); + if (!nlh) + return -EMSGSIZE; + + hdr = nlmsg_data(nlh); + *hdr = *um; + hdr->new_sel = *m->new_sel; + + if (m->encap) { + err = nla_put(skb, XFRMA_ENCAP, sizeof(*m->encap), m->encap); + if (err) + goto out_cancel; + } + + if (m->xuo) { + err = nla_put(skb, XFRMA_OFFLOAD_DEV, sizeof(*m->xuo), m->xuo); + if (err) + goto out_cancel; + } + + if (m->new_mark) { + err = nla_put(skb, XFRMA_MARK, sizeof(*m->new_mark), + m->new_mark); + if (err) + goto out_cancel; + } + + err = xfrm_smark_put(skb, &m->smark); + if (err) + goto out_cancel; + + if (m->mapping_maxage) { + err = nla_put_u32(skb, XFRMA_MTIMER_THRESH, m->mapping_maxage); + if (err) + goto out_cancel; + } + + if (m->nat_keepalive_interval) { + err = nla_put_u32(skb, XFRMA_NAT_KEEPALIVE_INTERVAL, + m->nat_keepalive_interval); + if (err) + goto out_cancel; + } + + if (dir) { + err = nla_put_u8(skb, XFRMA_SA_DIR, dir); + if (err) + goto out_cancel; + } + + nlmsg_end(skb, nlh); + return 0; + +out_cancel: + nlmsg_cancel(skb, nlh); + return err; +} + +static unsigned int xfrm_migrate_state_msgsize(const struct xfrm_migrate *m, + u8 dir) +{ + return NLMSG_ALIGN(sizeof(struct xfrm_user_migrate_state)) + + (m->encap ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0) + + (m->xuo ? nla_total_size(sizeof(struct xfrm_user_offload)) : 0) + + (m->new_mark ? nla_total_size(sizeof(struct xfrm_mark)) : 0) + + ((m->smark.v | m->smark.m) ? nla_total_size(sizeof(u32)) * 2 : 0) + + (m->mapping_maxage ? nla_total_size(sizeof(u32)) : 0) + + (m->nat_keepalive_interval ? nla_total_size(sizeof(u32)) : 0) + + (dir ? nla_total_size(sizeof(u8)) : 0); /* XFRMA_SA_DIR */ +} + +static int xfrm_send_migrate_state(struct net *net, + const struct xfrm_user_migrate_state *um, + const struct xfrm_migrate *m, + u8 dir, u32 portid, u32 seq) +{ + int err; + struct sk_buff *skb; + + skb = nlmsg_new(xfrm_migrate_state_msgsize(m, dir), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + err = build_migrate_state(skb, um, m, dir, portid, seq); + if (err < 0) { + kfree_skb(skb); + return err; + } + + return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE); +} + +static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh, + struct nlattr **attrs, struct netlink_ext_ack *extack) +{ + struct xfrm_user_migrate_state *um = nlmsg_data(nlh); + struct net *net = sock_net(skb->sk); + struct xfrm_user_offload xuo = {}; + struct xfrm_migrate m = {}; + struct xfrm_state *xc; + struct xfrm_state *x; + int err; + + if (!um->id.spi) { + NL_SET_ERR_MSG(extack, "Invalid SPI 0x0"); + return -EINVAL; + } + + if (um->reserved) { + NL_SET_ERR_MSG(extack, "Reserved field must be zero"); + return -EINVAL; + } + + if (um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS) { + NL_SET_ERR_MSG_FMT(extack, "Unknown flags: 0x%x", + um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS); + return -EINVAL; + } + + err = verify_xfrm_family(um->new_family, extack); + if (err) + return err; + + if (!(um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL)) { + err = verify_selector_prefixlen(um->new_sel.family, + &um->new_sel, extack); + if (err) + return err; + } + + copy_from_user_migrate_state(&m, um); + + x = xfrm_state_lookup(net, m.old_mark.v & m.old_mark.m, + &um->id.daddr, um->id.spi, + um->id.proto, um->id.family); + if (!x) { + NL_SET_ERR_MSG(extack, "Can not find state"); + return -ESRCH; + } + + if (um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) { + u8 prefixlen = (x->props.family == AF_INET6) ? 128 : 32; + + if (x->sel.prefixlen_s != x->sel.prefixlen_d || + x->sel.prefixlen_d != prefixlen || + !xfrm_addr_equal(&x->sel.daddr, &x->id.daddr, x->props.family) || + !xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family)) { + NL_SET_ERR_MSG(extack, + "SA selector is not a single-host match for SA addresses"); + err = -EINVAL; + goto out; + } + } + + if (attrs[XFRMA_ENCAP]) { + m.encap = nla_data(attrs[XFRMA_ENCAP]); + if (m.encap->encap_type == 0) { + m.encap = NULL; /* sentinel: remove encap */ + } else if (m.encap->encap_type != UDP_ENCAP_ESPINUDP) { + NL_SET_ERR_MSG(extack, "Unsupported encapsulation type"); + err = -EINVAL; + goto out; + } + } else { + m.encap = x->encap; /* omit-to-inherit */ + } + + if (attrs[XFRMA_MTIMER_THRESH]) { + err = verify_mtimer_thresh(!!m.encap, x->dir, extack); + if (err) + goto out; + } + + if (nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL], 0) && !m.encap) { + NL_SET_ERR_MSG(extack, + "NAT_KEEPALIVE_INTERVAL requires encapsulation"); + err = -EINVAL; + goto out; + } + + if (attrs[XFRMA_OFFLOAD_DEV]) { + m.xuo = nla_data(attrs[XFRMA_OFFLOAD_DEV]); + } else { + bool inherit_offload = !(um->flags & XFRM_MIGRATE_STATE_CLEAR_OFFLOAD); + + if (inherit_offload && x->xso.dev) { + xso_to_xuo(&x->xso, &xuo); + m.xuo = &xuo; + } + } + + if (attrs[XFRMA_MARK]) + m.new_mark = nla_data(attrs[XFRMA_MARK]); + + if (attrs[XFRMA_SET_MARK]) + xfrm_smark_init(attrs, &m.smark); + else + m.smark = x->props.smark; + + m.mapping_maxage = nla_get_u32_default(attrs[XFRMA_MTIMER_THRESH], + x->mapping_maxage); + m.nat_keepalive_interval = nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL], + x->nat_keepalive_interval); + + if (m.new_family != um->id.family || + !xfrm_addr_equal(&m.new_daddr, &um->id.daddr, um->id.family)) { + u32 new_mark_key = m.new_mark ? m.new_mark->v & m.new_mark->m : + m.old_mark.v & m.old_mark.m; + struct xfrm_state *x_new; + + x_new = xfrm_state_lookup(net, new_mark_key, &m.new_daddr, + um->id.spi, um->id.proto, m.new_family); + if (x_new) { + xfrm_state_put(x_new); + NL_SET_ERR_MSG(extack, "New SA tuple already occupied"); + err = -EEXIST; + goto out; + } + } + + xc = xfrm_state_migrate_create(x, &m, net, extack); + if (!xc) { + NL_SET_ERR_MSG_WEAK(extack, "State migration clone failed"); + err = -EINVAL; + goto out; + } + + spin_lock_bh(&x->lock); + if (x->km.state != XFRM_STATE_VALID) { + spin_unlock_bh(&x->lock); + NL_SET_ERR_MSG(extack, "State already deleted"); + err = -ESRCH; + goto out_xc; + } + xfrm_migrate_sync(xc, x); /* to prevent SN/IV reuse */ + __xfrm_state_delete(x); + spin_unlock_bh(&x->lock); + + err = xfrm_state_migrate_install(x, xc, &m, extack); + if (err < 0) { + /* + * Should not occur: pre-check above ensures the new tuple is + * free under xfrm_cfg_mutex. Both SAs are gone if it does; + * restoring x would risk SN/IV reuse. + */ + goto out; + } + + /* Restore encap cleared by sentinel (type=0) during migration. */ + if (attrs[XFRMA_ENCAP]) + m.encap = nla_data(attrs[XFRMA_ENCAP]); + + m.new_sel = &xc->sel; + m.mapping_maxage = xc->mapping_maxage; + m.nat_keepalive_interval = xc->nat_keepalive_interval; + + err = xfrm_send_migrate_state(net, um, &m, xc->dir, + nlh->nlmsg_pid, nlh->nlmsg_seq); + if (err < 0) { + NL_SET_ERR_MSG(extack, "Failed to send migration notification"); + err = 0; + } + +out: + xfrm_state_put(x); + return err; +out_xc: + xc->km.state = XFRM_STATE_DEAD; + xfrm_state_put(xc); + xfrm_state_put(x); + return err; +} + #else +static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh, + struct nlattr **attrs, struct netlink_ext_ack *extack) +{ + NL_SET_ERR_MSG(extack, "XFRM_MSG_MIGRATE_STATE is not supported"); + return -ENOPROTOOPT; +} + static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs, struct netlink_ext_ack *extack) { @@ -3188,7 +3525,7 @@ static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *sk memset(&um, 0, sizeof(um)); um.proto = m->proto; um.mode = m->mode; - um.reqid = m->reqid; + um.reqid = m->old_reqid; um.old_family = m->old_family; memcpy(&um.old_daddr, &m->old_daddr, sizeof(um.old_daddr)); memcpy(&um.old_saddr, &m->old_saddr, sizeof(um.old_saddr)); @@ -3325,6 +3662,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = { [XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping), [XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default), [XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default), + [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state), }; EXPORT_SYMBOL_GPL(xfrm_msg_min); @@ -3418,6 +3756,7 @@ static const struct xfrm_link { [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, [XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default }, [XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default }, + [XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate_state }, }; static int xfrm_reject_unused_attr(int type, struct nlattr **attrs, @@ -3449,6 +3788,30 @@ static int xfrm_reject_unused_attr(int type, struct nlattr **attrs, } } + if (type == XFRM_MSG_MIGRATE_STATE) { + int i; + + for (i = 0; i <= XFRMA_MAX; i++) { + if (!attrs[i]) + continue; + + switch (i) { + case XFRMA_MARK: + case XFRMA_ENCAP: + case XFRMA_OFFLOAD_DEV: + case XFRMA_SET_MARK: + case XFRMA_SET_MARK_MASK: + case XFRMA_MTIMER_THRESH: + case XFRMA_NAT_KEEPALIVE_INTERVAL: + break; + default: + NL_SET_ERR_MSG_ATTR(extack, attrs[i], + "Unsupported attribute in XFRM_MSG_MIGRATE_STATE"); + return -EINVAL; + } + } + } + return 0; } diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 2c0b07f9fbbd..655d2616c9d2 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -128,6 +128,7 @@ static const struct nlmsg_perm nlmsg_xfrm_perms[] = { { XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ }, { XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ }, + { XFRM_MSG_MIGRATE_STATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, }; static const struct nlmsg_perm nlmsg_audit_perms[] = { @@ -203,7 +204,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(XFRM_MSG_MAX != XFRM_MSG_GETDEFAULT); + BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MIGRATE_STATE); if (selinux_policycap_netlink_xperm()) { *perm = NETLINK_XFRM_SOCKET__NLMSG; diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 7e46ca4cd31b..757ce5e9426e 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -1526,6 +1526,8 @@ enum { IFLA_BOND_MISSED_MAX, IFLA_BOND_NS_IP6_TARGET, IFLA_BOND_COUPLED_CONTROL, + IFLA_BOND_BROADCAST_NEIGH, + IFLA_BOND_LACP_STRICT, __IFLA_BOND_MAX, }; diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h index 7df1056a35fd..2f3ab75e8cc0 100644 --- a/tools/include/uapi/linux/netdev.h +++ b/tools/include/uapi/linux/netdev.h @@ -97,6 +97,8 @@ enum { }; enum { + NETDEV_A_IO_URING_PROVIDER_INFO_RX_BUF_LEN = 1, + __NETDEV_A_IO_URING_PROVIDER_INFO_MAX, NETDEV_A_IO_URING_PROVIDER_INFO_MAX = (__NETDEV_A_IO_URING_PROVIDER_INFO_MAX - 1) }; diff --git a/tools/net/ynl/pyynl/lib/nlspec.py b/tools/net/ynl/pyynl/lib/nlspec.py index fcffeb5b7ba3..0469a0e270d0 100644 --- a/tools/net/ynl/pyynl/lib/nlspec.py +++ b/tools/net/ynl/pyynl/lib/nlspec.py @@ -439,6 +439,11 @@ class SpecFamily(SpecElement): # To be loaded dynamically as needed jsonschema = None + try: + _yaml_loader = pyyaml.CSafeLoader + except AttributeError: + _yaml_loader = pyyaml.SafeLoader + def __init__(self, spec_path, schema_path=None, exclude_ops=None): with open(spec_path, "r", encoding='utf-8') as stream: prefix = '# SPDX-License-Identifier: ' @@ -448,7 +453,7 @@ class SpecFamily(SpecElement): self.license = first[len(prefix):] stream.seek(0) - spec = pyyaml.safe_load(stream) + spec = pyyaml.load(stream, Loader=self._yaml_loader) self.fixed_header = None self._resolution_list = [] @@ -464,7 +469,7 @@ class SpecFamily(SpecElement): schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml' if schema_path: with open(schema_path, "r", encoding='utf-8') as stream: - schema = pyyaml.safe_load(stream) + schema = pyyaml.load(stream, Loader=self._yaml_loader) if SpecFamily.jsonschema is None: SpecFamily.jsonschema = importlib.import_module("jsonschema") diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py index 010aac0c6c67..092d132edec1 100644 --- a/tools/net/ynl/pyynl/lib/ynl.py +++ b/tools/net/ynl/pyynl/lib/ynl.py @@ -682,6 +682,7 @@ class YnlFamily(SpecFamily): ynl.ntf_subscribe(mcast_name) -- join a multicast group ynl.ntf_listen_all_nsid() -- listen on all netns + ynl.ntf_bind(addr=(0, 0)) -- bind socket for unicast notifications ynl.check_ntf() -- drain pending notifications ynl.poll_ntf(duration=None) -- yield notifications @@ -767,6 +768,10 @@ class YnlFamily(SpecFamily): return None return None + def ntf_bind(self, addr=(0, 0)): + """Bind socket for receiving unicast notifications.""" + self.sock.bind(addr) + def set_recv_dbg(self, enabled): self._recv_dbg = enabled diff --git a/tools/net/ynl/ynltool/page-pool.c b/tools/net/ynl/ynltool/page-pool.c index 4b24492abab7..9487eda6b3aa 100644 --- a/tools/net/ynl/ynltool/page-pool.c +++ b/tools/net/ynl/ynltool/page-pool.c @@ -327,7 +327,9 @@ static void aggregate_device_stats(struct pp_stats_array *a, static int do_stats(int argc, char **argv) { + struct netdev_page_pool_stats_get_req_dump pp_stat_req = {}; struct netdev_page_pool_stats_get_list *pp_stats; + struct netdev_page_pool_get_req_dump pp_req = {}; struct netdev_page_pool_get_list *pools; enum { GROUP_BY_DEVICE, @@ -374,14 +376,14 @@ static int do_stats(int argc, char **argv) return -1; } - pools = netdev_page_pool_get_dump(ys); + pools = netdev_page_pool_get_dump(ys, &pp_req); if (!pools) { p_err("failed to get page pools: %s", ys->err.msg); ret = -1; goto exit_close; } - pp_stats = netdev_page_pool_stats_get_dump(ys); + pp_stats = netdev_page_pool_stats_get_dump(ys, &pp_stat_req); if (!pp_stats) { p_err("failed to get page pool stats: %s", ys->err.msg); ret = -1; diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c index 6ed8e149e3d5..34737e8df6ea 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c @@ -9,7 +9,6 @@ #include "test_progs.h" #include "sockmap_helpers.h" #include "test_skmsg_load_helpers.skel.h" -#include "test_sockmap_ktls.skel.h" #define MAX_TEST_NAME 80 #define TCP_ULP 31 @@ -117,6 +116,68 @@ static void test_sockmap_ktls_update_fails_when_sock_has_ulp(int family, int map close(s); } +static void test_sockmap_ktls_enable_fails_when_in_sockmap(int family, int map) +{ + struct tls12_crypto_info_aes_gcm_128 crypto = { + .info = { + .version = TLS_1_2_VERSION, + .cipher_type = TLS_CIPHER_AES_GCM_128, + }, + }; + struct sockaddr_storage addr = {}; + socklen_t len = sizeof(addr); + struct sockaddr_in6 *v6; + struct sockaddr_in *v4; + int err, s, zero = 0; + + switch (family) { + case AF_INET: + v4 = (struct sockaddr_in *)&addr; + v4->sin_family = AF_INET; + break; + case AF_INET6: + v6 = (struct sockaddr_in6 *)&addr; + v6->sin6_family = AF_INET6; + break; + default: + PRINT_FAIL("unsupported socket family %d", family); + return; + } + + s = socket(family, SOCK_STREAM, 0); + if (!ASSERT_GE(s, 0, "socket")) + return; + + err = bind(s, (struct sockaddr *)&addr, len); + if (!ASSERT_OK(err, "bind")) + goto close; + + err = getsockname(s, (struct sockaddr *)&addr, &len); + if (!ASSERT_OK(err, "getsockname")) + goto close; + + err = connect(s, (struct sockaddr *)&addr, len); + if (!ASSERT_OK(err, "connect")) + goto close; + + /* Add the socket to the sockmap, attaching a psock. */ + err = bpf_map_update_elem(map, &zero, &s, BPF_ANY); + if (!ASSERT_OK(err, "sockmap update elem")) + goto close; + + /* Installing the TLS ULP is allowed, it does not touch the datapath. */ + err = setsockopt(s, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls")); + if (!ASSERT_OK(err, "setsockopt(TCP_ULP)")) + goto close; + + /* Enabling the TLS crypto datapath must be rejected. */ + err = setsockopt(s, SOL_TLS, TLS_TX, &crypto, sizeof(crypto)); + ASSERT_ERR(err, "setsockopt(TLS_TX)"); + +close: + close(s); +} + static const char *fmt_test_name(const char *subtest_name, int family, enum bpf_map_type map_type) { @@ -160,249 +221,6 @@ static void test_sockmap_ktls_offload(int family, int sotype) close(p); } -static void test_sockmap_ktls_tx_cork(int family, int sotype, bool push) -{ - int err, off; - int i, j; - int start_push = 0, push_len = 0; - int c = 0, p = 0, one = 1, sent, recvd; - int prog_fd, map_fd; - char msg[12] = "hello world\0"; - char rcv[20] = {0}; - struct test_sockmap_ktls *skel; - - skel = test_sockmap_ktls__open_and_load(); - if (!ASSERT_TRUE(skel, "open ktls skel")) - return; - - err = create_pair(family, sotype, &c, &p); - if (!ASSERT_OK(err, "create_pair()")) - goto out; - - prog_fd = bpf_program__fd(skel->progs.prog_sk_policy); - map_fd = bpf_map__fd(skel->maps.sock_map); - - err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); - if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) - goto out; - - err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); - if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) - goto out; - - err = init_ktls_pairs(c, p); - if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) - goto out; - - skel->bss->cork_byte = sizeof(msg); - if (push) { - start_push = 1; - push_len = 2; - } - skel->bss->push_start = start_push; - skel->bss->push_end = push_len; - - off = sizeof(msg) / 2; - sent = send(c, msg, off, 0); - if (!ASSERT_EQ(sent, off, "send(msg)")) - goto out; - - recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); - if (!ASSERT_EQ(-1, recvd, "expected no data")) - goto out; - - /* send remaining msg */ - sent = send(c, msg + off, sizeof(msg) - off, 0); - if (!ASSERT_EQ(sent, sizeof(msg) - off, "send remaining data")) - goto out; - - recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); - if (!ASSERT_OK(err, "recv(msg)") || - !ASSERT_EQ(recvd, sizeof(msg) + push_len, "check length mismatch")) - goto out; - - for (i = 0, j = 0; i < recvd;) { - /* skip checking the data that has been pushed in */ - if (i >= start_push && i <= start_push + push_len - 1) { - i++; - continue; - } - if (!ASSERT_EQ(rcv[i], msg[j], "data mismatch")) - goto out; - i++; - j++; - } -out: - if (c) - close(c); - if (p) - close(p); - test_sockmap_ktls__destroy(skel); -} - -static void test_sockmap_ktls_tx_no_buf(int family, int sotype, bool push) -{ - int c = -1, p = -1, one = 1, two = 2; - struct test_sockmap_ktls *skel; - unsigned char *data = NULL; - struct msghdr msg = {0}; - struct iovec iov[2]; - int prog_fd, map_fd; - int txrx_buf = 1024; - int iov_length = 8192; - int err; - - skel = test_sockmap_ktls__open_and_load(); - if (!ASSERT_TRUE(skel, "open ktls skel")) - return; - - err = create_pair(family, sotype, &c, &p); - if (!ASSERT_OK(err, "create_pair()")) - goto out; - - err = setsockopt(c, SOL_SOCKET, SO_RCVBUFFORCE, &txrx_buf, sizeof(int)); - err |= setsockopt(p, SOL_SOCKET, SO_SNDBUFFORCE, &txrx_buf, sizeof(int)); - if (!ASSERT_OK(err, "set buf limit")) - goto out; - - prog_fd = bpf_program__fd(skel->progs.prog_sk_policy_redir); - map_fd = bpf_map__fd(skel->maps.sock_map); - - err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); - if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) - goto out; - - err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); - if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) - goto out; - - err = bpf_map_update_elem(map_fd, &two, &p, BPF_NOEXIST); - if (!ASSERT_OK(err, "bpf_map_update_elem(p)")) - goto out; - - skel->bss->apply_bytes = 1024; - - err = init_ktls_pairs(c, p); - if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) - goto out; - - data = calloc(iov_length, sizeof(char)); - if (!data) - goto out; - - iov[0].iov_base = data; - iov[0].iov_len = iov_length; - iov[1].iov_base = data; - iov[1].iov_len = iov_length; - msg.msg_iov = iov; - msg.msg_iovlen = 2; - - for (;;) { - err = sendmsg(c, &msg, MSG_DONTWAIT); - if (err <= 0) - break; - } - -out: - if (data) - free(data); - if (c != -1) - close(c); - if (p != -1) - close(p); - - test_sockmap_ktls__destroy(skel); -} - -static void test_sockmap_ktls_tx_pop(int family, int sotype) -{ - char msg[37] = "0123456789abcdefghijklmnopqrstuvwxyz\0"; - int c = 0, p = 0, one = 1, sent, recvd; - struct test_sockmap_ktls *skel; - int prog_fd, map_fd; - char rcv[50] = {0}; - int err; - int i, m, r; - - skel = test_sockmap_ktls__open_and_load(); - if (!ASSERT_TRUE(skel, "open ktls skel")) - return; - - err = create_pair(family, sotype, &c, &p); - if (!ASSERT_OK(err, "create_pair()")) - goto out; - - prog_fd = bpf_program__fd(skel->progs.prog_sk_policy); - map_fd = bpf_map__fd(skel->maps.sock_map); - - err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); - if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) - goto out; - - err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); - if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) - goto out; - - err = init_ktls_pairs(c, p); - if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) - goto out; - - struct { - int pop_start; - int pop_len; - } pop_policy[] = { - /* trim the start */ - {0, 2}, - {0, 10}, - {1, 2}, - {1, 10}, - /* trim the end */ - {35, 2}, - /* New entries should be added before this line */ - {-1, -1}, - }; - - i = 0; - while (pop_policy[i].pop_start >= 0) { - skel->bss->pop_start = pop_policy[i].pop_start; - skel->bss->pop_end = pop_policy[i].pop_len; - - sent = send(c, msg, sizeof(msg), 0); - if (!ASSERT_EQ(sent, sizeof(msg), "send(msg)")) - goto out; - - recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); - if (!ASSERT_EQ(recvd, sizeof(msg) - pop_policy[i].pop_len, "pop len mismatch")) - goto out; - - /* verify the data - * msg: 0123456789a bcdefghij klmnopqrstuvwxyz - * | | - * popped data - */ - for (m = 0, r = 0; m < sizeof(msg);) { - /* skip checking the data that has been popped */ - if (m >= pop_policy[i].pop_start && - m <= pop_policy[i].pop_start + pop_policy[i].pop_len - 1) { - m++; - continue; - } - - if (!ASSERT_EQ(msg[m], rcv[r], "data mismatch")) - goto out; - m++; - r++; - } - i++; - } -out: - if (c) - close(c); - if (p) - close(p); - test_sockmap_ktls__destroy(skel); -} - static void run_tests(int family, enum bpf_map_type map_type) { int map; @@ -414,124 +232,16 @@ static void run_tests(int family, enum bpf_map_type map_type) if (test__start_subtest(fmt_test_name("update_fails_when_sock_has_ulp", family, map_type))) test_sockmap_ktls_update_fails_when_sock_has_ulp(family, map); + if (test__start_subtest(fmt_test_name("enable_fails_when_in_sockmap", family, map_type))) + test_sockmap_ktls_enable_fails_when_in_sockmap(family, map); + close(map); } -/* - * Regression test for the KTLS + sockmap (verdict) reverse-order UAF. - * - * Vulnerable sequence: - * 1. Insert receiver socket into sockmap with BPF_SK_SKB_VERDICT program. - * sk->sk_data_ready becomes sk_psock_verdict_data_ready. - * 2. Configure TLS RX: tls_sw_strparser_arm() saves - * sk_psock_verdict_data_ready as rx_ctx->saved_data_ready. - * - * When data arrives, tls_rx_msg_ready() calls saved_data_ready() = - * sk_psock_verdict_data_ready(), which calls tcp_read_skb() and drains - * sk_receive_queue via __skb_unlink() without advancing copied_seq. - * tls_strp_msg_load() then finds the queue empty while tcp_inq() is still - * non-zero, hits WARN_ON_ONCE(!first), and leaves a dangling frag_list - * pointer that tls_decrypt_sg() walks — a use-after-free. - * - * The fix adds a tls_sw_has_ctx_rx() check to sk_psock_verdict_data_ready(), - * mirroring what sk_psock_strp_data_ready() already does: when a TLS RX - * context is present, defer to psock->saved_data_ready (sock_def_readable) - * instead of calling tcp_read_skb(), so TLS retains sole ownership of the - * receive queue. Data is then decrypted and returned correctly by - * tls_sw_recvmsg(). - */ -static void test_sockmap_ktls_verdict_with_tls_rx(int family, int sotype) -{ - struct tls12_crypto_info_aes_gcm_128 crypto_info = {}; - char send_buf[] = "hello ktls sockmap reverse order"; - char recv_buf[sizeof(send_buf)] = {}; - struct test_sockmap_ktls *skel; - int c = -1, p = -1, zero = 0; - int prog_fd, map_fd; - ssize_t n; - int err; - - skel = test_sockmap_ktls__open_and_load(); - if (!ASSERT_TRUE(skel, "open_and_load")) - return; - - err = create_pair(family, sotype, &c, &p); - if (!ASSERT_OK(err, "create_pair")) - goto out; - - prog_fd = bpf_program__fd(skel->progs.prog_skb_verdict_pass); - map_fd = bpf_map__fd(skel->maps.sock_map_verdict); - - err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_SKB_VERDICT, 0); - if (!ASSERT_OK(err, "bpf_prog_attach sk_skb verdict")) - goto out; - - /* Step 1: configure TLS TX on sender (no sockmap involvement) */ - err = setsockopt(c, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls")); - if (!ASSERT_OK(err, "setsockopt(TCP_ULP) client")) - goto out; - - crypto_info.info.version = TLS_1_2_VERSION; - crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; - memset(crypto_info.key, 0x01, sizeof(crypto_info.key)); - memset(crypto_info.salt, 0x02, sizeof(crypto_info.salt)); - - err = setsockopt(c, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)); - if (!ASSERT_OK(err, "setsockopt(TLS_TX)")) - goto out; - - /* Step 2: insert receiver into sockmap BEFORE TLS RX */ - err = bpf_map_update_elem(map_fd, &zero, &p, BPF_NOEXIST); - if (!ASSERT_OK(err, "bpf_map_update_elem")) - goto out; - - /* Step 3: configure TLS RX AFTER sockmap insertion */ - err = setsockopt(p, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls")); - if (!ASSERT_OK(err, "setsockopt(TCP_ULP) server")) - goto out; - - err = setsockopt(p, SOL_TLS, TLS_RX, &crypto_info, sizeof(crypto_info)); - if (!ASSERT_OK(err, "setsockopt(TLS_RX)")) - goto out; - - /* - * A buggy kernel hits WARN_ON_ONCE in tls_strp_load_anchor_with_queue - * and may UAF in tls_decrypt_sg here. With the fix, - * sk_psock_verdict_data_ready defers to sock_def_readable and TLS - * decrypts the record normally. - */ - n = send(c, send_buf, sizeof(send_buf), 0); - if (!ASSERT_EQ(n, (ssize_t)sizeof(send_buf), "send")) - goto out; - - n = recv_timeout(p, recv_buf, sizeof(recv_buf), 0, 5); - if (!ASSERT_EQ(n, (ssize_t)sizeof(send_buf), "recv")) - goto out; - - ASSERT_OK(memcmp(send_buf, recv_buf, sizeof(send_buf)), "data integrity"); - -out: - if (c != -1) - close(c); - if (p != -1) - close(p); - test_sockmap_ktls__destroy(skel); -} - static void run_ktls_test(int family, int sotype) { if (test__start_subtest("tls simple offload")) test_sockmap_ktls_offload(family, sotype); - if (test__start_subtest("tls tx cork")) - test_sockmap_ktls_tx_cork(family, sotype, false); - if (test__start_subtest("tls tx cork with push")) - test_sockmap_ktls_tx_cork(family, sotype, true); - if (test__start_subtest("tls tx egress with no buf")) - test_sockmap_ktls_tx_no_buf(family, sotype, true); - if (test__start_subtest("tls tx with pop")) - test_sockmap_ktls_tx_pop(family, sotype); - if (test__start_subtest("tls verdict with tls rx")) - test_sockmap_ktls_verdict_with_tls_rx(family, sotype); } void test_sockmap_ktls(void) diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index 7950c504ed28..72875071d4f1 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -65,9 +65,9 @@ static void gen_eth_hdr(struct xsk_socket_info *xsk, struct ethhdr *eth_hdr) eth_hdr->h_proto = htons(ETH_P_LOOPBACK); } -static bool is_umem_valid(struct ifobject *ifobj) +static bool is_umem_valid(struct xsk_socket_info *xsk) { - return !!ifobj->umem->umem; + return !!xsk->umem->umem; } static u32 mode_to_xdp_flags(enum test_mode mode) @@ -213,6 +213,7 @@ 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; + struct xsk_umem_info *umem_real; ifobj->xsk = &ifobj->xsk_arr[0]; ifobj->use_poll = false; @@ -229,24 +230,30 @@ 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; - + umem_real = ifobj->xsk_arr[0].umem_real; + memset(umem_real, 0, sizeof(*umem_real)); for (j = 0; j < MAX_SOCKETS; j++) { - memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j])); - ifobj->xsk_arr[j].rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS; - ifobj->xsk_arr[j].batch_size = DEFAULT_BATCH_SIZE; - if (i == 0) - ifobj->xsk_arr[j].pkt_stream = test->tx_pkt_stream_default; - else - ifobj->xsk_arr[j].pkt_stream = test->rx_pkt_stream_default; + struct xsk_socket_info *xsk = &ifobj->xsk_arr[j]; - memcpy(ifobj->xsk_arr[j].src_mac, g_mac, ETH_ALEN); - memcpy(ifobj->xsk_arr[j].dst_mac, g_mac, ETH_ALEN); - ifobj->xsk_arr[j].src_mac[5] += ((j * 2) + 0); - ifobj->xsk_arr[j].dst_mac[5] += ((j * 2) + 1); + memset(xsk, 0, sizeof(*xsk)); + xsk->rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS; + if (j == 0) + xsk->umem_real = umem_real; + xsk->umem = umem_real; + xsk->batch_size = DEFAULT_BATCH_SIZE; + if (i == 0) + xsk->pkt_stream = test->tx_pkt_stream_default; + else + xsk->pkt_stream = test->rx_pkt_stream_default; + + memcpy(xsk->src_mac, g_mac, ETH_ALEN); + memcpy(xsk->dst_mac, g_mac, ETH_ALEN); + xsk->src_mac[5] += ((j * 2) + 0); + xsk->dst_mac[5] += ((j * 2) + 1); } + + ifobj->xsk->umem->num_frames = DEFAULT_UMEM_BUFFERS; + ifobj->xsk->umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; } if (ifobj_tx->hw_ring_size_supp) @@ -303,6 +310,18 @@ static void test_spec_reset(struct test_spec *test) __test_spec_init(test, test->ifobj_tx, test->ifobj_rx); } +static void test_spec_set_unaligned(struct test_spec *test) +{ + test->ifobj_tx->xsk->umem->unaligned_mode = true; + test->ifobj_rx->xsk->umem->unaligned_mode = true; +} + +static void test_spec_set_frame_size(struct test_spec *test, u32 size) +{ + test->ifobj_tx->xsk->umem->frame_size = size; + test->ifobj_rx->xsk->umem->frame_size = size; +} + static void test_spec_set_xdp_prog(struct test_spec *test, struct bpf_program *xdp_prog_rx, struct bpf_program *xdp_prog_tx, struct bpf_map *xskmap_rx, struct bpf_map *xskmap_tx) @@ -810,11 +829,11 @@ static bool is_frag_valid(struct xsk_umem_info *umem, u64 addr, u32 len, u32 exp { u32 seqnum, pkt_nb, *pkt_data, words_to_end, expected_seqnum; void *data = xsk_umem__get_data(umem->buffer, addr); + u64 umem_sz = umem_size(umem); addr -= umem->base_addr; - if (addr >= umem->num_frames * umem->frame_size || - addr + len > umem->num_frames * umem->frame_size) { + if (addr >= umem_sz || addr + len > umem_sz) { ksft_print_msg("Frag invalid addr: %llx len: %u\n", (unsigned long long)addr, len); return false; @@ -991,7 +1010,7 @@ static int __receive_pkts(struct test_spec *test, struct xsk_socket_info *xsk) return TEST_FAILURE; if (!ret) { - if (!is_umem_valid(test->ifobj_tx)) + if (!is_umem_valid(test->ifobj_tx->xsk)) return TEST_PASS; ksft_print_msg("ERROR: [%s] Poll timed out\n", __func__); @@ -1151,7 +1170,7 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b { u32 i, idx = 0, valid_pkts = 0, valid_frags = 0, buffer_len; struct pkt_stream *pkt_stream = xsk->pkt_stream; - struct xsk_umem_info *umem = ifobject->umem; + struct xsk_umem_info *umem = xsk->umem; bool use_poll = ifobject->use_poll; struct pollfd fds = { }; int ret; @@ -1210,7 +1229,7 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b while (nb_frags_left--) { struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i); - tx_desc->addr = pkt_get_addr(pkt, ifobject->umem); + tx_desc->addr = pkt_get_addr(pkt, umem); if (pkt_stream->verbatim) { tx_desc->len = pkt->len; tx_desc->options = pkt->options; @@ -1303,7 +1322,7 @@ bool all_packets_sent(struct test_spec *test, unsigned long *bitmap) static int send_pkts(struct test_spec *test, struct ifobject *ifobject) { - bool timeout = !is_umem_valid(test->ifobj_rx); + bool timeout = !is_umem_valid(test->ifobj_rx->xsk); DECLARE_BITMAP(bitmap, test->nb_sockets); u32 i, ret; @@ -1488,14 +1507,25 @@ static int xsk_configure(struct test_spec *test, struct ifobject *ifobject, static int thread_common_ops_tx(struct test_spec *test, struct ifobject *ifobject) { - int ret = xsk_configure(test, ifobject, test->ifobj_rx->umem, true); + struct xsk_umem_info *umem_rx, *umem_tx; + int ret; + if (!test->ifobj_rx || !test->ifobj_rx->xsk_arr[0].umem->umem) { + ksft_print_msg("Error: RX UMEM is not initialized before shared-UMEM TX setup\n"); + return -EINVAL; + } + + umem_rx = test->ifobj_rx->xsk_arr[0].umem; + umem_tx = ifobject->xsk_arr[0].umem_real; + memcpy(umem_tx, umem_rx, sizeof(*umem_tx)); + umem_tx->base_addr = 0; + umem_tx->next_buffer = 0; + + ret = xsk_configure(test, ifobject, umem_tx, true); if (ret) return ret; ifobject->xsk = &ifobject->xsk_arr[0]; ifobject->xskmap = test->ifobj_rx->xskmap; - memcpy(ifobject->umem, test->ifobj_rx->umem, sizeof(struct xsk_umem_info)); - ifobject->umem->base_addr = 0; return 0; } @@ -1548,31 +1578,37 @@ static int xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream static int thread_common_ops(struct test_spec *test, struct ifobject *ifobject) { + struct xsk_umem_info *umem = ifobject->xsk->umem; LIBBPF_OPTS(bpf_xdp_query_opts, opts); int mmap_flags; - u64 umem_sz; + u64 umem_sz, mmap_sz; void *bufs; int ret; u32 i; - umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; + umem_sz = umem_size(umem); mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; - if (ifobject->umem->unaligned_mode) + if (umem->unaligned_mode) mmap_flags |= MAP_HUGETLB | MAP_HUGE_2MB; if (ifobject->shared_umem) umem_sz *= 2; - bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); + mmap_sz = umem->unaligned_mode ? + ceil_u64(umem_sz, HUGEPAGE_SIZE) * HUGEPAGE_SIZE : umem_sz; + + bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); if (bufs == MAP_FAILED) return -errno; - ret = xsk_configure_umem(ifobject, ifobject->umem, bufs, umem_sz); + umem->mmap_size = mmap_sz; + + ret = xsk_configure_umem(ifobject, umem, bufs, umem_sz); if (ret) return ret; - ret = xsk_configure(test, ifobject, ifobject->umem, false); + ret = xsk_configure(test, ifobject, umem, false); if (ret) return ret; @@ -1581,14 +1617,13 @@ static int thread_common_ops(struct test_spec *test, struct ifobject *ifobject) if (!ifobject->rx_on) return 0; - ret = xsk_populate_fill_ring(ifobject->umem, ifobject->xsk->pkt_stream, + ret = xsk_populate_fill_ring(umem, ifobject->xsk->pkt_stream, ifobject->use_fill_ring); if (ret) return ret; for (i = 0; i < test->nb_sockets; i++) { - ifobject->xsk = &ifobject->xsk_arr[i]; - ret = xsk_update_xskmap(ifobject->xskmap, ifobject->xsk->xsk, i); + ret = xsk_update_xskmap(ifobject->xskmap, ifobject->xsk_arr[i].xsk, i); if (ret) return ret; } @@ -1675,14 +1710,10 @@ void *worker_testapp_validate_rx(void *arg) static void testapp_clean_xsk_umem(struct ifobject *ifobj) { - u64 umem_sz = ifobj->umem->num_frames * ifobj->umem->frame_size; + struct xsk_umem_info *umem = ifobj->xsk->umem; - if (ifobj->shared_umem) - umem_sz *= 2; - - umem_sz = ceil_u64(umem_sz, HUGEPAGE_SIZE) * HUGEPAGE_SIZE; - xsk_umem__delete(ifobj->umem->umem); - munmap(ifobj->umem->buffer, umem_sz); + xsk_umem__delete(umem->umem); + munmap(umem->buffer, umem->mmap_size); } static void handler(int signum) @@ -1825,8 +1856,7 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i if (!ifobj2) pthread_kill(t0, SIGUSR1); - else - pthread_join(t0, NULL); + pthread_join(t0, NULL); if (test->total_steps == test->current_step || test->fail) { clean_sockets(test, ifobj1); @@ -1845,8 +1875,8 @@ static int testapp_validate_traffic(struct test_spec *test) struct ifobject *ifobj_rx = test->ifobj_rx; struct ifobject *ifobj_tx = test->ifobj_tx; - if ((ifobj_rx->umem->unaligned_mode && !ifobj_rx->unaligned_supp) || - (ifobj_tx->umem->unaligned_mode && !ifobj_tx->unaligned_supp)) { + if ((ifobj_rx->xsk->umem->unaligned_mode && !ifobj_rx->unaligned_supp) || + (ifobj_tx->xsk->umem->unaligned_mode && !ifobj_tx->unaligned_supp)) { ksft_print_msg("No huge pages present.\n"); return TEST_SKIP; } @@ -1953,12 +1983,13 @@ int testapp_xdp_prog_cleanup(struct test_spec *test) int testapp_headroom(struct test_spec *test) { - test->ifobj_rx->umem->frame_headroom = UMEM_HEADROOM_TEST_SIZE; + test->ifobj_rx->xsk->umem->frame_headroom = UMEM_HEADROOM_TEST_SIZE; return testapp_validate_traffic(test); } int testapp_stats_rx_dropped(struct test_spec *test) { + struct xsk_umem_info *umem = test->ifobj_rx->xsk->umem; u32 umem_tr = test->ifobj_tx->umem_tailroom; if (test->mode == TEST_MODE_ZC) { @@ -1968,7 +1999,7 @@ int testapp_stats_rx_dropped(struct test_spec *test) if (pkt_stream_replace_half(test, (MIN_PKT_SIZE * 3) + umem_tr, 0)) return TEST_FAILURE; - test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size - + umem->frame_headroom = umem->frame_size - XDP_PACKET_HEADROOM - (MIN_PKT_SIZE * 2) - umem_tr; if (pkt_stream_receive_half(test)) return TEST_FAILURE; @@ -2025,8 +2056,7 @@ int testapp_stats_fill_empty(struct test_spec *test) int testapp_send_receive_unaligned(struct test_spec *test) { - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; + test_spec_set_unaligned(test); /* Let half of the packets straddle a 4K buffer boundary */ if (pkt_stream_replace_half(test, MIN_PKT_SIZE, -MIN_PKT_SIZE / 2)) return TEST_FAILURE; @@ -2037,8 +2067,7 @@ int testapp_send_receive_unaligned(struct test_spec *test) int testapp_send_receive_unaligned_mb(struct test_spec *test) { test->mtu = MAX_ETH_JUMBO_SIZE; - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; + test_spec_set_unaligned(test); if (pkt_stream_replace(test, DEFAULT_PKT_CNT, MAX_ETH_JUMBO_SIZE)) return TEST_FAILURE; return testapp_validate_traffic(test); @@ -2064,8 +2093,8 @@ int testapp_send_receive_mb(struct test_spec *test) int testapp_invalid_desc_mb(struct test_spec *test) { - struct xsk_umem_info *umem = test->ifobj_tx->umem; - u64 umem_size = umem->num_frames * umem->frame_size; + struct xsk_umem_info *umem = test->ifobj_tx->xsk->umem; + u64 umem_sz = umem_size(umem); struct pkt pkts[] = { /* Valid packet for synch to start with */ {0, MIN_PKT_SIZE, 0, true, 0}, @@ -2075,7 +2104,7 @@ int testapp_invalid_desc_mb(struct test_spec *test) {0, 0, 0, false, 0}, /* Invalid address in the second frame */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, - {umem_size, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {umem_sz, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, /* Invalid len in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, @@ -2105,8 +2134,8 @@ int testapp_invalid_desc_mb(struct test_spec *test) int testapp_invalid_desc(struct test_spec *test) { - struct xsk_umem_info *umem = test->ifobj_tx->umem; - u64 umem_size = umem->num_frames * umem->frame_size; + struct xsk_umem_info *umem = test->ifobj_tx->xsk->umem; + u64 umem_sz = umem_size(umem); struct pkt pkts[] = { /* Zero packet address allowed */ {0, MIN_PKT_SIZE, 0, true}, @@ -2117,11 +2146,11 @@ int testapp_invalid_desc(struct test_spec *test) /* Packet too large */ {0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false}, /* Up to end of umem allowed */ - {umem_size - MIN_PKT_SIZE - 2 * umem->frame_size, MIN_PKT_SIZE, 0, true}, + {umem_sz - MIN_PKT_SIZE - 2 * umem->frame_size, MIN_PKT_SIZE, 0, true}, /* After umem ends */ - {umem_size, MIN_PKT_SIZE, 0, false}, + {umem_sz, MIN_PKT_SIZE, 0, false}, /* Straddle the end of umem */ - {umem_size - MIN_PKT_SIZE / 2, MIN_PKT_SIZE, 0, false}, + {umem_sz - MIN_PKT_SIZE / 2, MIN_PKT_SIZE, 0, false}, /* Straddle a 4K boundary */ {0x1000 - MIN_PKT_SIZE / 2, MIN_PKT_SIZE, 0, false}, /* Straddle a 2K boundary */ @@ -2139,9 +2168,9 @@ int testapp_invalid_desc(struct test_spec *test) } if (test->ifobj_tx->shared_umem) { - pkts[4].offset += umem_size; - pkts[5].offset += umem_size; - pkts[6].offset += umem_size; + pkts[4].offset += umem_sz; + pkts[5].offset += umem_sz; + pkts[6].offset += umem_sz; } if (pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts))) @@ -2204,7 +2233,7 @@ int testapp_poll_txq_tmout(struct test_spec *test) { test->ifobj_tx->use_poll = true; /* create invalid frame by set umem frame_size and pkt length equal to 2048 */ - test->ifobj_tx->umem->frame_size = 2048; + test->ifobj_tx->xsk->umem->frame_size = 2048; if (pkt_stream_replace(test, 2 * DEFAULT_PKT_CNT, 2048)) return TEST_FAILURE; return testapp_validate_traffic_single_thread(test, test->ifobj_tx); @@ -2337,8 +2366,7 @@ int testapp_send_receive(struct test_spec *test) int testapp_send_receive_2k_frame(struct test_spec *test) { - test->ifobj_tx->umem->frame_size = 2048; - test->ifobj_rx->umem->frame_size = 2048; + test_spec_set_frame_size(test, 2048); if (pkt_stream_replace(test, DEFAULT_PKT_CNT, MIN_PKT_SIZE)) return TEST_FAILURE; return testapp_validate_traffic(test); @@ -2363,34 +2391,30 @@ int testapp_aligned_inv_desc(struct test_spec *test) int testapp_aligned_inv_desc_2k_frame(struct test_spec *test) { - test->ifobj_tx->umem->frame_size = 2048; - test->ifobj_rx->umem->frame_size = 2048; + test_spec_set_frame_size(test, 2048); return testapp_invalid_desc(test); } int testapp_unaligned_inv_desc(struct test_spec *test) { - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; + test_spec_set_unaligned(test); return testapp_invalid_desc(test); } int testapp_unaligned_inv_desc_4001_frame(struct test_spec *test) { - u64 page_size, umem_size; + u64 page_size, umem_sz; /* Odd frame size so the UMEM doesn't end near a page boundary. */ - test->ifobj_tx->umem->frame_size = 4001; - test->ifobj_rx->umem->frame_size = 4001; - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; + test_spec_set_frame_size(test, 4001); + test_spec_set_unaligned(test); /* This test exists to test descriptors that staddle the end of * the UMEM but not a page. */ page_size = sysconf(_SC_PAGESIZE); - umem_size = test->ifobj_tx->umem->num_frames * test->ifobj_tx->umem->frame_size; - assert(umem_size % page_size > MIN_PKT_SIZE); - assert(umem_size % page_size < page_size - MIN_PKT_SIZE); + umem_sz = umem_size(test->ifobj_tx->xsk->umem); + assert(umem_sz % page_size > MIN_PKT_SIZE); + assert(umem_sz % page_size < page_size - MIN_PKT_SIZE); return testapp_invalid_desc(test); } @@ -2402,8 +2426,7 @@ int testapp_aligned_inv_desc_mb(struct test_spec *test) int testapp_unaligned_inv_desc_mb(struct test_spec *test) { - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; + test_spec_set_unaligned(test); return testapp_invalid_desc_mb(test); } @@ -2447,9 +2470,9 @@ int testapp_hw_sw_max_ring_size(struct test_spec *test) test->total_steps = 2; test->ifobj_tx->ring.tx_pending = test->ifobj_tx->ring.tx_max_pending; test->ifobj_tx->ring.rx_pending = test->ifobj_tx->ring.rx_max_pending; - test->ifobj_rx->umem->num_frames = max_descs; - test->ifobj_rx->umem->fill_size = max_descs; - test->ifobj_rx->umem->comp_size = max_descs; + test->ifobj_rx->xsk->umem->num_frames = max_descs; + test->ifobj_rx->xsk->umem->fill_size = max_descs; + test->ifobj_rx->xsk->umem->comp_size = max_descs; test->ifobj_tx->xsk->batch_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; test->ifobj_rx->xsk->batch_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; @@ -2590,8 +2613,8 @@ struct ifobject *ifobject_create(void) if (!ifobj->xsk_arr) goto out_xsk_arr; - ifobj->umem = calloc(1, sizeof(*ifobj->umem)); - if (!ifobj->umem) + ifobj->xsk_arr[0].umem_real = calloc(1, sizeof(struct xsk_umem_info)); + if (!ifobj->xsk_arr[0].umem_real) goto out_umem; return ifobj; @@ -2605,7 +2628,9 @@ struct ifobject *ifobject_create(void) void ifobject_delete(struct ifobject *ifobj) { - free(ifobj->umem); + if (ifobj->xsk_arr) + free(ifobj->xsk_arr[0].umem_real); + free(ifobj->xsk_arr); free(ifobj); } diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.h b/tools/testing/selftests/bpf/prog_tests/test_xsk.h index 1ab8aee4ce56..4313d0d87235 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.h +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.h @@ -83,6 +83,7 @@ typedef int (*test_func_t)(struct test_spec *test); struct xsk_socket_info { struct xsk_ring_cons rx; struct xsk_ring_prod tx; + struct xsk_umem_info *umem_real; struct xsk_umem_info *umem; struct xsk_socket *xsk; struct pkt_stream *pkt_stream; @@ -102,6 +103,7 @@ struct xsk_umem_info { struct xsk_ring_cons cq; struct xsk_umem *umem; u64 next_buffer; + u64 mmap_size; u32 num_frames; u32 frame_headroom; void *buffer; @@ -123,7 +125,6 @@ struct ifobject { char ifname[MAX_INTERFACE_NAME_CHARS]; struct xsk_socket_info *xsk; struct xsk_socket_info *xsk_arr; - struct xsk_umem_info *umem; thread_func_t func_ptr; validation_func_t validation_func; struct xsk_xdp_progs *xdp_progs; diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_kern.h b/tools/testing/selftests/bpf/progs/test_sockmap_kern.h index f48f85f1bd70..284a2f2e50cf 100644 --- a/tools/testing/selftests/bpf/progs/test_sockmap_kern.h +++ b/tools/testing/selftests/bpf/progs/test_sockmap_kern.h @@ -85,13 +85,6 @@ struct { __type(value, int); } sock_skb_opts SEC(".maps"); -struct { - __uint(type, TEST_MAP_TYPE); - __uint(max_entries, 20); - __uint(key_size, sizeof(int)); - __uint(value_size, sizeof(int)); -} tls_sock_map SEC(".maps"); - SEC("sk_skb/stream_parser") int bpf_prog1(struct __sk_buff *skb) { @@ -135,55 +128,6 @@ int bpf_prog2(struct __sk_buff *skb) } -static inline void bpf_write_pass(struct __sk_buff *skb, int offset) -{ - int err = bpf_skb_pull_data(skb, 6 + offset); - void *data_end; - char *c; - - if (err) - return; - - c = (char *)(long)skb->data; - data_end = (void *)(long)skb->data_end; - - if (c + 5 + offset < data_end) - memcpy(c + offset, "PASS", 4); -} - -SEC("sk_skb/stream_verdict") -int bpf_prog3(struct __sk_buff *skb) -{ - int err, *f, ret = SK_PASS; - const int one = 1; - - f = bpf_map_lookup_elem(&sock_skb_opts, &one); - if (f && *f) { - __u64 flags = 0; - - ret = 0; - flags = *f; - - err = bpf_skb_adjust_room(skb, -13, 0, 0); - if (err) - return SK_DROP; - err = bpf_skb_adjust_room(skb, 4, 0, 0); - if (err) - return SK_DROP; - bpf_write_pass(skb, 0); -#ifdef SOCKMAP - return bpf_sk_redirect_map(skb, &tls_sock_map, ret, flags); -#else - return bpf_sk_redirect_hash(skb, &tls_sock_map, &ret, flags); -#endif - } - err = bpf_skb_adjust_room(skb, 4, 0, 0); - if (err) - return SK_DROP; - bpf_write_pass(skb, 13); - return ret; -} - SEC("sockops") int bpf_sockmap(struct bpf_sock_ops *skops) { diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c b/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c deleted file mode 100644 index facafeaf4620..000000000000 --- a/tools/testing/selftests/bpf/progs/test_sockmap_ktls.c +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include - -int cork_byte; -int push_start; -int push_end; -int apply_bytes; -int pop_start; -int pop_end; - -struct { - __uint(type, BPF_MAP_TYPE_SOCKMAP); - __uint(max_entries, 20); - __type(key, int); - __type(value, int); -} sock_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_SOCKMAP); - __uint(max_entries, 2); - __type(key, int); - __type(value, int); -} sock_map_verdict SEC(".maps"); - -SEC("sk_msg") -int prog_sk_policy(struct sk_msg_md *msg) -{ - if (cork_byte > 0) - bpf_msg_cork_bytes(msg, cork_byte); - if (push_start > 0 && push_end > 0) - bpf_msg_push_data(msg, push_start, push_end, 0); - if (pop_start >= 0 && pop_end > 0) - bpf_msg_pop_data(msg, pop_start, pop_end, 0); - - return SK_PASS; -} - -SEC("sk_msg") -int prog_sk_policy_redir(struct sk_msg_md *msg) -{ - int two = 2; - - bpf_msg_apply_bytes(msg, apply_bytes); - return bpf_msg_redirect_map(msg, &sock_map, two, 0); -} - -/* - * Verdict program for the reverse-order TLS/sockmap regression test. - * Returns SK_PASS so tcp_read_skb() drains the receive queue via - * sk_psock_verdict_recv() without calling tcp_eat_skb(), which is - * the precondition for the KTLS strparser frag_list UAF. - */ -SEC("sk_skb/verdict") -int prog_skb_verdict_pass(struct __sk_buff *skb) -{ - return SK_PASS; -} - -char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_sockmap.c b/tools/testing/selftests/bpf/test_sockmap.c index 76568db7a664..ac814eb63edb 100644 --- a/tools/testing/selftests/bpf/test_sockmap.c +++ b/tools/testing/selftests/bpf/test_sockmap.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -41,13 +40,6 @@ int running; static void running_handler(int a); -#ifndef TCP_ULP -# define TCP_ULP 31 -#endif -#ifndef SOL_TLS -# define SOL_TLS 282 -#endif - /* randomly selected ports for testing on lo */ #define S1_PORT 10000 #define S2_PORT 10001 @@ -63,10 +55,10 @@ int s1, s2, c1, c2, p1, p2; int test_cnt; int passed; int failed; -int map_fd[9]; -struct bpf_map *maps[9]; -struct bpf_program *progs[9]; -struct bpf_link *links[9]; +int map_fd[8]; +struct bpf_map *maps[8]; +struct bpf_program *progs[8]; +struct bpf_link *links[8]; int txmsg_pass; int txmsg_redir; @@ -81,10 +73,6 @@ int txmsg_start_pop; int txmsg_pop; int txmsg_ingress; int txmsg_redir_skb; -int txmsg_ktls_skb; -int txmsg_ktls_skb_drop; -int txmsg_ktls_skb_redir; -int ktls; int peek_flag; int skb_use_parser; int txmsg_omit_skb_parser; @@ -115,7 +103,6 @@ static const struct option long_options[] = { {"txmsg_pop", required_argument, NULL, 'x'}, {"txmsg_ingress", no_argument, &txmsg_ingress, 1 }, {"txmsg_redir_skb", no_argument, &txmsg_redir_skb, 1 }, - {"ktls", no_argument, &ktls, 1 }, {"peek", no_argument, &peek_flag, 1 }, {"txmsg_omit_skb_parser", no_argument, &txmsg_omit_skb_parser, 1}, {"whitelist", required_argument, NULL, 'n' }, @@ -183,7 +170,6 @@ static void test_reset(void) txmsg_pass = txmsg_drop = txmsg_redir = 0; txmsg_apply = txmsg_cork = 0; txmsg_ingress = txmsg_redir_skb = 0; - txmsg_ktls_skb = txmsg_ktls_skb_drop = txmsg_ktls_skb_redir = 0; txmsg_omit_skb_parser = 0; skb_use_parser = 0; } @@ -238,71 +224,6 @@ static void usage(char *argv[]) printf("\n"); } -char *sock_to_string(int s) -{ - if (s == c1) - return "client1"; - else if (s == c2) - return "client2"; - else if (s == s1) - return "server1"; - else if (s == s2) - return "server2"; - else if (s == p1) - return "peer1"; - else if (s == p2) - return "peer2"; - else - return "unknown"; -} - -static int sockmap_init_ktls(int verbose, int s) -{ - struct tls12_crypto_info_aes_gcm_128 tls_tx = { - .info = { - .version = TLS_1_2_VERSION, - .cipher_type = TLS_CIPHER_AES_GCM_128, - }, - }; - struct tls12_crypto_info_aes_gcm_128 tls_rx = { - .info = { - .version = TLS_1_2_VERSION, - .cipher_type = TLS_CIPHER_AES_GCM_128, - }, - }; - int so_buf = 6553500; - int err; - - err = setsockopt(s, 6, TCP_ULP, "tls", sizeof("tls")); - if (err) { - fprintf(stderr, "setsockopt: TCP_ULP(%s) failed with error %i\n", sock_to_string(s), err); - return -EINVAL; - } - err = setsockopt(s, SOL_TLS, TLS_TX, (void *)&tls_tx, sizeof(tls_tx)); - if (err) { - fprintf(stderr, "setsockopt: TLS_TX(%s) failed with error %i\n", sock_to_string(s), err); - return -EINVAL; - } - err = setsockopt(s, SOL_TLS, TLS_RX, (void *)&tls_rx, sizeof(tls_rx)); - if (err) { - fprintf(stderr, "setsockopt: TLS_RX(%s) failed with error %i\n", sock_to_string(s), err); - return -EINVAL; - } - err = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &so_buf, sizeof(so_buf)); - if (err) { - fprintf(stderr, "setsockopt: (%s) failed sndbuf with error %i\n", sock_to_string(s), err); - return -EINVAL; - } - err = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &so_buf, sizeof(so_buf)); - if (err) { - fprintf(stderr, "setsockopt: (%s) failed rcvbuf with error %i\n", sock_to_string(s), err); - return -EINVAL; - } - - if (verbose) - fprintf(stdout, "socket(%s) kTLS enabled\n", sock_to_string(s)); - return 0; -} static int sockmap_init_sockets(int verbose) { int i, err, one = 1; @@ -557,19 +478,6 @@ static int msg_verify_data(struct msghdr *msg, int size, int chunk_sz, for (i = 0, j = 0; i < msg->msg_iovlen && size; i++, j = 0) { unsigned char *d = msg->msg_iov[i].iov_base; - /* Special case test for skb ingress + ktls */ - if (i == 0 && txmsg_ktls_skb) { - if (msg->msg_iov[i].iov_len < 4) - return -EDATAINTEGRITY; - if (memcmp(d, "PASS", 4) != 0) { - fprintf(stderr, - "detected skb data error with skb ingress update @iov[%i]:%i \"%02x %02x %02x %02x\" != \"PASS\"\n", - i, 0, d[0], d[1], d[2], d[3]); - return -EDATAINTEGRITY; - } - j = 4; /* advance index past PASS header */ - } - for (; j < msg->msg_iov[i].iov_len && size; j++) { if (push > 0 && check_cnt == verify_push_start + verify_push_len - push) { @@ -849,21 +757,6 @@ static int sendmsg_test(struct sockmap_options *opt) else rx_fd = p2; - if (ktls) { - /* Redirecting into non-TLS socket which sends into a TLS - * socket is not a valid test. So in this case lets not - * enable kTLS but still run the test. - */ - if (!txmsg_redir || txmsg_ingress) { - err = sockmap_init_ktls(opt->verbose, rx_fd); - if (err) - return err; - } - err = sockmap_init_ktls(opt->verbose, c1); - if (err) - return err; - } - if (opt->tx_wait_mem) { struct timeval timeout; int rxtx_buf_len = 1024; @@ -882,7 +775,7 @@ static int sendmsg_test(struct sockmap_options *opt) rxpid = fork(); if (rxpid == 0) { - if (opt->drop_expected || txmsg_ktls_skb_drop) + if (opt->drop_expected) _exit(0); if (!iov_buf) /* zero bytes sent case */ @@ -1073,28 +966,8 @@ static int run_options(struct sockmap_options *options, int cg_fd, int test) return -1; } - /* Attach programs to TLS sockmap */ - if (txmsg_ktls_skb) { - if (!txmsg_omit_skb_parser) { - links[2] = bpf_program__attach_sockmap(progs[0], map_fd[8]); - if (!links[2]) { - fprintf(stderr, - "ERROR: bpf_program__attach_sockmap (TLS sockmap %i->%i): (%s)\n", - bpf_program__fd(progs[0]), map_fd[8], strerror(errno)); - return -1; - } - } - - links[3] = bpf_program__attach_sockmap(progs[2], map_fd[8]); - if (!links[3]) { - fprintf(stderr, "ERROR: bpf_program__attach_sockmap (TLS sockmap): (%s)\n", - strerror(errno)); - return -1; - } - } - /* Attach to cgroups */ - err = bpf_prog_attach(bpf_program__fd(progs[3]), cg_fd, BPF_CGROUP_SOCK_OPS, 0); + err = bpf_prog_attach(bpf_program__fd(progs[2]), cg_fd, BPF_CGROUP_SOCK_OPS, 0); if (err) { fprintf(stderr, "ERROR: bpf_prog_attach (groups): %d (%s)\n", err, strerror(errno)); @@ -1110,15 +983,15 @@ static int run_options(struct sockmap_options *options, int cg_fd, int test) /* Attach txmsg program to sockmap */ if (txmsg_pass) - tx_prog = progs[4]; + tx_prog = progs[3]; else if (txmsg_redir) - tx_prog = progs[5]; + tx_prog = progs[4]; else if (txmsg_apply) - tx_prog = progs[6]; + tx_prog = progs[5]; else if (txmsg_cork) - tx_prog = progs[7]; + tx_prog = progs[6]; else if (txmsg_drop) - tx_prog = progs[8]; + tx_prog = progs[7]; else tx_prog = NULL; @@ -1291,34 +1164,6 @@ static int run_options(struct sockmap_options *options, int cg_fd, int test) } } - if (txmsg_ktls_skb) { - int ingress = BPF_F_INGRESS; - - i = 0; - err = bpf_map_update_elem(map_fd[8], &i, &p2, BPF_ANY); - if (err) { - fprintf(stderr, - "ERROR: bpf_map_update_elem (c1 sockmap): %d (%s)\n", - err, strerror(errno)); - } - - if (txmsg_ktls_skb_redir) { - i = 1; - err = bpf_map_update_elem(map_fd[7], - &i, &ingress, BPF_ANY); - if (err) { - fprintf(stderr, - "ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s)\n", - err, strerror(errno)); - } - } - - if (txmsg_ktls_skb_drop) { - i = 1; - err = bpf_map_update_elem(map_fd[7], &i, &i, BPF_ANY); - } - } - if (txmsg_redir_skb) { int skb_fd = (test == SENDMSG || test == SENDPAGE) ? p2 : p1; @@ -1373,7 +1218,7 @@ static int run_options(struct sockmap_options *options, int cg_fd, int test) fprintf(stderr, "unknown test\n"); out: /* Detach and zero all the maps */ - bpf_prog_detach2(bpf_program__fd(progs[3]), cg_fd, BPF_CGROUP_SOCK_OPS); + bpf_prog_detach2(bpf_program__fd(progs[2]), cg_fd, BPF_CGROUP_SOCK_OPS); for (i = 0; i < ARRAY_SIZE(links); i++) { if (links[i]) @@ -1457,10 +1302,6 @@ static void test_options(char *options) append_str(options, "ingress,", OPTSTRING); if (txmsg_redir_skb) append_str(options, "redir_skb,", OPTSTRING); - if (txmsg_ktls_skb) - append_str(options, "ktls_skb,", OPTSTRING); - if (ktls) - append_str(options, "ktls,", OPTSTRING); if (peek_flag) append_str(options, "peek,", OPTSTRING); } @@ -1602,57 +1443,6 @@ static void test_txmsg_ingress_redir(int cgrp, struct sockmap_options *opt) test_send(opt, cgrp); } -static void test_txmsg_skb(int cgrp, struct sockmap_options *opt) -{ - bool data = opt->data_test; - int k = ktls; - - opt->data_test = true; - ktls = 1; - - txmsg_pass = txmsg_drop = 0; - txmsg_ingress = txmsg_redir = 0; - txmsg_ktls_skb = 1; - txmsg_pass = 1; - - /* Using data verification so ensure iov layout is - * expected from test receiver side. e.g. has enough - * bytes to write test code. - */ - opt->iov_length = 100; - opt->iov_count = 1; - opt->rate = 1; - test_exec(cgrp, opt); - - txmsg_ktls_skb_drop = 1; - test_exec(cgrp, opt); - - txmsg_ktls_skb_drop = 0; - txmsg_ktls_skb_redir = 1; - test_exec(cgrp, opt); - txmsg_ktls_skb_redir = 0; - - /* Tests that omit skb_parser */ - txmsg_omit_skb_parser = 1; - ktls = 0; - txmsg_ktls_skb = 0; - test_exec(cgrp, opt); - - txmsg_ktls_skb_drop = 1; - test_exec(cgrp, opt); - txmsg_ktls_skb_drop = 0; - - txmsg_ktls_skb_redir = 1; - test_exec(cgrp, opt); - - ktls = 1; - test_exec(cgrp, opt); - txmsg_omit_skb_parser = 0; - - opt->data_test = data; - ktls = k; -} - /* Test cork with hung data. This tests poor usage patterns where * cork can leave data on the ring if user program is buggy and * doesn't flush them somehow. They do take some time however @@ -1908,8 +1698,6 @@ static void test_txmsg_ingress_parser(int cgrp, struct sockmap_options *opt) { txmsg_pass = 1; skb_use_parser = 512; - if (ktls == 1) - skb_use_parser = 570; opt->iov_length = 256; opt->iov_count = 1; opt->rate = 2; @@ -1918,8 +1706,6 @@ static void test_txmsg_ingress_parser(int cgrp, struct sockmap_options *opt) static void test_txmsg_ingress_parser2(int cgrp, struct sockmap_options *opt) { - if (ktls == 1) - return; skb_use_parser = 10; opt->iov_length = 20; opt->iov_count = 1; @@ -1938,7 +1724,6 @@ char *map_names[] = { "sock_bytes", "sock_redir_flags", "sock_skb_opts", - "tls_sock_map", }; static int populate_progs(char *bpf_file) @@ -1988,7 +1773,6 @@ struct _test test[] = { {"txmsg test redirect wait send mem", test_txmsg_redir_wait_sndmem}, {"txmsg test drop", test_txmsg_drop}, {"txmsg test ingress redirect", test_txmsg_ingress_redir}, - {"txmsg test skb", test_txmsg_skb}, {"txmsg test apply", test_txmsg_apply}, {"txmsg test cork", test_txmsg_cork}, {"txmsg test hanging corks", test_txmsg_cork_hangs}, @@ -2085,20 +1869,10 @@ static void test_selftests_sockhash(int cg_fd, struct sockmap_options *opt) __test_selftests(cg_fd, opt); } -static void test_selftests_ktls(int cg_fd, struct sockmap_options *opt) -{ - opt->map = BPF_SOCKHASH_FILENAME; - opt->prepend = "ktls"; - ktls = 1; - __test_selftests(cg_fd, opt); - ktls = 0; -} - static int test_selftest(int cg_fd, struct sockmap_options *opt) { test_selftests_sockmap(cg_fd, opt); test_selftests_sockhash(cg_fd, opt); - test_selftests_ktls(cg_fd, opt); test_print_results(); return 0; } diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore index 585ecb4d5dc4..e5314ce4bb2d 100644 --- a/tools/testing/selftests/drivers/net/.gitignore +++ b/tools/testing/selftests/drivers/net/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only napi_id_helper psp_responder +so_txtime diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile index b72080c6d06b..d5bf4cb638a8 100644 --- a/tools/testing/selftests/drivers/net/Makefile +++ b/tools/testing/selftests/drivers/net/Makefile @@ -7,6 +7,7 @@ TEST_INCLUDES := $(wildcard lib/py/*.py) \ TEST_GEN_FILES := \ napi_id_helper \ + so_txtime \ # end of TEST_GEN_FILES TEST_PROGS := \ @@ -21,6 +22,7 @@ TEST_PROGS := \ queues.py \ ring_reconfig.py \ shaper.py \ + so_txtime.py \ stats.py \ xdp.py \ # end of TEST_PROGS diff --git a/tools/testing/selftests/drivers/net/bonding/Makefile b/tools/testing/selftests/drivers/net/bonding/Makefile index 9af5f84edd37..be130bf585a4 100644 --- a/tools/testing/selftests/drivers/net/bonding/Makefile +++ b/tools/testing/selftests/drivers/net/bonding/Makefile @@ -8,6 +8,7 @@ TEST_PROGS := \ bond-lladdr-target.sh \ bond_ipsec_offload.sh \ bond_lacp_prio.sh \ + bond_lacp_strict.sh \ bond_macvlan_ipvlan.sh \ bond_options.sh \ bond_passive_lacp.sh \ diff --git a/tools/testing/selftests/drivers/net/bonding/bond_lacp_strict.sh b/tools/testing/selftests/drivers/net/bonding/bond_lacp_strict.sh new file mode 100755 index 000000000000..f1a93a1d952f --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/bond_lacp_strict.sh @@ -0,0 +1,347 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Testing if bond lacp_strict works +# +# Partner (p_ns) +# +--------------------------+ +# | bond0 | +# | + | +# | eth0 | eth1 | +# | +---+---+ | +# | | | | +# +--------------------------+ +# | | eth0 | eth1 | +# | | | | +# |(br_ns) | br0 | br1 | +# | | | | +# | | eth2 | eth3 | +# +--------------------------+ +# | | | | +# | +---+---+ | +# | eth0 | eth1 | +# | + | +# | bond0 | +# +--------------------------+ +# Dut (d_ns) + +lib_dir=$(dirname "$0") +# shellcheck disable=SC1090 +source "$lib_dir"/../../../net/lib.sh + +COLLECTING_DISTRIBUTING_MASK=48 +COLLECTING_DISTRIBUTING=48 +FAILED=0 + +setup_links() +{ + # shellcheck disable=SC2154 + ip -n "${p_ns}" link add eth0 type veth peer name eth0 netns "${br_ns}" + ip -n "${p_ns}" link add eth1 type veth peer name eth1 netns "${br_ns}" + ip -n "${d_ns}" link add eth0 type veth peer name eth2 netns "${br_ns}" + ip -n "${d_ns}" link add eth1 type veth peer name eth3 netns "${br_ns}" + + ip -n "${br_ns}" link add br0 type bridge + ip -n "${br_ns}" link add br1 type bridge + + ip -n "${br_ns}" link set dev br0 type bridge stp_state 0 + ip -n "${br_ns}" link set dev br1 type bridge stp_state 0 + + ip -n "${br_ns}" link set eth0 master br0 + ip -n "${br_ns}" link set eth2 master br0 + ip -n "${br_ns}" link set eth1 master br1 + ip -n "${br_ns}" link set eth3 master br1 + + # Allow LACP trames forwarding on bridge ports + ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br0/brif/eth0/group_fwd_mask" + ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br1/brif/eth1/group_fwd_mask" + ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br0/brif/eth2/group_fwd_mask" + ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br1/brif/eth3/group_fwd_mask" + + ip -n "${br_ns}" link set eth0 up + ip -n "${br_ns}" link set eth2 up + ip -n "${br_ns}" link set eth1 up + ip -n "${br_ns}" link set eth3 up + + ip -n "${br_ns}" link set br0 up + ip -n "${br_ns}" link set br1 up + + ip -n "${d_ns}" link add bond0 type bond mode 802.3ad miimon 100 \ + lacp_rate fast min_links 1 + ip -n "${p_ns}" link add bond0 type bond mode 802.3ad miimon 100 \ + lacp_rate fast min_links 1 + + ip -n "${d_ns}" link set eth0 master bond0 + ip -n "${d_ns}" link set eth1 master bond0 + ip -n "${p_ns}" link set eth0 master bond0 + ip -n "${p_ns}" link set eth1 master bond0 + + ip -n "${d_ns}" link set bond0 up + ip -n "${p_ns}" link set bond0 up +} + +test_master_carrier() { + local expected=$1 + local mode_name=$2 + local carrier + + carrier=$(ip netns exec "${d_ns}" cat /sys/class/net/bond0/carrier) + [ "$carrier" == "1" ] && carrier="up" || carrier="down" + + [ "$carrier" == "$expected" ] && return + + echo "FAIL: Expected carrier $expected in lacp_strict $mode_name mode, got $carrier" + + RET=1 + +} + +compare_state() { + local actual_state=$1 + local expected_state=$2 + local iface=$3 + local last_attempt=$4 + + [ $((actual_state & COLLECTING_DISTRIBUTING_MASK)) -eq "$expected_state" ] \ + && return 0 + + [ "$last_attempt" -ne 1 ] && return 1 + + printf "FAIL: Expected LACP %s actor state to " "$iface" + if [ "$expected_state" -eq $COLLECTING_DISTRIBUTING ]; then + echo "be in Collecting/Distributing state" + else + echo "have neither Collecting nor Distributing set." + fi + + return 1 +} + +_test_lacp_port_state() { + local interface=$1 + local expected=$2 + local last_attempt=$3 + local eth0_actor_state eth1_actor_state + local ret=0 + + # shellcheck disable=SC2016 + while IFS='=' read -r k v; do + printf -v "$k" '%s' "$v" + done < <( + ip netns exec "${d_ns}" awk ' + /^Slave Interface: / { iface=$3 } + /details actor lacp pdu:/ { ctx="actor" } + /details partner lacp pdu:/ { ctx="partner" } + /^[[:space:]]+port state: / { + if (ctx == "actor") { + gsub(":", "", iface) + printf "%s_%s_state=%s\n", iface, ctx, $3 + } + } + ' /proc/net/bonding/bond0 + ) + + if [ "$interface" == "eth0" ] || [ "$interface" == "both" ]; then + compare_state "$eth0_actor_state" "$expected" eth0 "$last_attempt" || ret=1 + fi + + if [ "$interface" == "eth1" ] || [ "$interface" == "both" ]; then + compare_state "$eth1_actor_state" "$expected" eth1 "$last_attempt" || ret=1 + fi + + return $ret +} + +test_lacp_port_state() { + local interface=$1 + local expected=$2 + local retry=$3 + local last_attempt=0 + local attempt=1 + local ret=1 + + while [ $attempt -le $((retry + 1)) ]; do + [ $attempt -eq $((retry + 1)) ] && last_attempt=1 + _test_lacp_port_state "$interface" "$expected" "$last_attempt" && return + ((attempt++)) + sleep 1 + done + + RET=1 +} + + +trap cleanup_all_ns EXIT +setup_ns d_ns p_ns br_ns +setup_links + +# Initial state +RET=0 +mode=off +test_lacp_port_state both $COLLECTING_DISTRIBUTING 3 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 up" + +# partner eth0 down, eth1 up +# (replicate eth0 state to dut eth0 by shutting a bridge port) +RET=0 +ip -n "${p_ns}" link set eth0 down +ip -n "${br_ns}" link set eth2 down +test_lacp_port_state eth0 $FAILED 5 +test_lacp_port_state eth1 $COLLECTING_DISTRIBUTING 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 down" + +# partner eth0 and eth1 down +RET=0 +ip -n "${p_ns}" link set eth1 down +ip -n "${br_ns}" link set eth3 down +test_lacp_port_state both $FAILED 5 +test_master_carrier down $mode # down because of min_links +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 down" + +# partner eth0 up, eth1 down +RET=0 +ip -n "${p_ns}" link set eth0 up +ip -n "${br_ns}" link set eth2 up +test_lacp_port_state eth0 $COLLECTING_DISTRIBUTING 60 +test_lacp_port_state eth1 $FAILED 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 up, eth1 down" + +# partner eth0 and eth1 up +RET=0 +ip -n "${p_ns}" link set eth1 up +ip -n "${br_ns}" link set eth3 up +test_lacp_port_state both $COLLECTING_DISTRIBUTING 60 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 up" + +# partner eth0 stops LACP and eth1 up +RET=0 +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br0/brif/eth0/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br0/brif/eth2/group_fwd_mask" +test_lacp_port_state eth0 $FAILED 5 +test_lacp_port_state eth1 $COLLECTING_DISTRIBUTING 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 stopped sending LACP" + +# partner eth0 and eth1 stop LACP +RET=0 +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br1/brif/eth1/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br1/brif/eth3/group_fwd_mask" +test_lacp_port_state both $FAILED 5 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 stopped sending LACP" + +# switch to lacp_strict on +RET=0 +mode=on +ip -n "${d_ns}" link set dev bond0 type bond lacp_strict $mode +test_lacp_port_state both $FAILED 1 +test_master_carrier down $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 stopped sending LACP" + +# switch back to lacp_strict off mode +RET=0 +mode=off +ip -n "${d_ns}" link set dev bond0 type bond lacp_strict $mode +test_lacp_port_state both $FAILED 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 stopped sending LACP" + +# eth0 recovers LACP +RET=0 +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br0/brif/eth0/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br0/brif/eth2/group_fwd_mask" +test_lacp_port_state eth0 $COLLECTING_DISTRIBUTING 60 +test_lacp_port_state eth1 $FAILED 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 recovered and eth1 stopped sending LACP" + +# eth1 recovers LACP +RET=0 +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br1/brif/eth1/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br1/brif/eth3/group_fwd_mask" +test_lacp_port_state both $COLLECTING_DISTRIBUTING 60 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 recovered LACP" + +# switch to lacp_strict on +RET=0 +mode=on +ip -n "${d_ns}" link set dev bond0 type bond lacp_strict $mode +test_lacp_port_state both $COLLECTING_DISTRIBUTING 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 up" + +# partner eth0 down, eth1 up +RET=0 +ip -n "${p_ns}" link set eth0 down +ip -n "${br_ns}" link set eth2 down +test_lacp_port_state eth0 $FAILED 5 +test_lacp_port_state eth1 $COLLECTING_DISTRIBUTING 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 down" + +# partner eth0 and eth1 down +RET=0 +ip -n "${p_ns}" link set eth1 down +ip -n "${br_ns}" link set eth3 down +test_lacp_port_state both $FAILED 5 +test_master_carrier down $mode # down because of min_links +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 down" + +# partner eth0 up, eth1 down +RET=0 +ip -n "${p_ns}" link set eth0 up +ip -n "${br_ns}" link set eth2 up +test_lacp_port_state eth0 $COLLECTING_DISTRIBUTING 60 +test_lacp_port_state eth1 $FAILED 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 up, eth1 down" + +# partner eth0 and eth1 up +RET=0 +ip -n "${p_ns}" link set eth1 up +ip -n "${br_ns}" link set eth3 up +test_lacp_port_state both $COLLECTING_DISTRIBUTING 60 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 up" + +# partner eth0 stops LACP and eth1 up +RET=0 +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br0/brif/eth0/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br0/brif/eth2/group_fwd_mask" +test_lacp_port_state eth0 $FAILED 5 +test_lacp_port_state eth1 $COLLECTING_DISTRIBUTING 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 stopped sending LACP" + +# partner eth0 and eth1 stop LACP +RET=0 +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br1/brif/eth1/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 0 > /sys/class/net/br1/brif/eth3/group_fwd_mask" +test_lacp_port_state both $FAILED 5 +test_master_carrier down $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 stopped sending LACP" + +# eth0 recovers LACP +RET=0 +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br0/brif/eth0/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br0/brif/eth2/group_fwd_mask" +test_lacp_port_state eth0 $COLLECTING_DISTRIBUTING 60 +test_lacp_port_state eth1 $FAILED 1 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 recovered and eth1 stopped sending LACP" + +# eth1 recovers LACP +# shellcheck disable=SC2034 +RET=0 +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br1/brif/eth1/group_fwd_mask" +ip netns exec "${br_ns}" sh -c "echo 4 > /sys/class/net/br1/brif/eth3/group_fwd_mask" +test_lacp_port_state both $COLLECTING_DISTRIBUTING 60 +test_master_carrier up $mode +log_test "bond LACP" "lacp_strict $mode - eth0 and eth1 recovered LACP" + +exit "${EXIT_STATUS}" diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config index fd16994366f4..91d4fd410914 100644 --- a/tools/testing/selftests/drivers/net/config +++ b/tools/testing/selftests/drivers/net/config @@ -8,5 +8,10 @@ CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y CONFIG_NETCONSOLE_EXTENDED_LOG=y CONFIG_NETDEVSIM=m +CONFIG_NETKIT=y +CONFIG_NET_SCH_ETF=m +CONFIG_NET_SCH_FQ=m +CONFIG_PPP=y +CONFIG_PPPOE=y CONFIG_VLAN_8021Q=m CONFIG_XDP_SOCKETS=y diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py index 221f27e57147..6ab8c97880d1 100755 --- a/tools/testing/selftests/drivers/net/gro.py +++ b/tools/testing/selftests/drivers/net/gro.py @@ -40,7 +40,7 @@ import glob import os import re from lib.py import ksft_run, ksft_exit, ksft_pr -from lib.py import NetDrvEpEnv, KsftXfailEx +from lib.py import NetDrvEpEnv, KsftFailEx, KsftXfailEx from lib.py import NetdevFamily, EthtoolFamily from lib.py import bkg, cmd, defer, ethtool, ip from lib.py import ksft_variants, KsftNamedVariant @@ -132,11 +132,21 @@ def _get_queue_stats(cfg, queue_id): return {} +def _require_ntuple(cfg): + features = ethtool(f"-k {cfg.ifname}", json=True)[0] + if not features["ntuple-filters"]["active"]: + if features["ntuple-filters"]["fixed"]: + raise KsftXfailEx("Device does not support ntuple-filters") + ethtool(f"-K {cfg.ifname} ntuple-filters on") + defer(ethtool, f"-K {cfg.ifname} ntuple-filters off") + + def _setup_isolated_queue(cfg): """Set up an isolated queue for testing using ntuple filter. Remove queue 1 from the default RSS context and steer test traffic to it. """ + _require_ntuple(cfg) test_queue = 1 qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) @@ -313,6 +323,12 @@ def _gro_variants(): "ip_frag6", "ip_v6ext_same", "ip_v6ext_diff", ] + # Tests specific to PPPoE + pppoe_tests = [ + "data_same", "data_lrg_sml", "data_sml_lrg", "data_lrg_1byte", + "data_burst", "pppoe_sid", + ] + for mode in ["sw", "hw", "lro"]: for protocol in ["ipv4", "ipv6", "ipip", "ip6ip6"]: for test_name in common_tests: @@ -325,6 +341,11 @@ def _gro_variants(): for test_name in ipv6_tests: yield mode, protocol, test_name + for mode in ["sw"]: + for protocol in ["pppoev4", "pppoev6"]: + for test_name in pppoe_tests: + yield mode, protocol, test_name + @ksft_variants(_gro_variants()) def test(cfg, mode, protocol, test_name): @@ -349,6 +370,12 @@ def test(cfg, mode, protocol, test_name): ksft_pr(rx_proc) + # ret==42 means the receiver detected over-coalescing. + # This is unambiguous proof of a bug, retries can only cause + # false negatives. + if rx_proc.ret == 42: + raise KsftFailEx(f"GRO over-coalesced in {protocol}/{test_name}") + if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"): ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment") return diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index 82809d5b2478..fd0535a96d84 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -35,6 +35,7 @@ TEST_PROGS = \ irq.py \ loopback.sh \ nic_timestamp.py \ + nk_devmem.py \ nk_netns.py \ nk_qlease.py \ ntuple.py \ @@ -46,12 +47,14 @@ TEST_PROGS = \ rss_input_xfrm.py \ toeplitz.py \ tso.py \ + userns_devmem.py \ uso.py \ xdp_metadata.py \ xsk_reconfig.py \ # TEST_FILES := \ + devmem_lib.py \ ethtool_lib.sh \ # diff --git a/tools/testing/selftests/drivers/net/hw/config b/tools/testing/selftests/drivers/net/hw/config index 8c132ace2b8d..ed8642b68094 100644 --- a/tools/testing/selftests/drivers/net/hw/config +++ b/tools/testing/selftests/drivers/net/hw/config @@ -3,6 +3,7 @@ CONFIG_FAIL_FUNCTION=y CONFIG_FAULT_INJECTION=y CONFIG_FAULT_INJECTION_DEBUG_FS=y CONFIG_FUNCTION_ERROR_INJECTION=y +CONFIG_HUGETLBFS=y CONFIG_INET6_ESP=y CONFIG_INET6_ESP_OFFLOAD=y CONFIG_INET_ESP=y @@ -10,12 +11,16 @@ CONFIG_INET_ESP_OFFLOAD=y CONFIG_IO_URING=y CONFIG_IPV6=y CONFIG_IPV6_GRE=y +CONFIG_IPV6_SIT=y +CONFIG_IPV6_TUNNEL=y CONFIG_NET_CLS_ACT=y CONFIG_NET_CLS_BPF=y CONFIG_NET_IPGRE=y CONFIG_NET_IPGRE_DEMUX=y +CONFIG_NET_IPIP=y CONFIG_NETKIT=y CONFIG_NET_SCH_INGRESS=y CONFIG_UDMABUF=y +CONFIG_USER_NS=y CONFIG_VXLAN=y CONFIG_XFRM_USER=y diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py index ee863e90d1e0..031cf9905f65 100755 --- a/tools/testing/selftests/drivers/net/hw/devmem.py +++ b/tools/testing/selftests/drivers/net/hw/devmem.py @@ -2,91 +2,40 @@ # SPDX-License-Identifier: GPL-2.0 from os import path -from lib.py import ksft_run, ksft_exit -from lib.py import ksft_eq, KsftSkipEx +from devmem_lib import setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds +from lib.py import ksft_run, ksft_exit, ksft_disruptive from lib.py import NetDrvEpEnv -from lib.py import bkg, cmd, rand_port, wait_port_listen -from lib.py import ksft_disruptive - - -def require_devmem(cfg): - if not hasattr(cfg, "_devmem_probed"): - probe_command = f"{cfg.bin_local} -f {cfg.ifname}" - cfg._devmem_supported = cmd(probe_command, fail=False, shell=True).ret == 0 - cfg._devmem_probed = True - - if not cfg._devmem_supported: - raise KsftSkipEx("Test requires devmem support") @ksft_disruptive def check_rx(cfg) -> None: - require_devmem(cfg) - - port = rand_port() - socat = f"socat -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},bind={cfg.remote_baddr}:{port}" - listen_cmd = f"{cfg.bin_local} -l -f {cfg.ifname} -s {cfg.addr} -p {port} -c {cfg.remote_addr} -v 7" - - with bkg(listen_cmd, exit_wait=True) as ncdevmem: - wait_port_listen(port) - cmd(f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | \ - head -c 1K | {socat}", host=cfg.remote, shell=True) - - ksft_eq(ncdevmem.ret, 0) + """Run the devmem RX test.""" + run_rx(cfg) @ksft_disruptive def check_tx(cfg) -> None: - require_devmem(cfg) - - port = rand_port() - listen_cmd = f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}" - - with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat: - wait_port_listen(port, host=cfg.remote) - cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_local} -f {cfg.ifname} -s {cfg.remote_addr} -p {port}", shell=True) - - ksft_eq(socat.stdout.strip(), "hello\nworld") + """Run the devmem TX test.""" + run_tx(cfg) @ksft_disruptive def check_tx_chunks(cfg) -> None: - require_devmem(cfg) - - port = rand_port() - listen_cmd = f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}" - - with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat: - wait_port_listen(port, host=cfg.remote) - cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_local} -f {cfg.ifname} -s {cfg.remote_addr} -p {port} -z 3", shell=True) - - ksft_eq(socat.stdout.strip(), "hello\nworld") + """Run the devmem TX chunking test.""" + run_tx_chunks(cfg) def check_rx_hds(cfg) -> None: - """Test HDS splitting across payload sizes.""" - require_devmem(cfg) - - for size in [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]: - port = rand_port() - listen_cmd = f"{cfg.bin_local} -L -l -f {cfg.ifname} -s {cfg.addr} -p {port}" - - with bkg(listen_cmd, exit_wait=True) as ncdevmem: - wait_port_listen(port) - cmd(f"dd if=/dev/zero bs={size} count=1 2>/dev/null | " + - f"socat -b {size} -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},nodelay", - host=cfg.remote, shell=True) - - ksft_eq(ncdevmem.ret, 0, f"HDS failed for payload size {size}") + """Run the HDS test.""" + run_rx_hds(cfg) def main() -> None: + """Run the devmem test cases.""" with NetDrvEpEnv(__file__) as cfg: - cfg.bin_local = path.abspath(path.dirname(__file__) + "/ncdevmem") - cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) - + setup_test(cfg, path.abspath(path.dirname(__file__) + "/ncdevmem")) ksft_run([check_rx, check_tx, check_tx_chunks, check_rx_hds], - args=(cfg, )) + args=(cfg,)) ksft_exit() diff --git a/tools/testing/selftests/drivers/net/hw/devmem_lib.py b/tools/testing/selftests/drivers/net/hw/devmem_lib.py new file mode 100644 index 000000000000..0921ff03eb81 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/devmem_lib.py @@ -0,0 +1,222 @@ +# SPDX-License-Identifier: GPL-2.0 +"""Shared helpers for devmem TCP selftests.""" + +import re + +from lib.py import (bkg, cmd, defer, ethtool, rand_port, wait_port_listen, + ksft_eq, KsftSkipEx, NetNSEnter, EthtoolFamily, + NetdevFamily) + + +def require_devmem(cfg): + """Probe ncdevmem on cfg.ifname and SKIP the test if devmem isn't supported.""" + if not hasattr(cfg, "devmem_probed"): + probe_command = f"{cfg.bin_local} -f {cfg.ifname}" + cfg.devmem_supported = cmd(probe_command, fail=False, shell=True).ret == 0 + cfg.devmem_probed = True + + if not cfg.devmem_supported: + raise KsftSkipEx("Test requires devmem support") + + +def configure_nic(cfg): + """Channels, rings, RSS, queue lease for netkit devmem.""" + if not hasattr(cfg, 'netns'): + return + + cfg.require_ipver('6') + ethnl = EthtoolFamily() + + channels = ethnl.channels_get({'header': {'dev-index': cfg.ifindex}}) + channels = channels['combined-count'] + if channels < 2: + raise KsftSkipEx( + 'Test requires NETIF with at least 2 combined channels' + ) + + rings = ethnl.rings_get({'header': {'dev-index': cfg.ifindex}}) + orig_rx_rings = rings['rx'] + orig_hds_thresh = rings.get('hds-thresh', 0) + orig_data_split = rings.get('tcp-data-split', 'unknown') + + ethnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'enabled', + 'hds-thresh': 0, + 'rx': min(64, orig_rx_rings)}) + defer(ethnl.rings_set, {'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': orig_data_split, + 'hds-thresh': orig_hds_thresh, + 'rx': orig_rx_rings}) + + cfg.src_queue = channels - 1 + ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}") + defer(ethtool, f"-X {cfg.ifname} default") + + if not hasattr(cfg, 'nk_queue'): + with NetNSEnter(str(cfg.netns)): + netdevnl = NetdevFamily() + lease_result = netdevnl.queue_create({ + "ifindex": cfg.nk_guest_ifindex, + "type": "rx", + "lease": { + "ifindex": cfg.ifindex, + "queue": {"id": cfg.src_queue, "type": "rx"}, + "netns-id": 0, + }, + }) + cfg.nk_queue = lease_result['id'] + + +def set_flow_rule(cfg, port): + """Install a flow rule steering to src_queue and return the flow rule ID.""" + output = ethtool( + f"-N {cfg.ifname} flow-type tcp6 dst-port {port}" + f" action {cfg.src_queue}" + ).stdout + return int(re.search(r'ID (\d+)', output).group(1)) + + +def ncdevmem_rx(cfg, port, verify=True, fail_on_linear=False, flow_steer=False): + """Build the ncdevmem RX listener command.""" + if hasattr(cfg, 'netns'): + flow_rule_id = set_flow_rule(cfg, port) + defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") + + ifname = cfg.nk_guest_ifname + addr = cfg.nk_guest_ipv6 + extras = [f"-t {cfg.nk_queue}", "-q 1", "-n"] + else: + ifname = cfg.ifname + addr = cfg.addr + extras = [] + if flow_steer: + extras.append(f"-c {cfg.remote_addr}") + + if verify: + extras.append("-v 7") + if fail_on_linear: + extras.append("-L") + + parts = [cfg.bin_local, "-l", f"-f {ifname}", f"-s {addr}", + f"-p {port}", *extras] + return " ".join(parts) + + +def ncdevmem_tx(cfg, port, chunk_size=0): + """Build the ncdevmem TX send command.""" + if hasattr(cfg, 'netns'): + ifname = cfg.nk_guest_ifname + addr = cfg.remote_addr_v['6'] + extras = ["-t 0", "-q 1", "-n"] + else: + ifname = cfg.ifname + addr = cfg.remote_addr + extras = [] + + if chunk_size: + extras.append(f"-z {chunk_size}") + + parts = [cfg.bin_local, f"-f {ifname}", f"-s {addr}", + f"-p {port}", *extras] + return " ".join(parts) + + +def socat_send(cfg, port, buf_size=0): + """Socat command for sending to the devmem listener. + + When buf_size > 0, force one TCP segment per write of exactly that size by + setting socat's buffer (-b) and disabling Nagle (TCP_NODELAY). + """ + proto = f"TCP{cfg.addr_ipver}" + + if hasattr(cfg, 'netns'): + addr = f"[{cfg.nk_guest_ipv6}]" + else: + addr = cfg.baddr + + suffix = f",bind={cfg.remote_baddr}:{port}" + + buf = "" + if buf_size: + buf = f"-b {buf_size}" + suffix += ",nodelay" + + return f"socat {buf} -u - {proto}:{addr}:{port}{suffix}" + + +def socat_listen(cfg, port): + """Socat listen command for TX tests.""" + return f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}" + + +def setup_test(cfg, bin_local): + """Stash the local ncdevmem path on cfg and deploy it to the remote.""" + cfg.bin_local = bin_local + cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) + + +def run_rx(cfg): + """Run the devmem RX test.""" + require_devmem(cfg) + configure_nic(cfg) + port = rand_port() + socat = socat_send(cfg, port) + data_pipe = (f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | head -c 1K" + f" | {socat}") + netns = getattr(cfg, "netns", None) + + listen_cmd = ncdevmem_rx(cfg, port, flow_steer=not hasattr(cfg, 'netns')) + with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem: + wait_port_listen(port, proto="tcp", ns=netns) + cmd(data_pipe, host=cfg.remote, shell=True) + ksft_eq(ncdevmem.ret, 0) + + +def run_tx(cfg): + """Run the devmem TX test.""" + require_devmem(cfg) + configure_nic(cfg) + netns = getattr(cfg, "netns", None) + port = rand_port() + tx_cmd = ncdevmem_tx(cfg, port) + listen_cmd = socat_listen(cfg, port) + + with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat: + wait_port_listen(port, host=cfg.remote) + cmd(f"bash -c 'echo -e \"hello\\nworld\" | {tx_cmd}'", ns=netns, shell=True) + ksft_eq(socat.stdout.strip(), "hello\nworld") + + +def run_tx_chunks(cfg): + """Run the devmem TX chunking test.""" + require_devmem(cfg) + configure_nic(cfg) + netns = getattr(cfg, "netns", None) + port = rand_port() + tx_cmd = ncdevmem_tx(cfg, port, chunk_size=3) + listen_cmd = socat_listen(cfg, port) + + with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat: + wait_port_listen(port, host=cfg.remote) + cmd(f"bash -c 'echo -e \"hello\\nworld\" | {tx_cmd}'", ns=netns, shell=True) + ksft_eq(socat.stdout.strip(), "hello\nworld") + + +def run_rx_hds(cfg): + """Run the HDS test by running devmem RX across a segment size sweep.""" + require_devmem(cfg) + configure_nic(cfg) + netns = getattr(cfg, "netns", None) + + for size in [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]: + port = rand_port() + + listen_cmd = ncdevmem_rx(cfg, port, verify=False, + fail_on_linear=True) + socat = socat_send(cfg, port, buf_size=size) + + with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem: + wait_port_listen(port, proto="tcp", ns=netns) + cmd(f"dd if=/dev/zero bs={size} count=1 2>/dev/null | " + f"{socat}", host=cfg.remote, shell=True) + ksft_eq(ncdevmem.ret, 0, f"HDS failed for payload size {size}") diff --git a/tools/testing/selftests/drivers/net/hw/gro_hw.py b/tools/testing/selftests/drivers/net/hw/gro_hw.py index 10e08b22ee0e..70e76e3888bd 100755 --- a/tools/testing/selftests/drivers/net/hw/gro_hw.py +++ b/tools/testing/selftests/drivers/net/hw/gro_hw.py @@ -51,11 +51,21 @@ def _resolve_dmac(cfg, ipver): return getattr(cfg, attr) +def _require_ntuple(cfg): + features = ethtool(f"-k {cfg.ifname}", json=True)[0] + if not features["ntuple-filters"]["active"]: + if features["ntuple-filters"]["fixed"]: + raise KsftSkipEx("Device does not support ntuple-filters") + ethtool(f"-K {cfg.ifname} ntuple-filters on") + defer(ethtool, f"-K {cfg.ifname} ntuple-filters off") + + def _setup_isolated_queue(cfg): """Set up an isolated queue for testing using ntuple filter. Remove queue 1 from the default RSS context and steer test traffic to it. """ + _require_ntuple(cfg) test_queue = 1 qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) diff --git a/tools/testing/selftests/drivers/net/hw/iou-zcrx.c b/tools/testing/selftests/drivers/net/hw/iou-zcrx.c index 240d13dbc54e..f6a8fc5fac24 100644 --- a/tools/testing/selftests/drivers/net/hw/iou-zcrx.c +++ b/tools/testing/selftests/drivers/net/hw/iou-zcrx.c @@ -351,9 +351,6 @@ static void run_server(void) if (ret < 0) error(1, 0, "bind()"); - if (listen(fd, 1024) < 0) - error(1, 0, "listen()"); - flags |= IORING_SETUP_COOP_TASKRUN; flags |= IORING_SETUP_SINGLE_ISSUER; flags |= IORING_SETUP_DEFER_TASKRUN; @@ -366,6 +363,9 @@ static void run_server(void) if (cfg_dry_run) return; + if (listen(fd, 1024) < 0) + error(1, 0, "listen()"); + add_accept(&ring, fd); tstop = gettimeofday_ms() + 5000; diff --git a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py index e81724cb5542..b7a225fe4bea 100755 --- a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py +++ b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0 import re +import resource import time from os import path from lib.py import ksft_run, ksft_exit, KsftSkipEx, ksft_variants, KsftNamedVariant @@ -41,6 +42,23 @@ def set_flow_rule_rss(cfg, rss_ctx_id): return int(values) +def check_iou_rx_buf_len(cfg, expected_rx_buf_len): + """Check the io-uring memory provider exposes the expected rx_buf_len.""" + q = cfg.netnl.queue_get({'ifindex': cfg.ifindex, 'type': 'rx', 'id': cfg.target}) + napi_id = q['napi-id'] + pools = cfg.netnl.page_pool_get({}, dump=True) + pools = [p for p in pools if p.get('napi-id') == napi_id + and 'io-uring' in p] + if len(pools) != 1: + raise Exception(f"Expected 1 io-uring page pool, found {len(pools)}") + rx_buf_len = pools[0]['io-uring'].get('rx-buf-len') + if rx_buf_len is None: + raise KsftSkipEx("io-uring 'rx-buf-len' attribute not supported") + if rx_buf_len != expected_rx_buf_len: + raise Exception(f'Expected io-uring rx-buf-len {expected_rx_buf_len}, ' + f'got {rx_buf_len}') + + def single(cfg): channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}}) channels = channels['combined-count'] @@ -100,12 +118,22 @@ def rss(cfg): defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") +def _require_ntuple(cfg): + features = ethtool(f"-k {cfg.ifname}", json=True)[0] + if not features["ntuple-filters"]["active"]: + if features["ntuple-filters"]["fixed"]: + raise KsftSkipEx("Device does not support ntuple-filters") + ethtool(f"-K {cfg.ifname} ntuple-filters on") + defer(ethtool, f"-K {cfg.ifname} ntuple-filters off") + + @ksft_variants([ KsftNamedVariant("single", single), KsftNamedVariant("rss", rss), ]) def test_zcrx(cfg, setup) -> None: cfg.require_ipver('6') + _require_ntuple(cfg) setup(cfg) rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target}" @@ -121,6 +149,7 @@ def test_zcrx(cfg, setup) -> None: ]) def test_zcrx_oneshot(cfg, setup) -> None: cfg.require_ipver('6') + _require_ntuple(cfg) setup(cfg) rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target} -o 4" @@ -134,6 +163,7 @@ def test_zcrx_large_chunks(cfg) -> None: """Test zcrx with large buffer chunks.""" cfg.require_ipver('6') + _require_ntuple(cfg) hp_file = "/proc/sys/vm/nr_hugepages" with open(hp_file, 'r+', encoding='utf-8') as f: @@ -144,7 +174,10 @@ def test_zcrx_large_chunks(cfg) -> None: defer(lambda: open(hp_file, 'w', encoding='utf-8').write(str(nr_hugepages))) single(cfg) - rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target} -x 2" + page_size = resource.getpagesize() + nr_pages = 2 + rx_buf_len = nr_pages * page_size + rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target} -x {nr_pages}" tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840" probe = cmd(rx_cmd + " -d", fail=False) @@ -154,6 +187,9 @@ def test_zcrx_large_chunks(cfg) -> None: mp_clear_wait(cfg) with bkg(rx_cmd, exit_wait=True): wait_port_listen(cfg.port, proto="tcp") + + check_iou_rx_buf_len(cfg, rx_buf_len) + cmd(tx_cmd, host=cfg.remote) diff --git a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py index 84a4dab6c649..8a58cb17cc06 100644 --- a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py +++ b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py @@ -18,7 +18,7 @@ try: sys.path.append(KSFT_DIR.as_posix()) # Import one by one to avoid pylint false positives - from net.lib.py import NetNS, NetNSEnter, NetdevSimDev + from net.lib.py import NetNS, NetNSEnter, NetdevSimDev, UserNetNS from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \ NlError, RtnlFamily, DevlinkFamily, PSPFamily, Netlink from net.lib.py import CmdExitFailure @@ -34,7 +34,7 @@ try: from drivers.net.lib.py import GenerateTraffic, Remote, Iperf3Runner from drivers.net.lib.py import NetDrvEnv, NetDrvEpEnv, NetDrvContEnv - __all__ = ["NetNS", "NetNSEnter", "NetdevSimDev", + __all__ = ["NetNS", "NetNSEnter", "NetdevSimDev", "UserNetNS", "EthtoolFamily", "NetdevFamily", "NetshaperFamily", "NlError", "RtnlFamily", "DevlinkFamily", "PSPFamily", "Netlink", "CmdExitFailure", diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c index e098d6534c3c..d96e8a3b5a65 100644 --- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c +++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c @@ -93,6 +93,7 @@ static char *port; static size_t do_validation; static int start_queue = -1; static int num_queues = -1; +static int skip_config; static char *ifname; static unsigned int ifindex; static unsigned int dmabuf_id; @@ -828,7 +829,7 @@ static struct netdev_queue_id *create_queues(void) static int do_server(struct memory_buffer *mem) { - struct ethtool_rings_get_rsp *ring_config; + struct ethtool_rings_get_rsp *ring_config = NULL; char ctrl_data[sizeof(int) * 20000]; size_t non_page_aligned_frags = 0; struct sockaddr_in6 client_addr; @@ -851,27 +852,29 @@ static int do_server(struct memory_buffer *mem) return -1; } - ring_config = get_ring_config(); - if (!ring_config) { - pr_err("Failed to get current ring configuration"); - return -1; - } + if (!skip_config) { + ring_config = get_ring_config(); + if (!ring_config) { + pr_err("Failed to get current ring configuration"); + return -1; + } - if (configure_headersplit(ring_config, 1)) { - pr_err("Failed to enable TCP header split"); - goto err_free_ring_config; - } + if (configure_headersplit(ring_config, 1)) { + pr_err("Failed to enable TCP header split"); + goto err_free_ring_config; + } - /* Configure RSS to divert all traffic from our devmem queues */ - if (configure_rss()) { - pr_err("Failed to configure rss"); - goto err_reset_headersplit; - } + /* Configure RSS to divert all traffic from our devmem queues */ + if (configure_rss()) { + pr_err("Failed to configure rss"); + goto err_reset_headersplit; + } - /* Flow steer our devmem flows to start_queue */ - if (configure_flow_steering(&server_sin)) { - pr_err("Failed to configure flow steering"); - goto err_reset_rss; + /* Flow steer our devmem flows to start_queue */ + if (configure_flow_steering(&server_sin)) { + pr_err("Failed to configure flow steering"); + goto err_reset_rss; + } } if (bind_rx_queue(ifindex, mem->fd, create_queues(), num_queues, &ys)) { @@ -1052,13 +1055,17 @@ static int do_server(struct memory_buffer *mem) err_unbind: ynl_sock_destroy(ys); err_reset_flow_steering: - reset_flow_steering(); + if (!skip_config) + reset_flow_steering(); err_reset_rss: - reset_rss(); + if (!skip_config) + reset_rss(); err_reset_headersplit: - restore_ring_config(ring_config); + if (!skip_config) + restore_ring_config(ring_config); err_free_ring_config: - ethtool_rings_get_rsp_free(ring_config); + if (!skip_config) + ethtool_rings_get_rsp_free(ring_config); return err; } @@ -1404,7 +1411,7 @@ int main(int argc, char *argv[]) int is_server = 0, opt; int ret, err = 1; - while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:")) != -1) { + while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:n")) != -1) { switch (opt) { case 'L': fail_on_linear = true; @@ -1436,6 +1443,9 @@ int main(int argc, char *argv[]) case 'z': max_chunk = atoi(optarg); break; + case 'n': + skip_config = 1; + break; case '?': fprintf(stderr, "unknown option: %c\n", optopt); break; diff --git a/tools/testing/selftests/drivers/net/hw/nk_devmem.py b/tools/testing/selftests/drivers/net/hw/nk_devmem.py new file mode 100755 index 000000000000..300ed2a70ab4 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/nk_devmem.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +"""Test devmem TCP with netkit.""" + +import os +from devmem_lib import setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds +from lib.py import ksft_run, ksft_exit, ksft_disruptive +from lib.py import NetDrvContEnv + + +@ksft_disruptive +def check_nk_rx(cfg) -> None: + """Run the devmem RX test through netkit.""" + run_rx(cfg) + + +@ksft_disruptive +def check_nk_tx(cfg) -> None: + """Run the devmem TX test through netkit.""" + run_tx(cfg) + + +@ksft_disruptive +def check_nk_tx_chunks(cfg) -> None: + """Run the devmem TX chunking test through netkit.""" + run_tx_chunks(cfg) + + +def check_nk_rx_hds(cfg) -> None: + """Run the HDS test through netkit.""" + run_rx_hds(cfg) + + +def main() -> None: + """Run the netkit devmem test cases.""" + with NetDrvContEnv(__file__, rxqueues=2, primary_rx_redirect=True) as cfg: + setup_test(cfg, + os.path.join(os.path.dirname(os.path.abspath(__file__)), + "ncdevmem")) + ksft_run([check_nk_rx, check_nk_tx, check_nk_tx_chunks, + check_nk_rx_hds], args=(cfg,)) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/hw/nk_primary_rx_redirect.bpf.c b/tools/testing/selftests/drivers/net/hw/nk_primary_rx_redirect.bpf.c new file mode 100644 index 000000000000..46ff494b23de --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/nk_primary_rx_redirect.bpf.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +#define ctx_ptr(field) ((void *)(long)(field)) + +volatile __u32 phys_ifindex; + +SEC("tc/ingress") +int nk_primary_rx_redirect(struct __sk_buff *skb) +{ + void *data_end = ctx_ptr(skb->data_end); + void *data = ctx_ptr(skb->data); + struct ethhdr *eth; + struct ipv6hdr *ip6h; + + eth = data; + if ((void *)(eth + 1) > data_end) + return TC_ACT_OK; + + if (eth->h_proto != bpf_htons(ETH_P_IPV6)) + return TC_ACT_OK; + + ip6h = data + sizeof(struct ethhdr); + if ((void *)(ip6h + 1) > data_end) + return TC_ACT_OK; + + if (ip6h->nexthdr == IPPROTO_ICMPV6) + return TC_ACT_OK; + + return bpf_redirect_neigh(phys_ifindex, NULL, 0, 0); +} + +char __license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/drivers/net/hw/nk_qlease.py b/tools/testing/selftests/drivers/net/hw/nk_qlease.py index aa83dc321328..4f53034c9a50 100755 --- a/tools/testing/selftests/drivers/net/hw/nk_qlease.py +++ b/tools/testing/selftests/drivers/net/hw/nk_qlease.py @@ -18,8 +18,10 @@ from lib.py import ( NetNSEnter, EthtoolFamily, NetdevFamily, + RtnlFamily, ) from lib.py import ( + Netlink, bkg, cmd, defer, @@ -30,10 +32,138 @@ from lib.py import ( ) from lib.py import KsftSkipEx, CmdExitFailure +# iou-zcrx exits with 42 from setup_zcrx() when the NIC does not advertise +# QCFG_RX_PAGE_SIZE (or otherwise rejects the requested rx_buf_len). +SKIP_CODE = 42 -def set_flow_rule(cfg): + +def _restore_hugepages(count): + with open("/proc/sys/vm/nr_hugepages", "w", encoding="utf-8") as f: + f.write(str(count)) + + +def _mp_clear_wait(cfg, src_queue): + """Wait for the io_uring memory provider to clear from the leased + physical queue; io_uring tears it down asynchronously after the + process holding the ifq exits.""" + netdevnl = NetdevFamily() + deadline = time.time() + 5 + while time.time() < deadline: + queue_info = netdevnl.queue_get( + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} + ) + if "io-uring" not in queue_info: + return + time.sleep(0.1) + raise TimeoutError("Timed out waiting for memory provider to clear") + + +def _create_netkit_pair(cfg, rxqueues=2): + if cfg.nk_host_ifname: + cmd(f"ip link del dev {cfg.nk_host_ifname}", fail=False) + cfg.nk_host_ifname = None + cfg.nk_guest_ifname = None + cfg.detach_bpf() + + all_links = ip("-d link show", json=True) + old_idxs = { + link["ifindex"] + for link in all_links + if link.get("linkinfo", {}).get("info_kind") == "netkit" + } + + rtnl = RtnlFamily() + rtnl.newlink( + { + "linkinfo": { + "kind": "netkit", + "data": { + "mode": "l2", + "policy": "forward", + "peer-policy": "forward", + }, + }, + "num-rx-queues": rxqueues, + }, + flags=[Netlink.NLM_F_CREATE, Netlink.NLM_F_EXCL], + ) + + all_links = ip("-d link show", json=True) + nk_links = [ + link + for link in all_links + if link.get("linkinfo", {}).get("info_kind") == "netkit" + and link["ifindex"] not in old_idxs + ] + if len(nk_links) != 2: + raise KsftSkipEx("Failed to create netkit pair") + + nk_links.sort(key=lambda x: x["ifindex"]) + cfg.nk_host_ifname = nk_links[1]["ifname"] + cfg.nk_guest_ifname = nk_links[0]["ifname"] + cfg.nk_host_ifindex = nk_links[1]["ifindex"] + cfg.nk_guest_ifindex = nk_links[0]["ifindex"] + + ip(f"link set dev {cfg.nk_guest_ifname} netns {cfg.netns.name}") + ip(f"link set dev {cfg.nk_host_ifname} up") + ip(f"-6 addr add fe80::1/64 dev {cfg.nk_host_ifname} nodad") + ip( + f"-6 route add {cfg.nk_guest_ipv6}/128 via fe80::2 " + f"dev {cfg.nk_host_ifname}" + ) + ip(f"link set dev {cfg.nk_guest_ifname} up", ns=cfg.netns) + ip(f"-6 addr add fe80::2/64 dev {cfg.nk_guest_ifname}", ns=cfg.netns) + ip( + f"-6 addr add {cfg.nk_guest_ipv6}/64 dev {cfg.nk_guest_ifname} nodad", + ns=cfg.netns, + ) + ip( + f"-6 route add default via fe80::1 dev {cfg.nk_guest_ifname}", + ns=cfg.netns, + ) + + cfg.attach_bpf() + + +def _setup_lease(cfg, rxqueues=2): + _create_netkit_pair(cfg, rxqueues=rxqueues) + + ethnl = EthtoolFamily() + channels = ethnl.channels_get({"header": {"dev-index": cfg.ifindex}})[ + "combined-count" + ] + if channels < 2: + raise KsftSkipEx( + "Test requires NETIF with at least 2 combined channels" + ) + src_queue = channels - 1 + + with NetNSEnter(str(cfg.netns)): + netdevnl = NetdevFamily() + bind_result = netdevnl.queue_create( + { + "ifindex": cfg.nk_guest_ifindex, + "type": "rx", + "lease": { + "ifindex": cfg.ifindex, + "queue": {"id": src_queue, "type": "rx"}, + "netns-id": 0, + }, + } + ) + return src_queue, bind_result["id"] + + +def _teardown_netkit(cfg): + if cfg.nk_host_ifname: + cmd(f"ip link del dev {cfg.nk_host_ifname}", fail=False) + cfg.nk_host_ifname = None + cfg.nk_guest_ifname = None + + +def set_flow_rule(cfg, src_queue): output = ethtool( - f"-N {cfg.ifname} flow-type tcp6 dst-port {cfg.port} action {cfg.src_queue}" + f"-N {cfg.ifname} flow-type tcp6 dst-port {cfg.port} action {src_queue}" ).stdout values = re.search(r"ID (\d+)", output).group(1) return int(values) @@ -41,6 +171,8 @@ def set_flow_rule(cfg): def test_iou_zcrx(cfg) -> None: cfg.require_ipver("6") + src_queue, nk_queue = _setup_lease(cfg) + defer(_teardown_netkit, cfg) ethnl = EthtoolFamily() rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}}) @@ -65,40 +197,121 @@ def test_iou_zcrx(cfg) -> None: }, ) - ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}") + ethtool(f"-X {cfg.ifname} equal {src_queue}") defer(ethtool, f"-X {cfg.ifname} default") - flow_rule_id = set_flow_rule(cfg) + flow_rule_id = set_flow_rule(cfg, src_queue) defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") - rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg._nk_guest_ifname} -q {cfg.nk_queue}" + rx_cmd = ( + f"{cfg.bin_local} -s -p {cfg.port} " + f"-i {cfg.nk_guest_ifname} -q {nk_queue}" + ) tx_cmd = f"{cfg.bin_remote} -c -h {cfg.nk_guest_ipv6} -p {cfg.port} -l 12840" - with bkg(rx_cmd, exit_wait=True): + with bkg(rx_cmd, exit_wait=True, ns=cfg.netns): + wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns) + cmd(tx_cmd, host=cfg.remote) + + +def test_iou_zcrx_large_buf(cfg) -> None: + """iou-zcrx with rx_buf_len > page size, going through a netkit-leased + queue. Exercises the queue rx-buf-len path via netif_mp_open_rxq()'s + lease redirect: the netkit ifindex is opaque to io_uring, but + rx_page_size is honoured by the *physical* qops because the lease + pointer rewrites the request from netkit onto the leased physical + rxq before supported_params/validate_qcfg are consulted. + """ + cfg.require_ipver("6") + src_queue, nk_queue = _setup_lease(cfg) + defer(_teardown_netkit, cfg) + ethnl = EthtoolFamily() + + with open("/proc/sys/vm/nr_hugepages", "r+", encoding="utf-8") as f: + nr_hugepages = int(f.read().strip()) + if nr_hugepages < 64: + f.seek(0) + f.write("64") + defer(_restore_hugepages, nr_hugepages) + + rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}}) + rx_rings = rings["rx"] + hds_thresh = rings.get("hds-thresh", 0) + + ethnl.rings_set( + { + "header": {"dev-index": cfg.ifindex}, + "tcp-data-split": "enabled", + "hds-thresh": 0, + "rx": 64, + } + ) + defer( + ethnl.rings_set, + { + "header": {"dev-index": cfg.ifindex}, + "tcp-data-split": "unknown", + "hds-thresh": hds_thresh, + "rx": rx_rings, + }, + ) + + ethtool(f"-X {cfg.ifname} equal {src_queue}") + defer(ethtool, f"-X {cfg.ifname} default") + + flow_rule_id = set_flow_rule(cfg, src_queue) + defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") + + # -x 2 asks iou-zcrx for rx_buf_len = 2 * page_size (8 KiB on x86_64), + # backed by a 2 MiB hugepage area so the chunks are physically + # contiguous, which is what zcrx requires for non-default rx_buf_len. + rx_cmd = ( + f"{cfg.bin_local} -s -p {cfg.port} " + f"-i {cfg.nk_guest_ifname} -q {nk_queue} -x 2" + ) + tx_cmd = f"{cfg.bin_remote} -c -h {cfg.nk_guest_ipv6} -p {cfg.port} -l 12840" + + # Probe via -d (dry run): exits with SKIP_CODE if the leased physical + # qops doesn't advertise QCFG_RX_PAGE_SIZE (e.g. older bnxt FW/HW). + probe = cmd(rx_cmd + " -d", fail=False, ns=cfg.netns) + if probe.ret == SKIP_CODE: + msg = probe.stdout.strip() or "rx_buf_len not supported by leased NIC" + raise KsftSkipEx(msg) + + # A successful dry run still registered the zcrx ifq on the leased + # physical queue; wait for its async teardown before the real server + # binds the same queue. + _mp_clear_wait(cfg, src_queue) + + with bkg(rx_cmd, exit_wait=True, ns=cfg.netns): wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns) cmd(tx_cmd, host=cfg.remote) def test_attrs(cfg) -> None: cfg.require_ipver("6") + src_queue, nk_queue = _setup_lease(cfg) + defer(_teardown_netkit, cfg) netdevnl = NetdevFamily() queue_info = netdevnl.queue_get( - {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"} + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} ) - ksft_eq(queue_info["id"], cfg.src_queue) + ksft_eq(queue_info["id"], src_queue) ksft_eq(queue_info["type"], "rx") ksft_eq(queue_info["ifindex"], cfg.ifindex) ksft_in("lease", queue_info) lease = queue_info["lease"] ksft_eq(lease["ifindex"], cfg.nk_guest_ifindex) - ksft_eq(lease["queue"]["id"], cfg.nk_queue) + ksft_eq(lease["queue"]["id"], nk_queue) ksft_eq(lease["queue"]["type"], "rx") ksft_in("netns-id", lease) def test_attach_xdp_with_mp(cfg) -> None: cfg.require_ipver("6") + src_queue, nk_queue = _setup_lease(cfg) + defer(_teardown_netkit, cfg) ethnl = EthtoolFamily() rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}}) @@ -123,18 +336,21 @@ def test_attach_xdp_with_mp(cfg) -> None: }, ) - ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}") + ethtool(f"-X {cfg.ifname} equal {src_queue}") defer(ethtool, f"-X {cfg.ifname} default") netdevnl = NetdevFamily() - rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg._nk_guest_ifname} -q {cfg.nk_queue}" - with bkg(rx_cmd): + rx_cmd = ( + f"{cfg.bin_local} -s -p {cfg.port} " + f"-i {cfg.nk_guest_ifname} -q {nk_queue}" + ) + with bkg(rx_cmd, ns=cfg.netns): wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns) time.sleep(0.1) queue_info = netdevnl.queue_get( - {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"} + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} ) ksft_in("io-uring", queue_info) @@ -144,13 +360,15 @@ def test_attach_xdp_with_mp(cfg) -> None: time.sleep(0.1) queue_info = netdevnl.queue_get( - {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"} + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} ) ksft_not_in("io-uring", queue_info) def test_destroy(cfg) -> None: cfg.require_ipver("6") + src_queue, nk_queue = _setup_lease(cfg) + defer(_teardown_netkit, cfg) ethnl = EthtoolFamily() rings = ethnl.rings_get({"header": {"dev-index": cfg.ifindex}}) @@ -175,16 +393,19 @@ def test_destroy(cfg) -> None: }, ) - ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}") + ethtool(f"-X {cfg.ifname} equal {src_queue}") defer(ethtool, f"-X {cfg.ifname} default") - rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg._nk_guest_ifname} -q {cfg.nk_queue}" - rx_proc = cmd(rx_cmd, background=True) + rx_cmd = ( + f"{cfg.bin_local} -s -p {cfg.port} " + f"-i {cfg.nk_guest_ifname} -q {nk_queue}" + ) + rx_proc = cmd(rx_cmd, background=True, ns=cfg.netns) wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns) netdevnl = NetdevFamily() queue_info = netdevnl.queue_get( - {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"} + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} ) ksft_in("io-uring", queue_info) @@ -193,23 +414,20 @@ def test_destroy(cfg) -> None: kill_timer = threading.Timer(1, rx_proc.proc.terminate) kill_timer.start() - ip(f"link del dev {cfg._nk_host_ifname}") + ip(f"link del dev {cfg.nk_host_ifname}") kill_timer.join() - cfg._nk_host_ifname = None - cfg._nk_guest_ifname = None + cfg.nk_host_ifname = None + cfg.nk_guest_ifname = None queue_info = netdevnl.queue_get( - {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"} + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} ) ksft_not_in("io-uring", queue_info) - cmd(f"tc filter del dev {cfg.ifname} ingress pref {cfg._bpf_prog_pref}") - cfg._tc_attached = False - - flow_rule_id = set_flow_rule(cfg) + flow_rule_id = set_flow_rule(cfg, src_queue) defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") - rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.src_queue}" + rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {src_queue}" tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840" with bkg(rx_cmd, exit_wait=True): wait_port_listen(cfg.port, proto="tcp") @@ -217,7 +435,7 @@ def test_destroy(cfg) -> None: # Short delay since iou cleanup is async and takes a bit of time. time.sleep(0.1) queue_info = netdevnl.queue_get( - {"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"} + {"ifindex": cfg.ifindex, "id": src_queue, "type": "rx"} ) ksft_not_in("io-uring", queue_info) @@ -230,32 +448,14 @@ def main() -> None: cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) cfg.port = rand_port() - ethnl = EthtoolFamily() - channels = ethnl.channels_get({"header": {"dev-index": cfg.ifindex}}) - channels = channels["combined-count"] - if channels < 2: - raise KsftSkipEx("Test requires NETIF with at least 2 combined channels") - - cfg.src_queue = channels - 1 - - with NetNSEnter(str(cfg.netns)): - netdevnl = NetdevFamily() - bind_result = netdevnl.queue_create( - { - "ifindex": cfg.nk_guest_ifindex, - "type": "rx", - "lease": { - "ifindex": cfg.ifindex, - "queue": {"id": cfg.src_queue, "type": "rx"}, - "netns-id": 0, - }, - } - ) - cfg.nk_queue = bind_result["id"] - - # test_destroy must be last because it destroys the netkit devices ksft_run( - [test_iou_zcrx, test_attrs, test_attach_xdp_with_mp, test_destroy], + [ + test_iou_zcrx, + test_iou_zcrx_large_buf, + test_attrs, + test_attach_xdp_with_mp, + test_destroy, + ], args=(cfg,), ) ksft_exit() diff --git a/tools/testing/selftests/drivers/net/hw/ntuple.py b/tools/testing/selftests/drivers/net/hw/ntuple.py index 232733142c02..ef4604bfa8ef 100755 --- a/tools/testing/selftests/drivers/net/hw/ntuple.py +++ b/tools/testing/selftests/drivers/net/hw/ntuple.py @@ -22,7 +22,10 @@ class NtupleField(Enum): def _require_ntuple(cfg): features = ethtool(f"-k {cfg.ifname}", json=True)[0] if not features["ntuple-filters"]["active"]: - raise KsftSkipEx("Ntuple filters not enabled on the device: " + str(features["ntuple-filters"])) + if features["ntuple-filters"]["fixed"]: + raise KsftSkipEx("Device does not support ntuple-filters") + ethtool(f"-K {cfg.ifname} ntuple-filters on") + defer(ethtool, f"-K {cfg.ifname} ntuple-filters off") def _get_rx_cnts(cfg, prev=None): diff --git a/tools/testing/selftests/drivers/net/hw/rss_ctx.py b/tools/testing/selftests/drivers/net/hw/rss_ctx.py index 51f4e7bc3e5d..f36f76d6ca59 100755 --- a/tools/testing/selftests/drivers/net/hw/rss_ctx.py +++ b/tools/testing/selftests/drivers/net/hw/rss_ctx.py @@ -9,7 +9,7 @@ from lib.py import ksft_disruptive from lib.py import ksft_run, ksft_pr, ksft_exit from lib.py import ksft_eq, ksft_ne, ksft_ge, ksft_in, ksft_lt, ksft_true, ksft_raises from lib.py import NetDrvEpEnv -from lib.py import EthtoolFamily, NetdevFamily +from lib.py import EthtoolFamily, NetdevFamily, NlError from lib.py import KsftSkipEx, KsftFailEx from lib.py import rand_port, rand_ports from lib.py import cmd, ethtool, ip, defer, CmdExitFailure, wait_file @@ -57,9 +57,10 @@ def ethtool_create(cfg, act, opts): def require_ntuple(cfg): features = ethtool(f"-k {cfg.ifname}", json=True)[0] if not features["ntuple-filters"]["active"]: - # ntuple is more of a capability than a config knob, don't bother - # trying to enable it (until some driver actually needs it). - raise KsftSkipEx("Ntuple filters not enabled on the device: " + str(features["ntuple-filters"])) + if features["ntuple-filters"]["fixed"]: + raise KsftSkipEx("Device does not support ntuple-filters") + ethtool(f"-K {cfg.ifname} ntuple-filters on") + defer(ethtool, f"-K {cfg.ifname} ntuple-filters off") def require_context_cnt(cfg, need_cnt): @@ -828,6 +829,94 @@ def test_rss_default_context_rule(cfg): 'noise' : (0, 1) }) +def _set_flow_hash(cfg, fl_type, fields, context=0): + req = {"header": {"dev-index": cfg.ifindex}, + "flow-hash": {fl_type: fields}} + if context: + req["context"] = context + cfg.ethnl.rss_set(req) + + +def _get_flow_hash(cfg, fl_type, context=0): + req = {"header": {"dev-index": cfg.ifindex}} + if context: + req["context"] = context + rss = cfg.ethnl.rss_get(req) + return rss.get("flow-hash", {}).get(fl_type, set()) + + +def test_rss_context_flow_hash(cfg): + """ + Validate, with traffic, that an additional RSS context honors the + flow-hash field selection. If the driver lacks per-context field + configuration ("ops->rxfh_per_ctx_fields") fall back to setting the + fields on the main context, which the kernel applies device-wide. + """ + + require_ntuple(cfg) + + queue_cnt = len(_get_rx_cnts(cfg)) + if queue_cnt < 6: + try: + ksft_pr(f"Increasing queue count {queue_cnt} -> 6") + ethtool(f"-L {cfg.ifname} combined 6") + defer(ethtool, f"-L {cfg.ifname} combined {queue_cnt}") + except CmdExitFailure as exc: + raise KsftSkipEx("Not enough queues for the test") from exc + + fl_type = f"tcp{cfg.addr_ipver}" + if not _get_flow_hash(cfg, fl_type): + raise KsftSkipEx(f"Device does not report flow-hash for {fl_type}") + + # Reserve queues 0/1 for main, build a new context spanning 2..5 + ethtool(f"-X {cfg.ifname} equal 2") + defer(ethtool, f"-X {cfg.ifname} default") + ctx_id = ethtool_create(cfg, "-X", "context new start 2 equal 4") + defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") + + port = rand_port() + flow = f"flow-type {fl_type} dst-ip {cfg.addr} dst-port {port} context {ctx_id}" + ntuple = ethtool_create(cfg, "-N", flow) + defer(ethtool, f"-N {cfg.ifname} delete {ntuple}") + + ip_only = {"ip-src", "ip-dst"} + ip_l4 = ip_only | {"l4-b-0-1", "l4-b-2-3"} + + # Try per-context flow-hash; fall back to main context if unsupported. + cfg_ctx = ctx_id + try: + orig = _get_flow_hash(cfg, fl_type, context=ctx_id) + _set_flow_hash(cfg, fl_type, ip_only, context=ctx_id) + except NlError: + ksft_pr("Per-context flow-hash not supported, using device-wide") + cfg_ctx = 0 + orig = _get_flow_hash(cfg, fl_type) + _set_flow_hash(cfg, fl_type, ip_only) + defer(_set_flow_hash, cfg, fl_type, orig, context=cfg_ctx) + + def measure(): + cnts = _get_rx_cnts(cfg) + GenerateTraffic(cfg, port=port).wait_pkts_and_stop(20000) + cnts = _get_rx_cnts(cfg, prev=cnts) + ctx_cnts = cnts[2:6] + directed = sum(ctx_cnts) + used = sum(1 for c in ctx_cnts if c > directed / 200) + return cnts, directed, used + + # IP-only hash: iperf3 streams share src/dst IP, all should land on the + # same queue inside the context's range. + cnts, directed, used = measure() + ksft_ge(directed, 20000, f"traffic on context {ctx_id} (IP-only): {cnts}") + ksft_eq(used, 1, f"IP-only hash should use one queue in context {ctx_id}, got: {cnts}") + + # IP+L4 hash: streams have distinct src ports, traffic should spread. + _set_flow_hash(cfg, fl_type, ip_l4, context=cfg_ctx) + + cnts, directed, used = measure() + ksft_ge(directed, 20000, f"traffic on context {ctx_id} (IP+L4): {cnts}") + ksft_ge(used, 2, f"IP+L4 hash should spread across context {ctx_id} queues, got: {cnts}") + + @ksft_disruptive def test_rss_context_persist_ifupdown(cfg, pre_down=False): """ @@ -935,6 +1024,7 @@ def main() -> None: test_flow_add_context_missing, test_delete_rss_context_busy, test_rss_ntuple_addition, test_rss_default_context_rule, + test_rss_context_flow_hash, test_rss_context_persist_create_and_ifdown, test_rss_context_persist_ifdown_and_create], args=(cfg, )) diff --git a/tools/testing/selftests/drivers/net/hw/tso.py b/tools/testing/selftests/drivers/net/hw/tso.py index bb675e3dac88..1b789fea8929 100755 --- a/tools/testing/selftests/drivers/net/hw/tso.py +++ b/tools/testing/selftests/drivers/net/hw/tso.py @@ -239,6 +239,9 @@ def main() -> None: ("vxlan_csum", "", "tx-udp_tnl-csum-segmentation", ("vxlan", "id 100 dstport 4789 udpcsum", ("4", "6"))), ("gre", "4", "tx-gre-segmentation", ("gre", "", ("4", "6"))), ("gre", "6", "tx-gre-segmentation", ("ip6gre","", ("4", "6"))), + ("ip", "6", "tx-ipxip6-segmentation", ("ip6tnl","mode any", ("4", "6"))), + ("ip", "4", "tx-ipxip4-segmentation", ("sit","", ("6", ))), + ("ip", "4", "tx-ipxip4-segmentation", ("ipip","", ("4", ))), ) cases = [] diff --git a/tools/testing/selftests/drivers/net/hw/userns_devmem.py b/tools/testing/selftests/drivers/net/hw/userns_devmem.py new file mode 100755 index 000000000000..2aaf6ea81715 --- /dev/null +++ b/tools/testing/selftests/drivers/net/hw/userns_devmem.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Devmem tests for non-init userns. +""" + +import os + +from devmem_lib import run_rx, run_rx_hds, run_tx, run_tx_chunks, setup_test +from lib.py import NetDrvContEnv, ksft_disruptive, ksft_exit, ksft_run + + +@ksft_disruptive +def check_userns_rx(cfg) -> None: + """Run the devmem RX test through non-init userns netkit.""" + run_rx(cfg) + + +@ksft_disruptive +def check_userns_tx(cfg) -> None: + """Run the devmem TX test through non-init userns netkit.""" + run_tx(cfg) + + +@ksft_disruptive +def check_userns_tx_chunks(cfg) -> None: + """Run the devmem TX chunking test through non-init userns netkit.""" + run_tx_chunks(cfg) + + +def check_userns_rx_hds(cfg) -> None: + """Run the HDS test through non-init userns netkit.""" + run_rx_hds(cfg) + + +def main() -> None: + """Run userns devmem RX selftests against the test environment.""" + with NetDrvContEnv(__file__, userns=True, rxqueues=2, + primary_rx_redirect=True) as cfg: + setup_test(cfg, + os.path.join(os.path.dirname(os.path.abspath(__file__)), + "ncdevmem")) + ksft_run([check_userns_rx, check_userns_tx, check_userns_tx_chunks, + check_userns_rx_hds], args=(cfg,)) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/drivers/net/lib/py/__init__.py b/tools/testing/selftests/drivers/net/lib/py/__init__.py index 2b5ec0505672..ee903bcf3207 100644 --- a/tools/testing/selftests/drivers/net/lib/py/__init__.py +++ b/tools/testing/selftests/drivers/net/lib/py/__init__.py @@ -18,12 +18,13 @@ try: sys.path.append(KSFT_DIR.as_posix()) # Import one by one to avoid pylint false positives - from net.lib.py import NetNS, NetNSEnter, NetdevSimDev + from net.lib.py import NetNS, NetNSEnter, NetdevSimDev, UserNetNS from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \ NlError, RtnlFamily, DevlinkFamily, PSPFamily, Netlink from net.lib.py import CmdExitFailure from net.lib.py import bkg, cmd, bpftool, bpftrace, defer, ethtool, \ - fd_read_timeout, ip, rand_port, rand_ports, wait_port_listen, wait_file + fd_read_timeout, ip, rand_port, rand_ports, tc, wait_port_listen, \ + wait_file from net.lib.py import bpf_map_set, bpf_map_dump, bpf_prog_map_ids from net.lib.py import KsftSkipEx, KsftFailEx, KsftXfailEx from net.lib.py import ksft_disruptive, ksft_exit, ksft_pr, ksft_run, \ @@ -31,12 +32,12 @@ try: from net.lib.py import ksft_eq, ksft_ge, ksft_in, ksft_is, ksft_lt, \ ksft_ne, ksft_not_in, ksft_raises, ksft_true, ksft_gt, ksft_not_none - __all__ = ["NetNS", "NetNSEnter", "NetdevSimDev", + __all__ = ["NetNS", "NetNSEnter", "NetdevSimDev", "UserNetNS", "EthtoolFamily", "NetdevFamily", "NetshaperFamily", "NlError", "RtnlFamily", "DevlinkFamily", "PSPFamily", "Netlink", "CmdExitFailure", "bkg", "cmd", "bpftool", "bpftrace", "defer", "ethtool", - "fd_read_timeout", "ip", "rand_port", "rand_ports", + "fd_read_timeout", "ip", "rand_port", "rand_ports", "tc", "wait_port_listen", "wait_file", "bpf_map_set", "bpf_map_dump", "bpf_prog_map_ids", "KsftSkipEx", "KsftFailEx", "KsftXfailEx", diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py index 24ce122abd9c..e4ab99b905b1 100644 --- a/tools/testing/selftests/drivers/net/lib/py/env.py +++ b/tools/testing/selftests/drivers/net/lib/py/env.py @@ -2,13 +2,14 @@ import ipaddress import os +import sys import time import json from pathlib import Path from lib.py import KsftSkipEx, KsftXfailEx from lib.py import ksft_setup, wait_file from lib.py import cmd, ethtool, ip, CmdExitFailure -from lib.py import NetNS, NetdevSimDev +from lib.py import NetNS, NetdevSimDev, UserNetNS from .remote import Remote from . import bpftool, RtnlFamily, Netlink @@ -336,15 +337,20 @@ class NetDrvContEnv(NetDrvEpEnv): +---------------+ """ - def __init__(self, src_path, rxqueues=1, **kwargs): + def __init__(self, src_path, rxqueues=1, primary_rx_redirect=False, + userns=False, **kwargs): self.netns = None - self._nk_host_ifname = None - self._nk_guest_ifname = None + self._userns = userns + self.nk_host_ifname = None + self.nk_guest_ifname = None self._tc_clsact_added = False self._tc_attached = False + self._primary_rx_redirect_attached = False + self._primary_rx_redirect_clsact_added = False self._bpf_prog_pref = None self._bpf_prog_id = None self._init_ns_attached = False + self._remote_route_added = False self._old_fwd = None self._old_accept_ra = None @@ -389,15 +395,25 @@ class NetDrvContEnv(NetDrvEpEnv): raise KsftSkipEx("Failed to create netkit pair") netkit_links.sort(key=lambda x: x['ifindex']) - self._nk_host_ifname = netkit_links[1]['ifname'] - self._nk_guest_ifname = netkit_links[0]['ifname'] + self.nk_host_ifname = netkit_links[1]['ifname'] + self.nk_guest_ifname = netkit_links[0]['ifname'] self.nk_host_ifindex = netkit_links[1]['ifindex'] self.nk_guest_ifindex = netkit_links[0]['ifindex'] self._setup_ns() - self._attach_bpf() + self.attach_bpf() + if primary_rx_redirect: + self._attach_primary_rx_redirect_bpf() def __del__(self): + if self._primary_rx_redirect_attached: + cmd(f"tc filter del dev {self.nk_host_ifname} ingress", fail=False) + self._primary_rx_redirect_attached = False + + if self._primary_rx_redirect_clsact_added: + cmd(f"tc qdisc del dev {self.nk_host_ifname} clsact", fail=False) + self._primary_rx_redirect_clsact_added = False + if self._tc_attached: cmd(f"tc filter del dev {self.ifname} ingress pref {self._bpf_prog_pref}") self._tc_attached = False @@ -406,10 +422,15 @@ class NetDrvContEnv(NetDrvEpEnv): cmd(f"tc qdisc del dev {self.ifname} clsact") self._tc_clsact_added = False - if self._nk_host_ifname: - cmd(f"ip link del dev {self._nk_host_ifname}") - self._nk_host_ifname = None - self._nk_guest_ifname = None + if self._remote_route_added: + cmd(f"ip -6 route del {self.nk_guest_ipv6}/128", + host=self.remote, fail=False) + self._remote_route_added = False + + if self.nk_host_ifname: + cmd(f"ip link del dev {self.nk_host_ifname}") + self.nk_host_ifname = None + self.nk_guest_ifname = None if self._init_ns_attached: cmd("ip netns del init", fail=False) @@ -444,28 +465,37 @@ class NetDrvContEnv(NetDrvEpEnv): with open(ra_path, "w", encoding="utf-8") as f: f.write("2") - self.netns = NetNS() + self.netns = UserNetNS() if self._userns else NetNS() cmd("ip netns attach init 1") self._init_ns_attached = True ip("netns set init 0", ns=self.netns) - ip(f"link set dev {self._nk_guest_ifname} netns {self.netns.name}") - ip(f"link set dev {self._nk_host_ifname} up") - ip(f"-6 addr add fe80::1/64 dev {self._nk_host_ifname} nodad") - ip(f"-6 route add {self.nk_guest_ipv6}/128 via fe80::2 dev {self._nk_host_ifname}") + ip(f"link set dev {self.nk_guest_ifname} netns {self.netns.name}") + nk_guest_dev = ip(f"link show dev {self.nk_guest_ifname}", + json=True, ns=self.netns)[0] + self.nk_guest_ifindex = nk_guest_dev['ifindex'] + ip(f"link set dev {self.nk_host_ifname} up") + ip(f"-6 addr add fe80::1/64 dev {self.nk_host_ifname} nodad") + ip(f"-6 route add {self.nk_guest_ipv6}/128 via fe80::2 dev {self.nk_host_ifname}") ip("link set lo up", ns=self.netns) - ip(f"link set dev {self._nk_guest_ifname} up", ns=self.netns) - ip(f"-6 addr add fe80::2/64 dev {self._nk_guest_ifname}", ns=self.netns) - ip(f"-6 addr add {self.nk_guest_ipv6}/64 dev {self._nk_guest_ifname} nodad", ns=self.netns) - ip(f"-6 route add default via fe80::1 dev {self._nk_guest_ifname}", ns=self.netns) + ip(f"link set dev {self.nk_guest_ifname} up", ns=self.netns) + ip(f"-6 addr add fe80::2/64 dev {self.nk_guest_ifname}", ns=self.netns) + ip(f"-6 addr add {self.nk_guest_ipv6}/64 dev {self.nk_guest_ifname} nodad", ns=self.netns) + ip(f"-6 route add default via fe80::1 dev {self.nk_guest_ifname}", ns=self.netns) - def _tc_ensure_clsact(self): - qdisc = json.loads(cmd(f"tc -j qdisc show dev {self.ifname}").stdout) + def _tc_ensure_clsact(self, ifname=None): + """Ensure a clsact qdisc exists on @ifname. + + Returns True if this call added the qdisc, otherwise returns False. + """ + if ifname is None: + ifname = self.ifname + qdisc = json.loads(cmd(f"tc -j qdisc show dev {ifname}").stdout) for q in qdisc: if q['kind'] == 'clsact': - return - cmd(f"tc qdisc add dev {self.ifname} clsact") - self._tc_clsact_added = True + return False + cmd(f"tc qdisc add dev {ifname} clsact") + return True def _get_bpf_prog_ids(self): filters = json.loads(cmd(f"tc -j filter show dev {self.ifname} ingress").stdout) @@ -476,28 +506,43 @@ class NetDrvContEnv(NetDrvEpEnv): return (bpf['pref'], bpf['options']['prog']['id']) raise Exception("Failed to get BPF prog ID") - def _attach_bpf(self): - bpf_obj = self.test_dir / "nk_forward.bpf.o" - if not bpf_obj.exists(): - raise KsftSkipEx("BPF prog not found") + def _find_bss_map_id(self, prog_id): + """Find the .bss map ID for a loaded BPF program.""" + prog_info = bpftool(f"prog show id {prog_id}", json=True) + for map_id in prog_info.get("map_ids", []): + map_info = bpftool(f"map show id {map_id}", json=True) + if map_info.get("name", "").endswith("bss"): + return map_id + raise Exception(f"Failed to find .bss map for prog {prog_id}") - self._tc_ensure_clsact() + def _find_bpf_obj(self, name): + bpf_obj = self.test_dir / name + if bpf_obj.exists(): + return bpf_obj + bpf_obj = self.test_dir / "hw" / name + if bpf_obj.exists(): + return bpf_obj + return None + + def detach_bpf(self): + if self._tc_attached: + cmd(f"tc filter del dev {self.ifname} ingress pref " + f"{self._bpf_prog_pref}", fail=False) + self._tc_attached = False + + def attach_bpf(self): + bpf_obj = self._find_bpf_obj("nk_forward.bpf.o") + if not bpf_obj: + raise KsftSkipEx("BPF prog nk_forward.bpf.o not found") + + if self._tc_ensure_clsact(): + self._tc_clsact_added = True cmd(f"tc filter add dev {self.ifname} ingress bpf obj {bpf_obj}" " sec tc/ingress direct-action") self._tc_attached = True (self._bpf_prog_pref, self._bpf_prog_id) = self._get_bpf_prog_ids() - prog_info = bpftool(f"prog show id {self._bpf_prog_id}", json=True) - map_ids = prog_info.get("map_ids", []) - - bss_map_id = None - for map_id in map_ids: - map_info = bpftool(f"map show id {map_id}", json=True) - if map_info.get("name").endswith("bss"): - bss_map_id = map_id - - if bss_map_id is None: - raise Exception("Failed to find .bss map") + bss_map_id = self._find_bss_map_id(self._bpf_prog_id) ipv6_addr = ipaddress.IPv6Address(self.ipv6_prefix) ipv6_bytes = ipv6_addr.packed @@ -505,3 +550,36 @@ class NetDrvContEnv(NetDrvEpEnv): value = ipv6_bytes + ifindex_bytes value_hex = ' '.join(f'{b:02x}' for b in value) bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}") + + def _attach_primary_rx_redirect_bpf(self): + """Attach BPF redirect program on the primary netkit ingress.""" + bpf_obj = self._find_bpf_obj("nk_primary_rx_redirect.bpf.o") + if not bpf_obj: + raise KsftSkipEx("nk_primary_rx_redirect.bpf.o not found") + + if self._tc_ensure_clsact(self.nk_host_ifname): + self._primary_rx_redirect_clsact_added = True + cmd(f"tc filter add dev {self.nk_host_ifname} ingress" + f" bpf obj {bpf_obj} sec tc/ingress direct-action") + self._primary_rx_redirect_attached = True + + ip(f"-6 route add {self.nk_guest_ipv6}/128 via {self.addr_v['6']}", + host=self.remote) + self._remote_route_added = True + + filters = json.loads( + cmd(f"tc -j filter show dev {self.nk_host_ifname} ingress").stdout) + redirect_prog_id = None + for bpf in filters: + if 'options' not in bpf: + continue + if bpf['options']['bpf_name'].startswith('nk_primary_rx_redirect'): + redirect_prog_id = bpf['options']['prog']['id'] + break + if redirect_prog_id is None: + raise Exception("Failed to get primary RX redirect BPF prog ID") + + bss_map_id = self._find_bss_map_id(redirect_prog_id) + phys_ifindex_bytes = self.ifindex.to_bytes(4, byteorder=sys.byteorder) + value_hex = ' '.join(f'{b:02x}' for b in phys_ifindex_bytes) + bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}") diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py index 864d9fce1094..315648a770d0 100755 --- a/tools/testing/selftests/drivers/net/psp.py +++ b/tools/testing/selftests/drivers/net/psp.py @@ -5,6 +5,7 @@ import errno import fcntl +import os import socket import struct import termios @@ -14,9 +15,13 @@ from lib.py import defer from lib.py import ksft_run, ksft_exit, ksft_pr from lib.py import ksft_true, ksft_eq, ksft_ne, ksft_gt, ksft_raises from lib.py import ksft_not_none -from lib.py import KsftSkipEx -from lib.py import NetDrvEpEnv, PSPFamily, NlError +from lib.py import ksft_variants, KsftNamedVariant +from lib.py import KsftSkipEx, KsftFailEx +from lib.py import NetDrvEpEnv, NetDrvContEnv +from lib.py import Netlink, NlError, PSPFamily, RtnlFamily +from lib.py import NetNSEnter from lib.py import bkg, rand_port, wait_port_listen +from lib.py import ip def _get_outq(s): @@ -117,11 +122,13 @@ def _get_stat(cfg, key): # Test case boiler plate # -def _init_psp_dev(cfg): +def _init_psp_dev(cfg, use_psp_ifindex=False): if not hasattr(cfg, 'psp_dev_id'): # Figure out which local device we are testing against + # For NetDrvContEnv: use psp_ifindex instead of ifindex + target_ifindex = cfg.psp_ifindex if use_psp_ifindex else cfg.ifindex for dev in cfg.pspnl.dev_get({}, dump=True): - if dev['ifindex'] == cfg.ifindex: + if dev['ifindex'] == target_ifindex: cfg.psp_info = dev cfg.psp_dev_id = cfg.psp_info['id'] break @@ -571,33 +578,388 @@ def removal_device_bi(cfg): _close_conn(cfg, s) -def psp_ip_ver_test_builder(name, test_func, psp_ver, ipver): - """Build test cases for each combo of PSP version and IP version""" - def test_case(cfg): - cfg.require_ipver(ipver) - test_func(cfg, psp_ver, ipver) - - test_case.__name__ = f"{name}_v{psp_ver}_ip{ipver}" - return test_case +def _get_psp_ver_ip_variants(): + for ver in range(4): + for ipv in ("4", "6"): + yield KsftNamedVariant(f"v{ver}_ip{ipv}", ver, ipv) -def ipver_test_builder(name, test_func, ipver): - """Build test cases for each IP version""" - def test_case(cfg): - cfg.require_ipver(ipver) - test_func(cfg, ipver) +def _get_ip_variants(): + for ipv in ("4", "6"): + yield KsftNamedVariant(f"ip{ipv}", ipv) + + +@ksft_variants(_get_psp_ver_ip_variants()) +def data_basic_send(cfg, version, ipver): + """Test basic PSP data send.""" + cfg.require_ipver(ipver) + _data_basic_send(cfg, version, ipver) + + +@ksft_variants(_get_ip_variants()) +def data_mss_adjust(cfg, ipver): + """Test MSS adjustment with PSP.""" + cfg.require_ipver(ipver) + _data_mss_adjust(cfg, ipver) + + +def _check_assoc_list(cfg, psp_dev_id, ifindex, nsid=None): + """Verify assoc-list contains device with given ifindex, no duplicates.""" + dev_info = cfg.pspnl.dev_get({'id': psp_dev_id}) + + ksft_true('assoc-list' in dev_info, + "No assoc-list in dev_get() response after association") + found = False + for assoc in dev_info['assoc-list']: + if assoc['ifindex'] != ifindex: + continue + if nsid is not None and assoc['nsid'] != nsid: + continue + ksft_eq(found, False, "Duplicate assoc entry found") + found = True + ksft_eq(found, True, + "Associated device not found in dev_get() response") + + +def _data_basic_send_netkit_psp_assoc(cfg, version, ipver): + """ + Test basic data send with netkit interface associated with PSP dev. + """ + _assoc_nk_guest(cfg) + + # Enter guest namespace (netns) to run PSP test + with NetNSEnter(cfg.netns.name): + cfg.pspnl = PSPFamily() + + sock = _make_psp_conn(cfg, version, ipver) + + rx_assoc = cfg.pspnl.rx_assoc({"version": version, + "dev-id": cfg.psp_dev_id, + "sock-fd": sock.fileno()}) + rx_key = rx_assoc['rx-key'] + tx_key = _spi_xchg(sock, rx_key) + + cfg.pspnl.tx_assoc({"dev-id": cfg.psp_dev_id, + "version": version, + "tx-key": tx_key, + "sock-fd": sock.fileno()}) + + data_len = _send_careful(cfg, sock, 100) + _check_data_rx(cfg, data_len) + _close_psp_conn(cfg, sock) + + +def _assoc_check_list(cfg): + """Test that assoc-list is correctly populated after dev-assoc.""" + _assoc_nk_guest(cfg) + _check_assoc_list(cfg, cfg.psp_dev_id, cfg.nk_guest_ifindex, + cfg.psp_dev_peer_nsid) + + +def _get_psp_ver_ip6_variants(): + for ver in range(4): + yield KsftNamedVariant(f"v{ver}_ip6", ver, "6") + + +@ksft_variants(_get_psp_ver_ip6_variants()) +def data_basic_send_netkit_psp_assoc(cfg, version, ipver): + """Test PSP data send via netkit with dev-assoc.""" + cfg.require_ipver(ipver) + _data_basic_send_netkit_psp_assoc(cfg, version, ipver) + + +def _key_rotation_notify_multi_ns_netkit(cfg): + """ Test key rotation notifications across multiple namespaces using netkit """ + _assoc_nk_guest(cfg) + + # Create listener in guest namespace; socket stays bound to that ns + with NetNSEnter(cfg.netns.name): + peer_pspnl = PSPFamily() + peer_pspnl.ntf_subscribe('use') + + # Create listener in main namespace + main_pspnl = PSPFamily() + main_pspnl.ntf_subscribe('use') + + # Trigger key rotation on the PSP device + cfg.pspnl.key_rotate({"id": cfg.psp_dev_id}) + + # Poll both sockets from main thread + for pspnl, label in [(main_pspnl, "main"), (peer_pspnl, "guest")]: + for ntf in pspnl.poll_ntf(duration=10): + if ntf['msg'].get('id') == cfg.psp_dev_id: + break + else: + raise KsftFailEx( + f"No key rotation notification received" + f" in {label} namespace") + + +def _dev_change_notify_multi_ns_netkit(cfg): + """ Test dev_change notifications across multiple namespaces using netkit """ + _assoc_nk_guest(cfg) + + # Create listener in guest namespace; socket stays bound to that ns + with NetNSEnter(cfg.netns.name): + peer_pspnl = PSPFamily() + peer_pspnl.ntf_subscribe('mgmt') + + # Create listener in main namespace + main_pspnl = PSPFamily() + main_pspnl.ntf_subscribe('mgmt') + + # Trigger dev_change by calling dev_set (notification is always sent) + cfg.pspnl.dev_set({'id': cfg.psp_dev_id, + 'psp-versions-ena': cfg.psp_info['psp-versions-cap']}) + + # Poll both sockets from main thread + for pspnl, label in [(main_pspnl, "main"), (peer_pspnl, "guest")]: + for ntf in pspnl.poll_ntf(duration=10): + if ntf['msg'].get('id') == cfg.psp_dev_id: + break + else: + raise KsftFailEx( + f"No dev_change notification received" + f" in {label} namespace") + + +def _psp_dev_get_check_netkit_psp_assoc(cfg): + """ Check psp dev-get output with netkit interface associated with PSP dev """ + _assoc_nk_guest(cfg) + + # Check 1: In default netns, verify dev-get has correct ifindex and assoc-list + dev_info = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) + ksft_eq(dev_info['ifindex'], cfg.psp_ifindex) + _check_assoc_list(cfg, cfg.psp_dev_id, cfg.nk_guest_ifindex, + cfg.psp_dev_peer_nsid) + + # Check 2: In guest netns, verify dev-get has assoc-list with nk_guest device + with NetNSEnter(cfg.netns.name): + peer_pspnl = PSPFamily() + + # Dump all devices in the guest namespace + peer_devices = peer_pspnl.dev_get({}, dump=True) + + # Find the device with by-association flag + peer_dev = None + for dev in peer_devices: + if dev.get('by-association'): + peer_dev = dev + break + + ksft_not_none(peer_dev, "No PSP device found with by-association flag in guest netns") + + # Verify assoc-list contains the nk_guest device + ksft_true('assoc-list' in peer_dev and len(peer_dev['assoc-list']) > 0, + "Guest device should have assoc-list with local devices") + + # Verify the assoc-list contains nk_guest ifindex with nsid=-1 (same namespace) + found = False + for assoc in peer_dev['assoc-list']: + if assoc['ifindex'] == cfg.nk_guest_ifindex: + ksft_eq(assoc['nsid'], -1, + "nsid should be -1 (NETNSA_NSID_NOT_ASSIGNED) for same-namespace device") + found = True + break + ksft_true(found, "nk_guest ifindex not found in assoc-list") + + +def _dev_assoc_no_nsid(cfg): + """ Test dev-assoc and dev-disassoc without nsid attribute """ + _init_psp_dev(cfg, True) + + # Associate without nsid - should look up ifindex in caller's netns + cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, + 'ifindex': cfg.nk_host_ifindex}) + defer(_try_disassoc, cfg, + cfg.psp_dev_id, cfg.nk_host_ifindex) + defer(delattr, cfg, 'psp_dev_id') + defer(delattr, cfg, 'psp_info') + + # Verify assoc-list contains the device (match by ifindex only) + _check_assoc_list(cfg, cfg.psp_dev_id, cfg.nk_host_ifindex) + + # Disassociate without nsid - should also use caller's netns + cfg.pspnl.dev_disassoc({'id': cfg.psp_dev_id, + 'ifindex': cfg.nk_host_ifindex}) + + # Verify assoc-list no longer contains the device + dev_info = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) + found = False + if 'assoc-list' in dev_info: + for assoc in dev_info['assoc-list']: + if assoc['ifindex'] == cfg.nk_host_ifindex: + found = True + break + ksft_true(not found, "Device should not be in assoc-list after disassociation") + + +def _psp_dev_assoc_cleanup_on_netkit_del(cfg): + """Test that assoc-list is cleared when associated netkit is deleted. + + Creates a disposable netkit pair for this test to avoid destroying + the shared environment. + """ + _init_psp_dev(cfg, True) + defer(delattr, cfg, 'psp_dev_id') + defer(delattr, cfg, 'psp_info') + + existing = {cfg.nk_host_ifindex, cfg.nk_guest_ifindex} + + # Create a temporary netkit pair + tmp_host_name = "tmp_nk_host" + tmp_guest_name = "tmp_nk_guest" + rtnl = RtnlFamily() + rtnl.newlink( + { + "ifname": tmp_host_name, + "linkinfo": { + "kind": "netkit", + "data": { + "mode": "l2", + "policy": "forward", + "peer-policy": "forward", + }, + }, + }, + flags=[Netlink.NLM_F_CREATE, Netlink.NLM_F_EXCL], + ) + cleanup_netkit = defer(ip, f"link del {tmp_host_name}") + + # Find the peer by diffing against existing netkit ifindexes + all_links = ip("-d link show", json=True) + tmp_peer = [link for link in all_links + if link.get('linkinfo', {}).get('info_kind') == 'netkit' + and link['ifindex'] not in existing + and link['ifname'] != tmp_host_name] + ksft_eq(len(tmp_peer), 1, + "Failed to find temporary netkit peer") + guest_name = tmp_peer[0]['ifname'] + + # Rename and move guest end into the test namespace + ip(f"link set dev {guest_name} name {tmp_guest_name}") + ip(f"link set dev {tmp_guest_name} netns {cfg.netns.name}") + tmp_guest_dev = ip(f"link show dev {tmp_guest_name}", + json=True, ns=cfg.netns)[0] + tmp_guest_ifindex = tmp_guest_dev['ifindex'] + ip(f"link set dev {tmp_guest_name} up", ns=cfg.netns) + + # Associate PSP device with the temporary guest interface + cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, + 'ifindex': tmp_guest_ifindex, + 'nsid': cfg.psp_dev_peer_nsid}) + + # Verify assoc-list contains the temporary device + _check_assoc_list(cfg, cfg.psp_dev_id, tmp_guest_ifindex, + cfg.psp_dev_peer_nsid) + + # Delete the temporary netkit pair (deleting one end removes both) + ip(f"link del {tmp_host_name}") + cleanup_netkit.cancel() + + # Verify assoc-list is cleared after netkit deletion + dev_info = cfg.pspnl.dev_get({'id': cfg.psp_dev_id}) + ksft_true('assoc-list' not in dev_info + or len(dev_info['assoc-list']) == 0, + "assoc-list should be empty after netkit deletion") + + +def _try_disassoc(cfg, psp_dev_id, ifindex, nsid=None): + """Best-effort disassociate, ignoring errors if already removed.""" + try: + params = {'id': psp_dev_id, 'ifindex': ifindex} + if nsid is not None: + params['nsid'] = nsid + cfg.pspnl.dev_disassoc(params) + except NlError: + pass + + +def _assoc_nk_guest(cfg): + """Associate nk_guest with PSP device and register cleanup via defer().""" + _init_psp_dev(cfg, True) + + cfg.pspnl.dev_assoc({'id': cfg.psp_dev_id, + 'ifindex': cfg.nk_guest_ifindex, + 'nsid': cfg.psp_dev_peer_nsid}) + defer(_disassoc_nk_guest, cfg, + cfg.psp_dev_id, cfg.nk_guest_ifindex) + + +def _disassoc_nk_guest(cfg, psp_dev_id, nk_guest_ifindex): + """Disassociate nk_guest and reset cfg PSP state.""" + pspnl = PSPFamily() + pspnl.dev_disassoc({'id': psp_dev_id, 'ifindex': nk_guest_ifindex, + 'nsid': cfg.psp_dev_peer_nsid}) + cfg.pspnl = pspnl + del cfg.psp_dev_id + del cfg.psp_info + + +def _get_nsid(ns_name): + """Get the nsid for a namespace.""" + for entry in ip("netns list-id", json=True): + if entry.get("name") == str(ns_name): + return entry["nsid"] + raise KsftSkipEx(f"nsid not found for namespace {ns_name}") + + +def _setup_psp_attributes(cfg): + # pylint: disable=protected-access + """ + Set up PSP-specific attributes on the environment. + + This sets attributes needed for PSP tests based on whether we're using + netdevsim or a real NIC. + """ + if cfg._ns is not None: + # netdevsim case: PSP device is the local dev (in host namespace) + cfg.psp_dev = cfg._ns.nsims[0].dev + cfg.psp_ifname = cfg.psp_dev['ifname'] + cfg.psp_ifindex = cfg.psp_dev['ifindex'] + + # PSP peer device is the remote dev (in _netns, where psp_responder runs) + cfg.psp_dev_peer = cfg._ns_peer.nsims[0].dev + cfg.psp_dev_peer_ifname = cfg.psp_dev_peer['ifname'] + cfg.psp_dev_peer_ifindex = cfg.psp_dev_peer['ifindex'] + else: + # Real NIC case: PSP device is the local interface + cfg.psp_dev = cfg.dev + cfg.psp_ifname = cfg.ifname + cfg.psp_ifindex = cfg.ifindex + + # PSP peer device is the remote interface + cfg.psp_dev_peer = cfg.remote_dev + cfg.psp_dev_peer_ifname = cfg.remote_ifname + cfg.psp_dev_peer_ifindex = cfg.remote_ifindex + + # Get nsid for the guest namespace (netns) where nk_guest is + cfg.psp_dev_peer_nsid = _get_nsid(cfg.netns.name) - test_case.__name__ = f"{name}_ip{ipver}" - return test_case def main() -> None: """ Ksft boiler plate main """ - with NetDrvEpEnv(__file__) as cfg: + # Make sure LOCAL_PREFIX_V6 is set + if "LOCAL_PREFIX_V6" not in os.environ: + os.environ["LOCAL_PREFIX_V6"] = "2001:db8:2::" + + try: + env = NetDrvContEnv(__file__, primary_rx_redirect=True) + has_cont = True + except KsftSkipEx: + env = NetDrvEpEnv(__file__) + has_cont = False + + with env as cfg: cfg.pspnl = PSPFamily() + if has_cont: + _setup_psp_attributes(cfg) + # Set up responder and communication sock + # psp_responder runs in _netns (remote namespace with psp_dev_peer) responder = cfg.remote.deploy("psp_responder") cfg.comm_port = rand_port() @@ -611,17 +973,18 @@ def main() -> None: cfg.comm_port), timeout=1) - cases = [ - psp_ip_ver_test_builder( - "data_basic_send", _data_basic_send, version, ipver - ) - for version in range(0, 4) - for ipver in ("4", "6") - ] - cases += [ - ipver_test_builder("data_mss_adjust", _data_mss_adjust, ipver) - for ipver in ("4", "6") - ] + cases = [data_basic_send, data_mss_adjust] + + if has_cont: + cases += [ + _assoc_check_list, + data_basic_send_netkit_psp_assoc, + _key_rotation_notify_multi_ns_netkit, + _dev_change_notify_multi_ns_netkit, + _psp_dev_get_check_netkit_psp_assoc, + _dev_assoc_no_nsid, + _psp_dev_assoc_cleanup_on_netkit_del, + ] ksft_run(cases=cases, globs=globals(), case_pfx={"dev_", "data_", "assoc_", "removal_"}, diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c similarity index 95% rename from tools/testing/selftests/net/so_txtime.c rename to tools/testing/selftests/drivers/net/so_txtime.c index b76df1efc2ef..75f3beef13d9 100644 --- a/tools/testing/selftests/net/so_txtime.c +++ b/tools/testing/selftests/drivers/net/so_txtime.c @@ -33,9 +33,12 @@ #include #include +#include "kselftest.h" + static int cfg_clockid = CLOCK_TAI; static uint16_t cfg_port = 8000; static int cfg_variance_us = 4000; +static bool cfg_machine_slow; static uint64_t cfg_start_time_ns; static int cfg_mark; static bool cfg_rx; @@ -43,6 +46,8 @@ static bool cfg_rx; static uint64_t glob_tstart; static uint64_t tdeliver_max; +static int errors; + /* encode one timed transmission (of a 1B payload) */ struct timed_send { char data; @@ -131,13 +136,15 @@ static void do_recv_one(int fdr, struct timed_send *ts) fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n", rbuf[0], (long long)tstop, (long long)texpect); - if (rbuf[0] != ts->data) - error(1, 0, "payload mismatch. expected %c", ts->data); + if (rbuf[0] != ts->data) { + fprintf(stderr, "payload mismatch. expected %c\n", ts->data); + errors++; + } if (llabs(tstop - texpect) > cfg_variance_us) { fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us); - if (!getenv("KSFT_MACHINE_SLOW")) - exit(1); + if (!cfg_machine_slow) + errors++; } } @@ -255,8 +262,12 @@ static void start_time_wait(void) return; now = gettime_ns(CLOCK_REALTIME); - if (cfg_start_time_ns < now) + if (cfg_start_time_ns < now) { + fprintf(stderr, "FAIL: start time already passed\n"); + if (!cfg_machine_slow) + errors++; return; + } err = usleep((cfg_start_time_ns - now) / 1000); if (err) @@ -316,6 +327,9 @@ static int setup_rx(struct sockaddr *addr, socklen_t alen) if (bind(fd, addr, alen)) error(1, errno, "bind"); + if (cfg_machine_slow) + tv.tv_sec = 2; + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) error(1, errno, "setsockopt rcv timeout"); @@ -502,6 +516,8 @@ static void parse_opts(int argc, char **argv) setup_sockaddr(domain, saddr, &cfg_src_addr); cfg_num_pkt = parse_io(argv[optind], cfg_buf); + + cfg_machine_slow = getenv("KSFT_MACHINE_SLOW"); } int main(int argc, char **argv) @@ -513,5 +529,10 @@ int main(int argc, char **argv) else do_test_tx((void *)&cfg_src_addr, cfg_alen); - return 0; + if (errors) { + fprintf(stderr, "FAIL: %d errors\n", errors); + return KSFT_FAIL; + } + + return KSFT_PASS; } diff --git a/tools/testing/selftests/drivers/net/so_txtime.py b/tools/testing/selftests/drivers/net/so_txtime.py new file mode 100755 index 000000000000..adf6c848d6d8 --- /dev/null +++ b/tools/testing/selftests/drivers/net/so_txtime.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +"""Regression tests for the SO_TXTIME interface. + +Test delivery time in FQ and ETF qdiscs. +""" + +import os +import time + +from lib.py import ksft_exit, ksft_run, ksft_variants +from lib.py import KsftNamedVariant, KsftSkipEx +from lib.py import NetDrvEpEnv, bkg, cmd, defer, tc + + +def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_success): + """Main function. Run so_txtime as sender and receiver.""" + slow_machine = os.environ.get('KSFT_MACHINE_SLOW') + + if not hasattr(cfg, "bin_remote"): + cfg.bin_local = cfg.test_dir / "so_txtime" + cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) + + tstart = time.time_ns() + (2000_000_000 if slow_machine else 200_000_000) + + cmd_addr = f"-S {cfg.addr_v[ipver]} -D {cfg.remote_addr_v[ipver]}" + cmd_args = f"-{ipver} -c {clockid} -t {tstart} {cmd_addr}" + cmd_rx = f"{cfg.bin_remote} {cmd_args} {args_rx} -r" + cmd_tx = f"{cfg.bin_local} {cmd_args} {args_tx}" + + expect_fail = not expect_success + if slow_machine: + expect_success = False + + with bkg(cmd_rx, host=cfg.remote, fail=expect_success, + expect_fail=expect_fail, exit_wait=True): + cmd(cmd_tx) + + +def _qdisc_setup(ifname, qdisc, optargs=""): + """Replace root qdisc. Restore the original after the test. + + If the original is mq, children will be of type default_qdisc. + """ + orig = tc(f"qdisc show dev {ifname} root", json=True)[0].get("kind", None) + defer(tc, f"qdisc replace dev {ifname} root {orig}") + tc(f"qdisc replace dev {ifname} root {qdisc} {optargs}") + + +def _test_variants_fq(): + for ipver in ["4", "6"]: + for testcase in [ + ["no_delay", "a,-1", "a,-1"], + ["zero_delay", "a,0", "a,0"], + ["one_pkt", "a,10", "a,10"], + ["in_order", "a,10,b,20", "a,10,b,20"], + ["reverse_order", "a,20,b,10", "b,10,a,20"], + ]: + name = f"v{ipver}_{testcase[0]}" + yield KsftNamedVariant(name, ipver, testcase[1], testcase[2]) + + +@ksft_variants(_test_variants_fq()) +def test_so_txtime_fq_mono(cfg, ipver, args_tx, args_rx): + """Run all variants of monotonic (fq) tests.""" + cfg.require_ipver(ipver) + _qdisc_setup(cfg.ifname, "fq") + test_so_txtime(cfg, "mono", ipver, args_tx, args_rx, True) + + +@ksft_variants(_test_variants_fq()) +def test_so_txtime_fq_tai(cfg, ipver, args_tx, args_rx): + """Run all variants of fq tests, but pass CLOCK_TAI to test conversion.""" + cfg.require_ipver(ipver) + _qdisc_setup(cfg.ifname, "fq") + test_so_txtime(cfg, "tai", ipver, args_tx, args_rx, True) + + +def _test_variants_etf(): + for ipver in ["4", "6"]: + for testcase in [ + ["no_delay", "a,-1", "a,-1", False], + ["zero_delay", "a,0", "a,0", False], + ["one_pkt", "a,10", "a,10", True], + ["in_order", "a,10,b,20", "a,10,b,20", True], + ["reverse_order", "a,20,b,10", "b,10,a,20", True], + ]: + name = f"v{ipver}_{testcase[0]}" + yield KsftNamedVariant( + name, ipver, testcase[1], testcase[2], testcase[3] + ) + + +@ksft_variants(_test_variants_etf()) +def test_so_txtime_etf(cfg, ipver, args_tx, args_rx, expect_fail): + """Run all variants of etf tests.""" + cfg.require_ipver(ipver) + try: + _qdisc_setup(cfg.ifname, "etf", "clockid CLOCK_TAI delta 400000") + except Exception as e: + raise KsftSkipEx("tc does not support qdisc etf. skipping") from e + + test_so_txtime(cfg, "tai", ipver, args_tx, args_rx, expect_fail) + + +def main() -> None: + """Boilerplate ksft main.""" + with NetDrvEpEnv(__file__) as cfg: + ksft_run( + [test_so_txtime_fq_mono, test_so_txtime_fq_tai, test_so_txtime_etf], + args=(cfg,), + ) + ksft_exit() + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 97ad4d551d44..c9f46031ac73 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -6,6 +6,7 @@ busy_poller cmsg_sender epoll_busy_poll fin_ack_lat +getsockopt_iter hwtstamp_config icmp_rfc4884 io_uring_zerocopy_tx @@ -40,7 +41,6 @@ skf_net_off socket so_incoming_cpu so_netns_cookie -so_txtime so_rcv_listener stress_reuseport_listen tap diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 2ed7d803eb54..708d960ae07d 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -26,6 +26,7 @@ TEST_PROGS := \ cmsg_time.sh \ double_udp_encap.sh \ drop_monitor_tests.sh \ + ecmp_rehash.sh \ fcnal-ipv4.sh \ fcnal-ipv6.sh \ fcnal-other.sh \ @@ -69,6 +70,7 @@ TEST_PROGS := \ nl_netdev.py \ nl_nlctrl.py \ pmtu.sh \ + protodown.sh \ psock_snd.sh \ reuseaddr_ports_exhausted.sh \ reuseport_addr_any.sh \ @@ -83,7 +85,6 @@ TEST_PROGS := \ rxtimestamp.sh \ sctp_vrf.sh \ skf_net_off.sh \ - so_txtime.sh \ srv6_end_dt46_l3vpn_test.sh \ srv6_end_dt4_l3vpn_test.sh \ srv6_end_dt6_l3vpn_test.sh \ @@ -159,7 +160,6 @@ TEST_GEN_FILES := \ skf_net_off \ so_netns_cookie \ so_rcv_listener \ - so_txtime \ socket \ stress_reuseport_listen \ tcp_fastopen_backup_key \ @@ -178,6 +178,7 @@ TEST_GEN_PROGS := \ bind_timewait \ bind_wildcard \ epoll_busy_poll \ + getsockopt_iter \ icmp_rfc4884 \ ipv6_fragmentation \ proc_net_pktgen \ diff --git a/tools/testing/selftests/net/bind_bhash.c b/tools/testing/selftests/net/bind_bhash.c index da04b0b19b73..2bd100777448 100644 --- a/tools/testing/selftests/net/bind_bhash.c +++ b/tools/testing/selftests/net/bind_bhash.c @@ -52,18 +52,19 @@ static int bind_socket(int opt, const char *addr) err = setsockopt(sock_fd, SOL_SOCKET, opt, &reuse, sizeof(reuse)); if (err) { perror("setsockopt failed"); - goto cleanup; + goto err_free_info; } } err = bind(sock_fd, res->ai_addr, res->ai_addrlen); if (err) { perror("failed to bind to port"); - goto cleanup; + goto err_free_info; } - + freeaddrinfo(res); return sock_fd; - +err_free_info: + freeaddrinfo(res); cleanup: close(sock_fd); return err; diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config index 94d722770420..e1ce35c2abbe 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -117,11 +117,14 @@ CONFIG_OPENVSWITCH=m CONFIG_OPENVSWITCH_GENEVE=m CONFIG_OPENVSWITCH_GRE=m CONFIG_OPENVSWITCH_VXLAN=m +CONFIG_PAGE_POOL_STATS=y CONFIG_PROC_SYSCTL=y CONFIG_PSAMPLE=m CONFIG_RPS=y +CONFIG_SYN_COOKIES=y CONFIG_SYSFS=y CONFIG_TAP=m +CONFIG_TCP_CONG_DCTCP=y CONFIG_TCP_MD5SIG=y CONFIG_TEST_BLACKHOLE_DEV=m CONFIG_TEST_BPF=m diff --git a/tools/testing/selftests/net/ecmp_rehash.sh b/tools/testing/selftests/net/ecmp_rehash.sh new file mode 100755 index 000000000000..f05a6c8edd2a --- /dev/null +++ b/tools/testing/selftests/net/ecmp_rehash.sh @@ -0,0 +1,1109 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test local ECMP path re-selection on TCP retransmission timeout and PLB. +# +# Two namespaces connected by two parallel veth pairs with a 2-way ECMP +# route. When a TCP path is blocked (via tc drop) or congested (via +# netem ECN marking), the kernel rehashes the connection via +# sk_rethink_txhash() + __sk_dst_reset(), causing the next route lookup +# to select the other ECMP path. +# +# Expected runtime: ~60 seconds. Most time is spent waiting for TCP +# retransmission timeouts (1-7s per test) and running multi-round +# consistency checks (10 rounds each). The large slowwait/connect-timeout +# values (30-120s) are worst-case bounds for CI; a correctly functioning +# kernel reaches each check well before the timeout expires. + +source lib.sh + +SUBNETS=(a b) +PORT=9900 +: "${ECMP_REBUILD_ROUNDS:=10}" + +# alloc_ports NAME [COUNT]: set NAME to the next free port and reserve +# COUNT ports (default 1) from a shared counter. Each test allocates its +# own port(s) where it runs, so a retry or a newly added test never +# collides; the per-round tests reserve ECMP_REBUILD_ROUNDS each. +NEXT_PORT=$PORT +alloc_ports() +{ + printf -v "$1" '%d' "$NEXT_PORT" + NEXT_PORT=$((NEXT_PORT + ${2:-1})) +} + +ALL_TESTS=" + test_ecmp_syn_rehash + test_ecmp_synack_rehash + test_ecmp_midstream_rehash + test_ecmp_midstream_ack_rehash + test_ecmp_plb_rehash + test_ecmp_hash_policy1_no_rehash + test_ecmp_no_flowlabel_leak + test_ecmp_dst_rebuild_consistency + test_ecmp_syncookie_path_consistency +" + +link_tx_packets_get() +{ + local ns=$1; shift + local dev=$1; shift + + ip netns exec "$ns" cat "/sys/class/net/$dev/statistics/tx_packets" +} + +# Return the number of packets matched by the tc filter action on a device. +# When tc drops packets via "action drop", the device's tx_packets is not +# incremented (packet never reaches veth_xmit), but the tc action maintains +# its own counter. +tc_filter_pkt_count() +{ + local ns=$1; shift + local dev=$1; shift + + ip netns exec "$ns" tc -s filter show dev "$dev" parent 1: 2>/dev/null | + awk '/Sent .* pkt/ { + for (i=1; i<=NF; i++) + if ($i == "pkt") { print $(i-1); exit } + }' +} + +# Read a TcpExt counter from /proc/net/netstat in a namespace. +# Returns 0 if the counter is not found. +get_netstat_counter() +{ + local ns=$1; shift + local field=$1; shift + local val + + # shellcheck disable=SC2016 + val=$(ip netns exec "$ns" awk -v key="$field" ' + /^TcpExt:/ { + if (!h) { split($0, n); h=1 } + else { + split($0, v) + for (i in n) + if (n[i] == key) print v[i] + } + } + ' /proc/net/netstat) + echo "${val:-0}" +} + +# Apply netem ECN marking: CE-mark all ECT packets instead of dropping them. +mark_ecn() +{ + local ns=$1; shift + local dev=$1; shift + + ip netns exec "$ns" tc qdisc add dev "$dev" root netem loss 100% ecn +} + +# Block TCP (IPv6 next-header = 6) egress, allowing ICMPv6 through. +block_tcp() +{ + local ns=$1; shift + local dev=$1; shift + + ip netns exec "$ns" tc qdisc add dev "$dev" root handle 1: prio + ip netns exec "$ns" tc filter add dev "$dev" parent 1: \ + protocol ipv6 prio 1 u32 match u8 0x06 0xff at 6 action drop +} + +unblock_tcp() +{ + local ns=$1; shift + local dev=$1; shift + + ip netns exec "$ns" tc qdisc del dev "$dev" root 2>/dev/null +} + +# Return success when a device's TX counter exceeds a baseline value. +dev_tx_packets_above() +{ + local ns=$1; shift + local dev=$1; shift + local baseline=$1; shift + + local cur + cur=$(link_tx_packets_get "$ns" "$dev") + [ "$cur" -gt "$baseline" ] +} + +# Return success when both devices have dropped at least one TCP packet. +both_devs_attempted() +{ + local ns=$1; shift + local dev0=$1; shift + local dev1=$1; shift + + local c0 c1 + c0=$(tc_filter_pkt_count "$ns" "$dev0") + c1=$(tc_filter_pkt_count "$ns" "$dev1") + [ "${c0:-0}" -ge 1 ] && [ "${c1:-0}" -ge 1 ] +} + +link_tx_packets_total() +{ + local ns=$1; shift + local dev0=${1:-veth0a}; shift 2>/dev/null + local dev1=${1:-veth1a} + + echo $(( $(link_tx_packets_get "$ns" "$dev0") + + $(link_tx_packets_get "$ns" "$dev1") )) +} + +# (Re)install the ECMP multipath routes between NS1 and NS2. $1 is the +# ip route operation ("add" to create, "change" to replace). If $2 is +# given it names a congestion control to pin on both routes via "congctl"; +# because dctcp carries TCP_CONG_NEEDS_ECN, this also tags the route with +# DST_FEATURE_ECN_CA, which makes the server negotiate ECN without the +# listener itself having to run dctcp. The nexthop topology lives here +# only, so a test can re-pin the routes and restore them with one call. +install_ecmp_routes() +{ + local op=$1 cc=$2 + local -a cc_attr=() + + [ -n "$cc" ] && cc_attr=(congctl "$cc") + + ip -n "$NS1" -6 route "$op" fd00:ff::2/128 "${cc_attr[@]}" \ + nexthop via fd00:a::2 dev veth0a \ + nexthop via fd00:b::2 dev veth1a + + ip -n "$NS2" -6 route "$op" fd00:ff::1/128 "${cc_attr[@]}" \ + nexthop via fd00:a::1 dev veth0b \ + nexthop via fd00:b::1 dev veth1b +} + +setup() +{ + setup_ns NS1 NS2 + + local ns + for ns in "$NS1" "$NS2"; do + ip netns exec "$ns" sysctl -qw net.ipv6.conf.all.accept_dad=0 + ip netns exec "$ns" sysctl -qw net.ipv6.conf.default.accept_dad=0 + ip netns exec "$ns" sysctl -qw net.ipv6.conf.all.forwarding=1 + ip netns exec "$ns" sysctl -qw net.core.txrehash=1 + done + + local i sub + for i in 0 1; do + sub=${SUBNETS[$i]} + ip link add "veth${i}a" type veth peer name "veth${i}b" + ip link set "veth${i}a" netns "$NS1" + ip link set "veth${i}b" netns "$NS2" + ip -n "$NS1" addr add "fd00:${sub}::1/64" dev "veth${i}a" + ip -n "$NS2" addr add "fd00:${sub}::2/64" dev "veth${i}b" + ip -n "$NS1" link set "veth${i}a" up + ip -n "$NS2" link set "veth${i}b" up + done + + ip -n "$NS1" addr add fd00:ff::1/128 dev lo + ip -n "$NS2" addr add fd00:ff::2/128 dev lo + + # Allow many SYN retries at 1-second intervals (linear, no + # exponential backoff) so the rehash test has enough attempts + # to exercise both ECMP paths. + if ! ip netns exec "$NS1" sysctl -qw \ + net.ipv4.tcp_syn_linear_timeouts=25; then + echo "SKIP: tcp_syn_linear_timeouts not supported" + return "$ksft_skip" + fi + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_syn_retries=25 + + # Keep the server's request socket alive during the blocking + # period so SYN/ACK retransmits continue. + ip netns exec "$NS2" sysctl -qw net.ipv4.tcp_synack_retries=25 + + install_ecmp_routes add + + for i in 0 1; do + sub=${SUBNETS[$i]} + ip netns exec "$NS1" \ + ping -6 -c1 -W5 "fd00:${sub}::2" &>/dev/null + ip netns exec "$NS2" \ + ping -6 -c1 -W5 "fd00:${sub}::1" &>/dev/null + done + + if ! ip netns exec "$NS1" ping -6 -c1 -W5 fd00:ff::2 &>/dev/null; then + echo "Basic connectivity check failed" + return "$ksft_skip" + fi +} + +# Block ALL paths, start a connection, wait until SYNs have been dropped +# on both interfaces (proving rehash steered the SYN to a new path), then +# unblock so the connection completes. +test_ecmp_syn_rehash() +{ + RET=0 + local port + alloc_ports port + + block_tcp "$NS1" veth0a + defer unblock_tcp "$NS1" veth0a + block_tcp "$NS1" veth1a + defer unblock_tcp "$NS1" veth1a + + ip netns exec "$NS2" socat \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr,fork" \ + EXEC:"echo ESTABLISH_OK" & + defer kill_process $! + + wait_local_port_listen "$NS2" "$port" tcp + + local rehash_before + rehash_before=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + + # Start the connection in the background; it will retry SYNs at + # 1-second intervals until an unblocked path is found. + # Use -u (unidirectional) to only receive from the server; + # sending data back would risk SIGPIPE if the server's EXEC + # child has already exited. + local tmpfile + tmpfile=$(mktemp) + defer rm -f "$tmpfile" + + ip netns exec "$NS1" socat -u \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1],connect-timeout=60" \ + STDOUT >"$tmpfile" 2>&1 & + local client_pid=$! + defer kill_process "$client_pid" + + # Wait until both paths have seen at least one dropped SYN. + # This proves sk_rethink_txhash() rehashed the connection from + # one ECMP path to the other. + slowwait 30 both_devs_attempted "$NS1" veth0a veth1a > /dev/null + check_err $? "SYNs did not appear on both paths (rehash not working)" + if [ "$RET" -ne 0 ]; then + log_test "Local ECMP SYN rehash: establish with blocked paths" + return + fi + + # Unblock both paths and let the next SYN retransmit succeed. + unblock_tcp "$NS1" veth0a + unblock_tcp "$NS1" veth1a + + local rc=0 + wait "$client_pid" || rc=$? + + local result + result=$(cat "$tmpfile" 2>/dev/null) + + if [[ "$result" != *"ESTABLISH_OK"* ]]; then + check_err 1 "connection failed after unblocking (rc=$rc): $result" + fi + + local rehash_after + rehash_after=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + if [ "$rehash_after" -le "$rehash_before" ]; then + check_err 1 "TcpTimeoutRehash counter did not increment" + fi + + log_test "Local ECMP SYN rehash: establish with blocked paths" +} + +# Block the server's return paths so SYN/ACKs are dropped. The client +# retransmits SYNs at 1-second intervals; each duplicate SYN arriving at +# the server triggers tcp_rtx_synack() which re-rolls txhash, so the +# retransmitted SYN/ACK selects a different ECMP return path. +test_ecmp_synack_rehash() +{ + RET=0 + local port + alloc_ports port + + block_tcp "$NS2" veth0b + defer unblock_tcp "$NS2" veth0b + block_tcp "$NS2" veth1b + defer unblock_tcp "$NS2" veth1b + + ip netns exec "$NS2" socat \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr,fork" \ + EXEC:"echo SYNACK_OK" & + defer kill_process $! + + wait_local_port_listen "$NS2" "$port" tcp + + # Start the connection; SYNs reach the server (client egress is + # open) but SYN/ACKs are dropped on the server's return path. + local tmpfile + tmpfile=$(mktemp) + defer rm -f "$tmpfile" + + ip netns exec "$NS1" socat -u \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1],connect-timeout=60" \ + STDOUT >"$tmpfile" 2>&1 & + local client_pid=$! + defer kill_process "$client_pid" + + # Wait until both server-side interfaces have dropped at least + # one SYN/ACK, proving the server rehashed its return path. + slowwait 30 both_devs_attempted "$NS2" veth0b veth1b > /dev/null + check_err $? "SYN/ACKs did not appear on both return paths" + if [ "$RET" -ne 0 ]; then + log_test "Local ECMP SYN/ACK rehash: blocked return path" + return + fi + + # Unblock and let the connection complete. + unblock_tcp "$NS2" veth0b + unblock_tcp "$NS2" veth1b + + local rc=0 + wait "$client_pid" || rc=$? + + local result + result=$(cat "$tmpfile" 2>/dev/null) + + if [[ "$result" != *"SYNACK_OK"* ]]; then + check_err 1 "connection failed after unblocking (rc=$rc): $result" + fi + + log_test "Local ECMP SYN/ACK rehash: blocked return path" +} + +# Establish a data transfer with both paths open, then block the +# active path. Verify that data appears on the previously inactive +# path (proving RTO triggered a rehash) and that TcpTimeoutRehash +# incremented. +# +# With 2-way ECMP each rehash may pick the same path, so a single +# attempt can occasionally fail. Retry once for robustness. + +# Single attempt at the midstream rehash check. Returns 0 on success. +ecmp_midstream_rehash_attempt() +{ + local port=$1; shift + local reason="" + + ip netns exec "$NS2" socat -u \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" - >/dev/null & + local server_pid=$! + + wait_local_port_listen "$NS2" "$port" tcp + + local base_tx0 base_tx1 + base_tx0=$(link_tx_packets_get "$NS1" veth0a) + base_tx1=$(link_tx_packets_get "$NS1" veth1a) + + # Continuous data source; timeout caps overall test duration and + # must exceed the slowwait below so data keeps flowing. + ip netns exec "$NS1" timeout 90 socat -u \ + OPEN:/dev/zero \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1]" &>/dev/null & + local client_pid=$! + + # Wait for enough packets to identify the active path. + if ! busywait "$BUSYWAIT_TIMEOUT" until_counter_is \ + ">= $((base_tx0 + base_tx1 + 10))" \ + link_tx_packets_total "$NS1" > /dev/null; then + kill "$client_pid" "$server_pid" 2>/dev/null + wait "$client_pid" "$server_pid" 2>/dev/null + echo "no TX activity" + return 1 + fi + + # Find the active path and block it. + local current_tx0 current_tx1 active_idx inactive_idx + current_tx0=$(link_tx_packets_get "$NS1" veth0a) + current_tx1=$(link_tx_packets_get "$NS1" veth1a) + if [ $((current_tx0 - base_tx0)) -ge $((current_tx1 - base_tx1)) ]; then + active_idx=0; inactive_idx=1 + else + active_idx=1; inactive_idx=0 + fi + + local rehash_before + rehash_before=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + # Suppress __dst_negative_advice() in tcp_write_timeout() so + # that __sk_dst_reset() is the only dst-invalidation mechanism + # on the RTO path. + local saved_retries1 + saved_retries1=$(ip netns exec "$NS1" sysctl -n net.ipv4.tcp_retries1) + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_retries1=255 + + block_tcp "$NS1" "veth${active_idx}a" + + # Capture baseline after block_tcp returns. block_tcp adds a + # prio qdisc then a tc filter; between those two steps the + # qdisc's CAN_BYPASS fast-path lets packets through unfiltered. + local inactive_before + inactive_before=$(link_tx_packets_get "$NS1" "veth${inactive_idx}a") + + # Wait for meaningful data on the previously inactive path, + # proving RTO triggered a rehash and data actually moved. + if ! slowwait 60 dev_tx_packets_above \ + "$NS1" "veth${inactive_idx}a" "$((inactive_before + 100))" \ + > /dev/null; then + reason="no data on alternate path" + fi + + local rehash_after + rehash_after=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + if [ "$rehash_after" -le "$rehash_before" ]; then + reason="${reason:+$reason; }TcpTimeoutRehash did not increment" + fi + + unblock_tcp "$NS1" "veth${active_idx}a" + ip netns exec "$NS1" sysctl -qw \ + net.ipv4.tcp_retries1="$saved_retries1" + kill "$client_pid" "$server_pid" 2>/dev/null + wait "$client_pid" "$server_pid" 2>/dev/null + if [ -n "$reason" ]; then + echo "$reason" + return 1 + fi + return 0 +} + +test_ecmp_midstream_rehash() +{ + RET=0 + local port retry_port + alloc_ports port + alloc_ports retry_port + + local fail_reason + if ! ecmp_midstream_rehash_attempt "$port" >/dev/null; then + fail_reason=$(ecmp_midstream_rehash_attempt "$retry_port") + check_err $? "$fail_reason" + fi + + log_test "Local ECMP midstream rehash: block active path" +} + +# Single attempt at the ACK rehash check. Returns 0 on success. +ecmp_ack_rehash_attempt() +{ + local port=$1; shift + local reason="" + + ip netns exec "$NS2" socat -u \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" - >/dev/null & + local server_pid=$! + + wait_local_port_listen "$NS2" "$port" tcp + + local base_tx0 base_tx1 + base_tx0=$(link_tx_packets_get "$NS2" veth0b) + base_tx1=$(link_tx_packets_get "$NS2" veth1b) + + # Continuous data source from NS1 to NS2. Cap the send buffer + # so in-flight data stays below the receiver's advertised window. + # Without this, the sender can exhaust the receiver's window and + # enter persist mode (zero-window probing) instead of RTO when + # ACKs are blocked, and persist probes do not trigger flowlabel + # rehash. + ip netns exec "$NS1" timeout 120 socat -u \ + OPEN:/dev/zero \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1],sndbuf=16384" \ + &>/dev/null & + local client_pid=$! + + # Wait for enough server TX (ACKs) to identify the active return path. + if ! busywait "$BUSYWAIT_TIMEOUT" until_counter_is \ + ">= $((base_tx0 + base_tx1 + 10))" \ + link_tx_packets_total "$NS2" veth0b veth1b > /dev/null; then + kill "$client_pid" "$server_pid" 2>/dev/null + wait "$client_pid" "$server_pid" 2>/dev/null + echo "no server TX activity" + return 1 + fi + + local cur_tx0 cur_tx1 active_dev inactive_dev + cur_tx0=$(link_tx_packets_get "$NS2" veth0b) + cur_tx1=$(link_tx_packets_get "$NS2" veth1b) + if [ $((cur_tx0 - base_tx0)) -ge $((cur_tx1 - base_tx1)) ]; then + active_dev=veth0b; inactive_dev=veth1b + else + active_dev=veth1b; inactive_dev=veth0b + fi + + local rehash_before + rehash_before=$(get_netstat_counter "$NS2" TcpDuplicateDataRehash) + + # Block the inactive return path first (no effect on current + # ACK flow), then block the active path. This avoids counting + # normal ACK drops as rehash evidence. + block_tcp "$NS2" "$inactive_dev" + local inactive_before + inactive_before=$(tc_filter_pkt_count "$NS2" "$inactive_dev") + block_tcp "$NS2" "$active_dev" + + # NS1 will RTO (no ACKs), retransmit with new flowlabel. + # NS2 detects the flowlabel change via tcp_rcv_spurious_retrans(), + # rehashes, and NS2's ACKs try the previously inactive return + # path. One successful rehash is sufficient. + if ! slowwait 60 until_counter_is \ + ">= $((${inactive_before:-0} + 1))" \ + tc_filter_pkt_count "$NS2" "$inactive_dev" > /dev/null; then + reason="no ACKs on alternate return path after blocking" + fi + + local rehash_after + rehash_after=$(get_netstat_counter "$NS2" TcpDuplicateDataRehash) + if [ "$rehash_after" -le "$rehash_before" ]; then + reason="${reason:+$reason; }TcpDuplicateDataRehash did not increment" + fi + + unblock_tcp "$NS2" "$active_dev" + unblock_tcp "$NS2" "$inactive_dev" + kill "$client_pid" "$server_pid" 2>/dev/null + wait "$client_pid" "$server_pid" 2>/dev/null + if [ -n "$reason" ]; then + echo "$reason" + return 1 + fi + return 0 +} + +# Block the receiver's (NS2) ACK return paths while data flows from +# NS1 to NS2. The sender (NS1) times out and retransmits with a new +# flowlabel; the receiver detects the changed flowlabel via +# tcp_rcv_spurious_retrans() and rehashes its own txhash so that its +# ACKs try a different ECMP return path. +# +# With 2-way ECMP each rehash may pick the same path, so a single +# attempt can occasionally fail. Retry once for robustness. +test_ecmp_midstream_ack_rehash() +{ + RET=0 + local port retry_port + alloc_ports port + alloc_ports retry_port + + local fail_reason + if ! ecmp_ack_rehash_attempt "$port" >/dev/null; then + fail_reason=$(ecmp_ack_rehash_attempt "$retry_port") + check_err $? "$fail_reason" + fi + + log_test "Local ECMP midstream ACK rehash: blocked return path" +} + +# Establish a DCTCP data transfer with PLB enabled, then ECN-mark both +# paths. Sustained CE marking triggers PLB to call sk_rethink_txhash() +# + __sk_dst_reset(), bouncing the connection between ECMP paths. +# Verify data appears on both paths and that TCPPLBRehash incremented. +test_ecmp_plb_rehash() +{ + RET=0 + local port + alloc_ports port + + # PLB needs DCTCP, a restricted congestion control. Adding it to + # the host-global tcp_allowed_congestion_control would relax the + # restricted-CC policy for the whole host (there is no per-netns + # allowed set). Instead pin dctcp on the test routes with + # "congctl": the route's RTAX_CC_ALGO is honoured on both the + # connect and accept paths without the restricted-CC check, and a + # dctcp route also carries DST_FEATURE_ECN_CA so the server + # negotiates ECN -- all confined to the test namespaces. + local available + available=$(ip netns exec "$NS1" sysctl -n \ + net.ipv4.tcp_available_congestion_control) + if ! echo "$available" | grep -qw dctcp; then + log_test_skip "Local ECMP PLB rehash: DCTCP not available" + return "$ksft_skip" + fi + install_ecmp_routes change dctcp + defer install_ecmp_routes change + + # Save NS1 sysctls before modifying them. + local saved_ecn1 saved_plb_enabled saved_plb_rounds + local saved_plb_thresh saved_plb_suspend + saved_ecn1=$(ip netns exec "$NS1" sysctl -n net.ipv4.tcp_ecn) + saved_plb_enabled=$(ip netns exec "$NS1" sysctl -n net.ipv4.tcp_plb_enabled) + saved_plb_rounds=$(ip netns exec "$NS1" sysctl -n net.ipv4.tcp_plb_rehash_rounds) + saved_plb_thresh=$(ip netns exec "$NS1" sysctl -n net.ipv4.tcp_plb_cong_thresh) + saved_plb_suspend=$(ip netns exec "$NS1" sysctl -n net.ipv4.tcp_plb_suspend_rto_sec) + + # Enable ECN and PLB on the sender; dctcp comes from the route. + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_ecn=1 + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_enabled=1 + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_rehash_rounds=3 + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_cong_thresh=1 + ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_suspend_rto_sec=0 + defer ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_ecn="$saved_ecn1" + defer ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_enabled="$saved_plb_enabled" + defer ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_rehash_rounds="$saved_plb_rounds" + defer ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_cong_thresh="$saved_plb_thresh" + defer ip netns exec "$NS1" sysctl -qw net.ipv4.tcp_plb_suspend_rto_sec="$saved_plb_suspend" + + ip netns exec "$NS2" socat -u \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" - >/dev/null & + defer kill_process $! + + wait_local_port_listen "$NS2" "$port" tcp + + local base_tx0 base_tx1 + base_tx0=$(link_tx_packets_get "$NS1" veth0a) + base_tx1=$(link_tx_packets_get "$NS1" veth1a) + + ip netns exec "$NS1" timeout 90 socat -u \ + OPEN:/dev/zero \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1]" &>/dev/null & + local client_pid=$! + defer kill_process "$client_pid" + + # Wait for data to start flowing before applying ECN marking. + busywait "$BUSYWAIT_TIMEOUT" until_counter_is \ + ">= $((base_tx0 + base_tx1 + 10))" \ + link_tx_packets_total "$NS1" > /dev/null + check_err $? "no TX activity detected" + if [ "$RET" -ne 0 ]; then + log_test "Local ECMP PLB rehash: ECN-marked path" + return + fi + + # Snapshot TX counters and rehash stats before ECN marking. + local pre_ecn_tx0 pre_ecn_tx1 + pre_ecn_tx0=$(link_tx_packets_get "$NS1" veth0a) + pre_ecn_tx1=$(link_tx_packets_get "$NS1" veth1a) + + local plb_before rto_before + plb_before=$(get_netstat_counter "$NS1" TCPPLBRehash) + rto_before=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + + # CE-mark all data on both paths. PLB detects sustained + # congestion and rehashes, bouncing traffic between paths. + mark_ecn "$NS1" veth0a + defer unblock_tcp "$NS1" veth0a # removes the marking rule + mark_ecn "$NS1" veth1a + defer unblock_tcp "$NS1" veth1a # removes the marking rule + + # Wait for meaningful data on both paths, proving PLB rehashed + # the connection and traffic actually moved. Require at least + # 100 packets beyond the baseline to rule out stray control + # packets (ND, etc.) satisfying the check. + slowwait 60 dev_tx_packets_above \ + "$NS1" veth0a "$((pre_ecn_tx0 + 100))" > /dev/null + check_err $? "no data on veth0a after ECN marking" + + slowwait 60 dev_tx_packets_above \ + "$NS1" veth1a "$((pre_ecn_tx1 + 100))" > /dev/null + check_err $? "no data on veth1a after ECN marking" + + local plb_after rto_after + plb_after=$(get_netstat_counter "$NS1" TCPPLBRehash) + rto_after=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + if [ "$plb_after" -le "$plb_before" ]; then + check_err 1 "TCPPLBRehash counter did not increment" + fi + if [ "$rto_after" -gt "$rto_before" ]; then + check_err 1 "TcpTimeoutRehash incremented; rehash was RTO-driven, not PLB" + fi + + log_test "Local ECMP PLB rehash: ECN-marked path" +} + +# Verify that hash policy 1 (L3+L4 symmetric) preserves the ECMP path +# across rehash. Policy 1 computes a deterministic hash from the +# 5-tuple, so mp_hash stays 0 and rt6_multipath_hash() always selects +# the same path regardless of txhash changes. +test_ecmp_hash_policy1_no_rehash() +{ + RET=0 + local port + alloc_ports port + + local saved_policy + saved_policy=$(ip netns exec "$NS1" sysctl -n \ + net.ipv6.fib_multipath_hash_policy) + ip netns exec "$NS1" sysctl -qw net.ipv6.fib_multipath_hash_policy=1 + defer ip netns exec "$NS1" sysctl -qw \ + net.ipv6.fib_multipath_hash_policy="$saved_policy" + + block_tcp "$NS1" veth0a + defer unblock_tcp "$NS1" veth0a + block_tcp "$NS1" veth1a + defer unblock_tcp "$NS1" veth1a + + ip netns exec "$NS2" socat \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr,fork" \ + EXEC:"echo POLICY1_OK" & + defer kill_process $! + + wait_local_port_listen "$NS2" "$port" tcp + + local rehash_before + rehash_before=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + + ip netns exec "$NS1" timeout 10 socat -u \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1],connect-timeout=8" \ + STDOUT >/dev/null 2>&1 & + local client_pid=$! + defer kill_process "$client_pid" + + # With policy 1, the deterministic 5-tuple hash always selects + # the same path. Wait for multiple SYN retransmits (proving + # rehash was attempted), then verify all SYNs landed on the + # same interface. + local rehash_after + slowwait 8 until_counter_is ">= $((rehash_before + 3))" \ + get_netstat_counter "$NS1" TcpTimeoutRehash > /dev/null + rehash_after=$(get_netstat_counter "$NS1" TcpTimeoutRehash) + if [ "$rehash_after" -le "$rehash_before" ]; then + check_err 1 "TcpTimeoutRehash counter did not increment" + fi + + local c0 c1 + c0=$(tc_filter_pkt_count "$NS1" veth0a) + c1=$(tc_filter_pkt_count "$NS1" veth1a) + if [ "${c0:-0}" -ge 1 ] && [ "${c1:-0}" -ge 1 ]; then + check_err 1 "SYNs appeared on both paths despite policy 1" + fi + if [ "${c0:-0}" -eq 0 ] && [ "${c1:-0}" -eq 0 ]; then + check_err 1 "no SYNs observed on either path" + fi + + log_test "Local ECMP policy 1: no path change on rehash" +} + +# Verify that mp_hash does not leak into the on-wire flowlabel. +# With auto_flowlabels=0, the wire flowlabel must be 0. Install tc +# filters that pass TCP with flowlabel=0 but drop TCP with nonzero +# flowlabel, then establish a connection and transfer data. If +# mp_hash leaked into fl6->flowlabel, the SYN or data packets would +# be dropped and the connection would fail. +test_ecmp_no_flowlabel_leak() +{ + RET=0 + local port + alloc_ports port + + local saved_afl + saved_afl=$(ip netns exec "$NS1" sysctl -n \ + net.ipv6.auto_flowlabels) + ip netns exec "$NS1" sysctl -qw net.ipv6.auto_flowlabels=0 + defer ip netns exec "$NS1" sysctl -qw \ + net.ipv6.auto_flowlabels="$saved_afl" + + # On both egress interfaces: pass TCP with flowlabel=0 (prio 1), + # drop any remaining TCP (nonzero flowlabel, prio 2). ICMPv6 + # matches neither filter and passes through normally. + local dev + for dev in veth0a veth1a; do + ip netns exec "$NS1" tc qdisc add dev "$dev" \ + root handle 1: prio + ip netns exec "$NS1" tc filter add dev "$dev" parent 1: \ + protocol ipv6 prio 1 u32 \ + match u32 0x00000000 0x000FFFFF at 0 \ + match u8 0x06 0xff at 6 \ + action ok + ip netns exec "$NS1" tc filter add dev "$dev" parent 1: \ + protocol ipv6 prio 2 u32 \ + match u8 0x06 0xff at 6 \ + action drop + defer unblock_tcp "$NS1" "$dev" + done + + ip netns exec "$NS2" socat \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" \ + EXEC:"echo FLOWLABEL_OK" & + defer kill_process $! + + wait_local_port_listen "$NS2" "$port" tcp + + local tmpfile + tmpfile=$(mktemp) + defer rm -f "$tmpfile" + + ip netns exec "$NS1" socat -u \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1],connect-timeout=10" \ + STDOUT >"$tmpfile" 2>&1 + + local result + result=$(cat "$tmpfile" 2>/dev/null) + if [[ "$result" != *"FLOWLABEL_OK"* ]]; then + check_err 1 "connection failed: mp_hash may have leaked into wire flowlabel" + fi + + log_test "No flowlabel leak with auto_flowlabels=0" +} + +# Helper: stream data, invalidate the cached dst by adding and +# removing a dummy route (bumps fib6_node sernum), then check that +# traffic stays on the same ECMP path. Used by both the normal +# tcp_v6_connect and syncookie variants. +ecmp_dst_rebuild_check() +{ + local ns_client=$1; shift + local port=$1; shift + local rc=0 + + # Suppress __dst_negative_advice() during the test so that a + # real TCP timeout cannot trigger an additional dst + # invalidation via a different code path. + local saved_retries1 + saved_retries1=$(ip netns exec "$ns_client" sysctl -n \ + net.ipv4.tcp_retries1) + ip netns exec "$ns_client" sysctl -qw net.ipv4.tcp_retries1=255 + + local base0 base1 + base0=$(link_tx_packets_get "$ns_client" veth0a) + base1=$(link_tx_packets_get "$ns_client" veth1a) + + ip netns exec "$ns_client" timeout 15 socat -u \ + OPEN:/dev/zero \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1]" \ + &>/dev/null & + local client_pid=$! + + # Wait for enough packets to identify the active path. + # Return 2 for setup failure (distinct from 1 = path changed). + if ! busywait "$BUSYWAIT_TIMEOUT" until_counter_is \ + ">= $((base0 + base1 + 50))" \ + link_tx_packets_total "$ns_client" > /dev/null; then + ip netns exec "$ns_client" sysctl -qw \ + net.ipv4.tcp_retries1="$saved_retries1" + kill "$client_pid" 2>/dev/null + wait "$client_pid" 2>/dev/null + return 2 + fi + + local mid0 mid1 active_dev inactive_dev + mid0=$(link_tx_packets_get "$ns_client" veth0a) + mid1=$(link_tx_packets_get "$ns_client" veth1a) + if [ $((mid0 - base0)) -ge $((mid1 - base1)) ]; then + active_dev=veth0a; inactive_dev=veth1a + else + active_dev=veth1a; inactive_dev=veth0a + fi + + local active_before inactive_before + active_before=$(link_tx_packets_get "$ns_client" "$active_dev") + inactive_before=$(link_tx_packets_get "$ns_client" "$inactive_dev") + + # Invalidate the cached dst by bumping the fib6_node sernum. + # Adding and removing a high-metric dummy route achieves this + # without touching the ECMP nexthops, avoiding a transient + # single-nexthop state during multipath route replace. + ip -n "$ns_client" -6 route add fd00:ff::2/128 dev lo metric 9999 + ip -n "$ns_client" -6 route del fd00:ff::2/128 dev lo metric 9999 + + # Wait for enough post-rebuild traffic to detect a path change. + if ! busywait "$BUSYWAIT_TIMEOUT" until_counter_is \ + ">= $((active_before + inactive_before + 50))" \ + link_tx_packets_total "$ns_client" > /dev/null; then + ip netns exec "$ns_client" sysctl -qw \ + net.ipv4.tcp_retries1="$saved_retries1" + kill "$client_pid" 2>/dev/null + wait "$client_pid" 2>/dev/null + return 2 + fi + + local active_after inactive_after + active_after=$(link_tx_packets_get "$ns_client" "$active_dev") + inactive_after=$(link_tx_packets_get "$ns_client" "$inactive_dev") + + local active_delta=$((active_after - active_before)) + local inactive_delta=$((inactive_after - inactive_before)) + + if [ "$inactive_delta" -gt "$active_delta" ]; then + rc=1 + fi + + ip netns exec "$ns_client" sysctl -qw \ + net.ipv4.tcp_retries1="$saved_retries1" + kill "$client_pid" 2>/dev/null + wait "$client_pid" 2>/dev/null + return "$rc" +} + +# Run ecmp_dst_rebuild_check for ECMP_REBUILD_ROUNDS rounds, each with +# a fresh server and connection. With a correct kernel the path is +# deterministic (same txhash always selects the same ECMP nexthop), +# so any path change is a bug. Multiple rounds catch a buggy kernel +# that picks a random path: each round has 50% chance of accidentally +# matching, so 10 rounds gives < 0.1% false-pass probability. +ecmp_dst_rebuild_loop() +{ + local base_port=$1; shift + local label=$1; shift + local path_changes=0 + local r + + for r in $(seq 1 "$ECMP_REBUILD_ROUNDS"); do + local port=$((base_port + r - 1)) + + ip netns exec "$NS2" socat -u \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" \ + - >/dev/null & + local server_pid=$! + + wait_local_port_listen "$NS2" "$port" tcp + + local check_rc=0 + ecmp_dst_rebuild_check "$NS1" "$port" || check_rc=$? + + kill "$server_pid" 2>/dev/null + wait "$server_pid" 2>/dev/null + + busywait "$BUSYWAIT_TIMEOUT" \ + port_has_no_active_tcp "$NS1" "$port" > /dev/null + busywait "$BUSYWAIT_TIMEOUT" \ + port_has_no_active_tcp "$NS2" "$port" > /dev/null + + if [ "$check_rc" -eq 2 ]; then + check_err 1 "no TX activity in round $r" + break + elif [ "$check_rc" -eq 1 ]; then + path_changes=$((path_changes + 1)) + fi + done + + if [ "$path_changes" -gt 0 ]; then + check_err 1 "$path_changes/$ECMP_REBUILD_ROUNDS changed path" + fi + + log_test "$label" +} + +# Verify that a dst invalidation does not cause the connection to +# switch ECMP paths. With the fix, both the initial route lookup +# (tcp_v6_connect) and subsequent rebuilds (inet6_csk_route_socket) +# use sk_txhash >> 1, so the path is stable. +test_ecmp_dst_rebuild_consistency() +{ + RET=0 + local base_port + alloc_ports base_port "$ECMP_REBUILD_ROUNDS" + + ecmp_dst_rebuild_loop "$base_port" \ + "ECMP path stable after dst invalidation" +} + +# Return 0 (true) when no active TCP sockets remain on a port. +# TIME_WAIT is excluded because it does not generate outgoing traffic. +port_has_no_active_tcp() +{ + local ns=$1; shift + local port=$1; shift + + ! ip netns exec "$ns" ss -tnH \ + state established \ + state fin-wait-1 \ + state fin-wait-2 \ + state close-wait \ + state last-ack \ + state closing \ + state syn-sent \ + state syn-recv \ + "sport = :$port or dport = :$port" | grep -q . +} + +# Count TCP packets on server egress without blocking them. +# Uses tc filters with "action ok" so packets are counted and passed. +count_tcp() +{ + local ns=$1; shift + local dev=$1; shift + + ip netns exec "$ns" tc qdisc add dev "$dev" root handle 1: prio + ip netns exec "$ns" tc filter add dev "$dev" parent 1: \ + protocol ipv6 prio 1 u32 match u8 0x06 0xff at 6 action ok +} + +# Verify that the server's SYN-ACK (sent from the request socket) and +# subsequent ACKs (sent from the full socket created in cookie_v6_check) +# use the same ECMP path. With syncookies the request socket is freed +# after the SYN-ACK and a new one is created during cookie validation; +# this test catches the case where the two request sockets pick +# different ECMP paths due to independent txhash values. +test_ecmp_syncookie_path_consistency() +{ + RET=0 + + local saved_syncookies + saved_syncookies=$(ip netns exec "$NS2" sysctl -n \ + net.ipv4.tcp_syncookies 2>/dev/null) + if [ -z "$saved_syncookies" ]; then + log_test_skip "Syncookie server ECMP path consistent" + return "$ksft_skip" + fi + ip netns exec "$NS2" sysctl -qw net.ipv4.tcp_syncookies=2 + defer ip netns exec "$NS2" sysctl -qw \ + net.ipv4.tcp_syncookies="$saved_syncookies" + + count_tcp "$NS2" veth0b + defer unblock_tcp "$NS2" veth0b + count_tcp "$NS2" veth1b + defer unblock_tcp "$NS2" veth1b + + local path_splits=0 + local r base_port + alloc_ports base_port "$ECMP_REBUILD_ROUNDS" + + for r in $(seq 1 "$ECMP_REBUILD_ROUNDS"); do + local port=$((base_port + r - 1)) + + ip netns exec "$NS2" socat -u \ + "TCP6-LISTEN:$port,bind=[fd00:ff::2],reuseaddr" \ + - >/dev/null & + local server_pid=$! + + wait_local_port_listen "$NS2" "$port" tcp + + local srv_base0 srv_base1 + srv_base0=$(tc_filter_pkt_count "$NS2" veth0b) + srv_base1=$(tc_filter_pkt_count "$NS2" veth1b) + + ip netns exec "$NS1" timeout 5 socat -u \ + OPEN:/dev/zero \ + "TCP6:[fd00:ff::2]:$port,bind=[fd00:ff::1]" \ + &>/dev/null & + local client_pid=$! + + local cli_base + cli_base=$(link_tx_packets_total "$NS1") + if ! busywait "$BUSYWAIT_TIMEOUT" until_counter_is \ + ">= $((cli_base + 200))" \ + link_tx_packets_total "$NS1" > /dev/null; then + check_err 1 "no TX activity in round $r" + kill "$client_pid" 2>/dev/null + wait "$client_pid" 2>/dev/null + kill "$server_pid" 2>/dev/null + wait "$server_pid" 2>/dev/null + break + fi + + local srv_tcp0 srv_tcp1 + srv_tcp0=$(tc_filter_pkt_count "$NS2" veth0b) + srv_tcp1=$(tc_filter_pkt_count "$NS2" veth1b) + local srv_delta0=$(( ${srv_tcp0:-0} - ${srv_base0:-0} )) + local srv_delta1=$(( ${srv_tcp1:-0} - ${srv_base1:-0} )) + + if [ "$srv_delta0" -gt 0 ] && [ "$srv_delta1" -gt 0 ]; then + path_splits=$((path_splits + 1)) + fi + + kill "$client_pid" 2>/dev/null + wait "$client_pid" 2>/dev/null + kill "$server_pid" 2>/dev/null + wait "$server_pid" 2>/dev/null + + # Wait for TCP teardown packets (FIN/RST) to finish so + # they do not pollute the next round's tc filter counters. + busywait "$BUSYWAIT_TIMEOUT" \ + port_has_no_active_tcp "$NS1" "$port" > /dev/null + busywait "$BUSYWAIT_TIMEOUT" \ + port_has_no_active_tcp "$NS2" "$port" > /dev/null + done + + if [ "$path_splits" -gt 0 ]; then + check_err 1 "$path_splits/$ECMP_REBUILD_ROUNDS had split server path" + fi + + log_test "Syncookie server ECMP path consistent" +} + +require_command socat + +trap 'defer_scopes_cleanup; cleanup_all_ns' EXIT +setup || exit $? +tests_run +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh index af64f93bb2e1..b338bfb196a2 100755 --- a/tools/testing/selftests/net/fib_tests.sh +++ b/tools/testing/selftests/net/fib_tests.sh @@ -12,7 +12,9 @@ TESTS="unregister down carrier nexthop suppress ipv6_notify ipv4_notify \ ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr \ ipv6_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh fib6_gc_test \ ipv4_mpath_list ipv6_mpath_list ipv4_mpath_balance ipv6_mpath_balance \ - ipv4_mpath_balance_preferred fib6_ra_to_static" + ipv4_mpath_balance_preferred ipv4_mpath_oif ipv4_mpath_oif_nh \ + ipv4_mpath_oif_vrf ipv6_mpath_oif ipv6_mpath_oif_nh ipv6_mpath_oif_vrf \ + fib6_ra_to_static fib6_temp_addr_renewal" VERBOSE=0 PAUSE_ON_FAIL=no @@ -1611,6 +1613,62 @@ fib6_ra_to_static() cleanup &> /dev/null } +fib6_temp_addr_renewal() { + setup + + echo + echo "Fib6 temporary address renewal test" + set -e + + # ra6 is required for the test. (ipv6toolkit) + if [ ! -x "$(command -v ra6)" ]; then + echo "SKIP: ra6 not found." + set +e + cleanup &> /dev/null + return + fi + + # Create a pair of veth devices to send a RA message from one + # device to another. + $IP link add veth1 type veth peer name veth2 + $IP link set dev veth1 up + $IP link set dev veth2 up + + # Make veth1 ready to receive RA messages. + $NS_EXEC sysctl -wq net.ipv6.conf.veth1.accept_ra=2 + $NS_EXEC sysctl -wq net.ipv6.conf.veth1.use_tempaddr=2 + $NS_EXEC sysctl -wq net.ipv6.conf.veth1.temp_prefered_lft=15 + $NS_EXEC sysctl -wq net.ipv6.conf.veth1.max_desync_factor=0 + + # Send a RA message with a prefix from veth2. + $NS_EXEC ra6 -i veth2 -s fe80::1 -d ff02::1 -P 2001:12::/64\#LA\#3600\#3600 -e + sleep 3 + + # Deprecate it + $NS_EXEC ra6 -i veth2 -s fe80::1 -d ff02::1 -P 2001:12::/64\#LA\#3600\#0 -e + sleep 3 + + # Restore it + $NS_EXEC ra6 -i veth2 -s fe80::1 -d ff02::1 -P 2001:12::/64\#LA\#3600\#3600 -e + + ret=1 + for i in $(seq 1 25); do + sleep 1 + num_dep="$($IP -6 addr | grep -c "temporary deprecated" || true)" + num_tot="$($IP -6 addr | grep -c "temporary" || true)" + + if [ "$num_dep" -eq 1 ] && [ "$num_tot" -ge 2 ]; then + ret=0 + break + fi + done + log_test "$ret" 0 "IPv6 temporary address cleanly deprecated and regenerated" + + set +e + + cleanup &> /dev/null +} + # add route for a prefix, flushing any existing routes first # expected to be the first step of a test add_route() @@ -2915,6 +2973,247 @@ ipv6_mpath_balance_test() forwarding_cleanup } +ipv4_mpath_oif_test_common() +{ + local get_param=$1; shift + local expected_oif=$1; shift + local test_name=$1; shift + local tmp_file + + tmp_file=$(mktemp) + + for i in {1..100}; do + $IP route get 203.0.113.${i} $get_param >> "$tmp_file" + done + + [[ $(grep "$expected_oif" "$tmp_file" | wc -l) -eq 100 ]] + log_test $? 0 "$test_name" + + rm "$tmp_file" +} + +ipv4_mpath_oif_test() +{ + echo + echo "IPv4 multipath oif test" + + setup + + set -e + $IP link add dummy1 up type dummy + $IP address add 192.0.2.1/28 dev dummy1 + $IP address add 192.0.2.17/32 dev lo + + $IP route add 203.0.113.0/24 \ + nexthop via 198.51.100.2 dev dummy0 \ + nexthop via 192.0.2.2 dev dummy1 + set +e + + ipv4_mpath_oif_test_common "oif dummy0" "dummy0" \ + "IPv4 multipath via first nexthop" + + ipv4_mpath_oif_test_common "oif dummy1" "dummy1" \ + "IPv4 multipath via second nexthop" + + ipv4_mpath_oif_test_common "oif dummy0 from 192.0.2.17" "dummy0" \ + "IPv4 multipath via first nexthop with source address" + + ipv4_mpath_oif_test_common "oif dummy1 from 192.0.2.17" "dummy1" \ + "IPv4 multipath via second nexthop with source address" + + cleanup +} + +ipv4_mpath_oif_nh_test() +{ + echo + echo "IPv4 multipath oif with nexthop object test" + + setup + + set -e + $IP link add dummy1 up type dummy + $IP address add 192.0.2.1/28 dev dummy1 + $IP address add 192.0.2.17/32 dev lo + + $IP nexthop add id 1 via 198.51.100.2 dev dummy0 + $IP nexthop add id 2 via 192.0.2.2 dev dummy1 + $IP nexthop add id 3 group 1/2 + $IP route add 203.0.113.0/24 nhid 3 + set +e + + ipv4_mpath_oif_test_common "oif dummy0" "dummy0" \ + "IPv4 multipath via first nexthop" + + ipv4_mpath_oif_test_common "oif dummy1" "dummy1" \ + "IPv4 multipath via second nexthop" + + ipv4_mpath_oif_test_common "oif dummy0 from 192.0.2.17" "dummy0" \ + "IPv4 multipath via first nexthop with source address" + + ipv4_mpath_oif_test_common "oif dummy1 from 192.0.2.17" "dummy1" \ + "IPv4 multipath via second nexthop with source address" + + cleanup +} + +ipv4_mpath_oif_vrf_test() +{ + echo + echo "IPv4 multipath oif with VRF test" + + setup + + set -e + $IP -4 rule add pref 32765 table local + $IP -4 rule del pref 0 + $IP link add name vrf-123 up type vrf table 123 + $IP link set dev dummy0 master vrf-123 + $IP link add dummy1 up master vrf-123 type dummy + $IP address add 192.0.2.1/28 dev dummy1 + $IP address add 192.0.2.17/32 dev vrf-123 + + $IP route add 203.0.113.0/24 vrf vrf-123 \ + nexthop via 198.51.100.2 dev dummy0 \ + nexthop via 192.0.2.2 dev dummy1 + set +e + + ipv4_mpath_oif_test_common "oif dummy0" "dummy0" \ + "IPv4 multipath via first nexthop" + + ipv4_mpath_oif_test_common "oif dummy1" "dummy1" \ + "IPv4 multipath via second nexthop" + + ipv4_mpath_oif_test_common "oif dummy0 from 192.0.2.17" "dummy0" \ + "IPv4 multipath via first nexthop with source address" + + ipv4_mpath_oif_test_common "oif dummy1 from 192.0.2.17" "dummy1" \ + "IPv4 multipath via second nexthop with source address" + + cleanup +} + +ipv6_mpath_oif_test_common() +{ + local get_param=$1; shift + local expected_oif=$1; shift + local test_name=$1; shift + local tmp_file + + tmp_file=$(mktemp) + + for i in {1..100}; do + $IP route get 2001:db8:10::${i} $get_param >> "$tmp_file" + done + + [[ $(grep "$expected_oif" "$tmp_file" | wc -l) -eq 100 ]] + log_test $? 0 "$test_name" + + rm "$tmp_file" +} + +ipv6_mpath_oif_test() +{ + echo + echo "IPv6 multipath oif test" + + setup + + set -e + $IP link add dummy1 up type dummy + $IP address add 2001:db8:2::1/64 dev dummy1 + $IP address add 2001:db8:100::1/128 dev lo + + $IP route add 2001:db8:10::/64 \ + nexthop via 2001:db8:1::2 dev dummy0 \ + nexthop via 2001:db8:2::2 dev dummy1 + set +e + + ipv6_mpath_oif_test_common "oif dummy0" "dummy0" \ + "IPv6 multipath via first nexthop" + + ipv6_mpath_oif_test_common "oif dummy1" "dummy1" \ + "IPv6 multipath via second nexthop" + + ipv6_mpath_oif_test_common "oif dummy0 from 2001:db8:100::1" "dummy0" \ + "IPv6 multipath via first nexthop with source address" + + ipv6_mpath_oif_test_common "oif dummy1 from 2001:db8:100::1" "dummy1" \ + "IPv6 multipath via second nexthop with source address" + + cleanup +} + +ipv6_mpath_oif_nh_test() +{ + echo + echo "IPv6 multipath oif with nexthop object test" + + setup + + set -e + $IP link add dummy1 up type dummy + $IP address add 2001:db8:2::1/64 dev dummy1 + $IP address add 2001:db8:100::1/128 dev lo + + $IP nexthop add id 1 via 2001:db8:1::2 dev dummy0 + $IP nexthop add id 2 via 2001:db8:2::2 dev dummy1 + $IP nexthop add id 3 group 1/2 + $IP route add 2001:db8:10::/64 nhid 3 + set +e + + ipv6_mpath_oif_test_common "oif dummy0" "dummy0" \ + "IPv6 multipath via first nexthop" + + ipv6_mpath_oif_test_common "oif dummy1" "dummy1" \ + "IPv6 multipath via second nexthop" + + ipv6_mpath_oif_test_common "oif dummy0 from 2001:db8:100::1" "dummy0" \ + "IPv6 multipath via first nexthop with source address" + + ipv6_mpath_oif_test_common "oif dummy1 from 2001:db8:100::1" "dummy1" \ + "IPv6 multipath via second nexthop with source address" + + cleanup +} + +ipv6_mpath_oif_vrf_test() +{ + echo + echo "IPv6 multipath oif with VRF test" + + setup + + set -e + $NS_EXEC sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1 + $IP -6 rule add pref 32765 table local + $IP -6 rule del pref 0 + $IP link add name vrf-123 up type vrf table 123 + $IP link set dev dummy0 master vrf-123 + $IP link add dummy1 up master vrf-123 type dummy + $IP address add 2001:db8:2::1/64 dev dummy1 + $IP address add 2001:db8:100::1/128 dev vrf-123 + + $IP route add 2001:db8:10::/64 vrf vrf-123 \ + nexthop via 2001:db8:1::2 dev dummy0 \ + nexthop via 2001:db8:2::2 dev dummy1 + set +e + + ipv6_mpath_oif_test_common "oif dummy0" "dummy0" \ + "IPv6 multipath via first nexthop" + + ipv6_mpath_oif_test_common "oif dummy1" "dummy1" \ + "IPv6 multipath via second nexthop" + + ipv6_mpath_oif_test_common "oif dummy0 from 2001:db8:100::1" "dummy0" \ + "IPv6 multipath via first nexthop with source address" + + ipv6_mpath_oif_test_common "oif dummy1 from 2001:db8:100::1" "dummy1" \ + "IPv6 multipath via second nexthop with source address" + + cleanup +} + ################################################################################ # usage @@ -3001,7 +3300,14 @@ do ipv4_mpath_balance) ipv4_mpath_balance_test;; ipv6_mpath_balance) ipv6_mpath_balance_test;; ipv4_mpath_balance_preferred) ipv4_mpath_balance_preferred_test;; + ipv4_mpath_oif) ipv4_mpath_oif_test;; + ipv4_mpath_oif_nh) ipv4_mpath_oif_nh_test;; + ipv4_mpath_oif_vrf) ipv4_mpath_oif_vrf_test;; + ipv6_mpath_oif) ipv6_mpath_oif_test;; + ipv6_mpath_oif_nh) ipv6_mpath_oif_nh_test;; + ipv6_mpath_oif_vrf) ipv6_mpath_oif_vrf_test;; fib6_ra_to_static) fib6_ra_to_static;; + fib6_temp_addr_renewal) fib6_temp_addr_renewal;; help) echo "Test names: $TESTS"; exit 0;; esac diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh index ebdb4c790a5d..c4cd2078a8db 100755 --- a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh @@ -162,14 +162,27 @@ vlmc_query_cnt_setup() { local type=$1 local dev=$2 + local match=($3) if [[ $type == "igmp" ]]; then - tc filter add dev $dev egress pref 10 prot 802.1Q \ + # This matches: IP Protocol 2 (IGMP) + tc filter add dev "$dev" egress pref 10 prot 802.1Q \ flower vlan_id 10 vlan_ethtype ipv4 dst_ip 224.0.0.1 ip_proto 2 \ + action goto chain 1 + # AND Type 0x11 (Query) at offset 0 of IGMP header + # 20 bytes IPv4 header + 4 bytes Router Alert option + IGMP[offset 0] + tc filter add dev "$dev" egress pref 20 chain 1 prot 802.1Q u32 \ + match u8 0x11 0xff at 24 "${match[@]}" \ action pass else - tc filter add dev $dev egress pref 10 prot 802.1Q \ + # This matches: ICMPv6 + tc filter add dev "$dev" egress pref 10 prot 802.1Q \ flower vlan_id 10 vlan_ethtype ipv6 dst_ip ff02::1 ip_proto icmpv6 \ + action goto chain 1 + # AND Type 0x82 (Query) at offset 0 of MLD header + # 40 bytes IPv6 header + 8 bytes Hop-by-hop option + MLD[offset 0] + tc filter add dev "$dev" egress pref 20 chain 1 prot 802.1Q u32 \ + match u8 0x82 0xff at 48 "${match[@]}" \ action pass fi @@ -181,7 +194,39 @@ vlmc_query_cnt_cleanup() local dev=$1 ip link set dev br0 type bridge mcast_stats_enabled 0 - tc filter del dev $dev egress pref 10 + tc filter del dev "$dev" egress pref 20 chain 1 + tc filter del dev "$dev" egress pref 10 +} + +vlmc_query_get_intvl_match() +{ + local type=$1 + local version=$2 + local test=$3 + local enc_val=$4 + + if [ "$test" = "qqic" ]; then + # QQIC is 8-bit floating point encoding for IGMPv3 and MLDv2 + if [ "${type}v${version}" = "igmpv3" ]; then + # QQIC is at offset 9 of IGMP header + # 20 bytes IPv4 header + 4 bytes Router Alert option + IGMP[offset 9] + echo "match u8 $enc_val 0xff at 33" + elif [ "${type}v${version}" = "mldv2" ]; then + # QQIC is at offset 25 of MLD header + # 40 bytes IPv6 header + 8 bytes Hop-by-hop option + MLD[offset 25] + echo "match u8 $enc_val 0xff at 73" + fi + elif [ "$test" = "mrc" ]; then + if [ "${type}v${version}" = "igmpv3" ]; then + # MRC is 8-bit floating point encoding at offset 1 of IGMP header + # 20 bytes IPv4 header + 4 bytes Router Alert option + IGMP[offset 1] + echo "match u8 $enc_val 0xff at 25" + elif [ "${type}v${version}" = "mldv2" ]; then + # MRC is 16-bit floating point encoding at offset 4 of MLD header + # 40 bytes IPv6 header + 8 bytes Hop-by-hop option + MLD[offset 4] + echo "match u16 $enc_val 0xffff at 52" + fi + fi } vlmc_check_query() @@ -191,9 +236,13 @@ vlmc_check_query() local dev=$3 local expect=$4 local time=$5 + local test=$6 + local enc_val=$7 + local intvl_match="" local ret=0 - vlmc_query_cnt_setup $type $dev + intvl_match="$(vlmc_query_get_intvl_match "$type" "$version" "$test" "$enc_val")" + vlmc_query_cnt_setup "$type" "$dev" "$intvl_match" local pre_tx_xstats=$(vlmc_query_cnt_xstats $type $version $dev) bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_querier 1 @@ -201,7 +250,7 @@ vlmc_check_query() if [[ $ret -eq 0 ]]; then sleep $time - local tcstats=$(tc_rule_stats_get $dev 10 egress) + local tcstats=$(tc_rule_stats_get "$dev" 20 egress) local post_tx_xstats=$(vlmc_query_cnt_xstats $type $version $dev) if [[ $tcstats != $expect || \ @@ -448,8 +497,46 @@ vlmc_query_intvl_test() # 1 is sent immediately, then 2 more in the next 5 seconds vlmc_check_query igmp 2 $swp1 3 5 check_err $? "Wrong number of tagged IGMPv2 general queries sent" - log_test "Vlan 10 mcast_query_interval option changed to 200" + log_test "Number of tagged IGMPv2 general query" + RET=0 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 3 + check_err $? "Could not set mcast_igmp_version in vlan 10" + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 2 + check_err $? "Could not set mcast_mld_version in vlan 10" + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 6000 + check_err $? "Could not set mcast_query_interval in vlan 10" + # 1 is sent immediately, IGMPv3 QQIC should match with linear value 60 (0x3c) + # which is 8-bit encoded value of 60 [units of seconds] + vlmc_check_query igmp 3 $swp1 1 1 qqic 0x3c + check_err $? "Wrong QQIC in generated IGMPv3 general queries" + log_test "IGMPv3 QQIC linear value 60(s)" + + RET=0 + # 1 is sent immediately, MLDv2 QQIC should match with linear value 60 (0x3c) + # which is 8-bit encoded value of 60 [units of seconds] + vlmc_check_query mld 2 $swp1 1 1 qqic 0x3c + check_err $? "Wrong QQIC in generated MLDv2 general queries" + log_test "MLDv2 QQIC linear value 60(s)" + + RET=0 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 16000 + check_err $? "Could not set mcast_query_interval in vlan 10" + # 1 is sent immediately, IGMPv3 QQIC should match with non linear value 132 (0x84) + # which is 8-bit encoded value of 160 [units of seconds] + vlmc_check_query igmp 3 $swp1 1 1 qqic 0x84 + check_err $? "Wrong QQIC in generated IGMPv3 general queries" + log_test "IGMPv3 QQIC non linear value 160(s)" + + RET=0 + # 1 is sent immediately, MLDv2 QQIC should match with non linear value 132 (0x84) + # which is 8-bit encoded value of 160 [units of seconds] + vlmc_check_query mld 2 $swp1 1 1 qqic 0x84 + check_err $? "Wrong QQIC in generated MLDv2 general queries" + log_test "MLDv2 QQIC non linear value 160(s)" + + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 2 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 1 bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 2 bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 12500 } @@ -469,10 +556,47 @@ vlmc_query_response_intvl_test() log_test "Vlan mcast_query_response_interval global option default value" RET=0 - bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 200 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 0 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 3 + check_err $? "Could not set mcast_igmp_version in vlan 10" + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 2 + check_err $? "Could not set mcast_mld_version in vlan 10" + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 600 check_err $? "Could not set mcast_query_response_interval in vlan 10" - log_test "Vlan 10 mcast_query_response_interval option changed to 200" + # 1 is sent immediately, IGMPv3 MRC should match with linear value 60 (0x3c) + # which is 8-bit encoded value of 60 [units of 0.1s = 6 seconds] + vlmc_check_query igmp 3 $swp1 1 1 mrc 0x3c + check_err $? "Wrong MRC in generated IGMPv3 general queries" + log_test "IGMPv3 MRC linear value of 60(x0.1s)" + RET=0 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 2400 + check_err $? "Could not set mcast_query_response_interval in vlan 10" + # 1 is sent immediately, MLDv2 MRC should match with linear value 0x5dc0 (24000) + # which is 16-bit encoded value of 24000 [units of ms / 24 seconds] + vlmc_check_query mld 2 $swp1 1 1 mrc 0x5dc0 + check_err $? "Wrong MRC in generated MLDv2 general queries" + log_test "MLDv2 MRC linear value of 24000(ms)" + + RET=0 + # 1 is sent immediately, IGMPv3 MRC should match with non linear value 142 (0x8e) + # which is 8-bit encoded value of 240 [units of 0.1s = 24 seconds] + vlmc_check_query igmp 3 $swp1 1 1 mrc 0x8e + check_err $? "Wrong MRC in generated IGMPv3 general queries" + log_test "IGMPv3 MRC non linear value of 240(x0.1s)" + + RET=0 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 4800 + check_err $? "Could not set mcast_query_response_interval in vlan 10" + # 1 is sent immediately, MLDv2 MRC should match with non linear value 0x8770 (34672) + # which is 16-bit encoded value of 48000 [units of ms / 48 seconds] + vlmc_check_query mld 2 $swp1 1 1 mrc 0x8770 + check_err $? "Wrong MRC in generated MLDv2 general queries" + log_test "MLDv2 MRC non linear value of 48000(ms)" + + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 2 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 1 + bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 2 bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 1000 } diff --git a/tools/testing/selftests/net/forwarding/ipmr.c b/tools/testing/selftests/net/forwarding/ipmr.c index df870aad9ead..9cd9f70de132 100644 --- a/tools/testing/selftests/net/forwarding/ipmr.c +++ b/tools/testing/selftests/net/forwarding/ipmr.c @@ -2,7 +2,9 @@ /* Copyright 2026 Google LLC */ #include +#include #include +#include #include #include #include @@ -17,6 +19,14 @@ FIXTURE(ipmr) int netlink_sk; int raw_sk; int veth_ifindex; + union { + struct vifctl vif; + struct mif6ctl vif6; + }; + union { + struct mfcctl mfc; + struct mf6cctl mfc6; + }; }; FIXTURE_VARIANT(ipmr) @@ -24,7 +34,14 @@ FIXTURE_VARIANT(ipmr) int family; int protocol; int level; + int rtm_family; int opts[MRT_MAX - MRT_BASE + 1]; + int flush_flags; + int vif_size; + char vif_check_cmd_pimreg[64]; + char vif_check_cmd_veth[64]; + int mfc_size; + char mfc_check_cmd[1024]; }; FIXTURE_VARIANT_ADD(ipmr, ipv4) @@ -32,6 +49,7 @@ FIXTURE_VARIANT_ADD(ipmr, ipv4) .family = AF_INET, .protocol = IPPROTO_IGMP, .level = IPPROTO_IP, + .rtm_family = RTNL_FAMILY_IPMR, .opts = { MRT_INIT, MRT_DONE, @@ -47,6 +65,44 @@ FIXTURE_VARIANT_ADD(ipmr, ipv4) MRT_DEL_MFC_PROXY, MRT_FLUSH, }, + .flush_flags = MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC | + MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC, + .vif_size = sizeof(struct vifctl), + .vif_check_cmd_pimreg = "cat /proc/net/ip_mr_vif | grep -q pimreg", + .vif_check_cmd_veth = "cat /proc/net/ip_mr_vif | grep -q veth", + .mfc_size = sizeof(struct mfcctl), + .mfc_check_cmd = "cat /proc/net/ip_mr_cache | grep -q '00000000 00000000'", +}; + +FIXTURE_VARIANT_ADD(ipmr, ipv6) +{ + .family = AF_INET6, + .protocol = IPPROTO_ICMPV6, + .level = IPPROTO_IPV6, + .rtm_family = RTNL_FAMILY_IP6MR, + .opts = { + MRT6_INIT, + MRT6_DONE, + MRT6_ADD_MIF, + MRT6_DEL_MIF, + MRT6_ADD_MFC, + MRT6_DEL_MFC, + MRT6_VERSION, + MRT6_ASSERT, + MRT6_PIM, + MRT6_TABLE, + MRT6_ADD_MFC_PROXY, + MRT6_DEL_MFC_PROXY, + MRT6_FLUSH, + }, + .flush_flags = MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC | + MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC, + .vif_size = sizeof(struct mif6ctl), + .vif_check_cmd_pimreg = "cat /proc/net/ip6_mr_vif | grep -q pim6reg", + .vif_check_cmd_veth = "cat /proc/net/ip6_mr_vif | grep -q veth", + .mfc_size = sizeof(struct mf6cctl), + .mfc_check_cmd = "cat /proc/net/ip6_mr_cache | " + "grep -q '0000:0000:0000:0000:0000:0000:0000:0000 0000:0000:0000:0000:0000:0000:0000:0000'", }; struct mfc_attr { @@ -71,7 +127,9 @@ static struct rtattr *nl_add_rtattr(struct nlmsghdr *nlmsg, struct rtattr *rta, return RTA_NEXT(rta, unused); } -static int nl_sendmsg_mfc(struct __test_metadata *_metadata, FIXTURE_DATA(ipmr) *self, +static int nl_sendmsg_mfc(struct __test_metadata *_metadata, + FIXTURE_DATA(ipmr) *self, + const FIXTURE_VARIANT(ipmr) *variant, __u16 nlmsg_type, struct mfc_attr *mfc_attr) { struct { @@ -87,7 +145,7 @@ static int nl_sendmsg_mfc(struct __test_metadata *_metadata, FIXTURE_DATA(ipmr) }, .rtm = { /* hard requirements in rtm_to_ipmr_mfcc() */ - .rtm_family = RTNL_FAMILY_IPMR, + .rtm_family = variant->rtm_family, .rtm_dst_len = 32, .rtm_type = RTN_MULTICAST, .rtm_scope = RT_SCOPE_UNIVERSE, @@ -144,6 +202,18 @@ FIXTURE_SETUP(ipmr) ASSERT_EQ(0, err); self->veth_ifindex = ifr.ifr_ifindex; + + if (variant->family == AF_INET) { + self->vif = (struct vifctl){ + .vifc_flags = VIFF_USE_IFINDEX, + .vifc_lcl_ifindex = self->veth_ifindex, + }; + } else { + self->vif6 = (struct mif6ctl){ + .mif6c_flags = 0, + .mif6c_pifi = self->veth_ifindex, + }; + } } FIXTURE_TEARDOWN(ipmr) @@ -169,41 +239,39 @@ TEST_F(ipmr, mrt_init) TEST_F(ipmr, mrt_add_vif_register) { - struct vifctl vif = { - .vifc_vifi = 0, - .vifc_flags = VIFF_REGISTER, - }; int err; + memset(&self->vif, 0, variant->vif_size); + + if (variant->family == AF_INET) + self->vif.vifc_flags = VIFF_REGISTER; + else + self->vif6.mif6c_flags = MIFF_REGISTER; + err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); - err = system("cat /proc/net/ip_mr_vif | grep -q pimreg"); + err = system(variant->vif_check_cmd_pimreg); ASSERT_EQ(0, err); err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_DEL_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); } TEST_F(ipmr, mrt_del_vif_unreg) { - struct vifctl vif = { - .vifc_vifi = 0, - .vifc_flags = VIFF_USE_IFINDEX, - .vifc_lcl_ifindex = self->veth_ifindex, - }; int err; err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); - err = system("cat /proc/net/ip_mr_vif | grep -q veth0"); + err = system(variant->vif_check_cmd_veth); ASSERT_EQ(0, err); /* VIF is removed along with its device. */ @@ -213,23 +281,18 @@ TEST_F(ipmr, mrt_del_vif_unreg) /* mrt->vif_table[veth_ifindex]->dev is NULL. */ err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_DEL_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(-1, err); ASSERT_EQ(EADDRNOTAVAIL, errno); } TEST_F(ipmr, mrt_del_vif_netns_dismantle) { - struct vifctl vif = { - .vifc_vifi = 0, - .vifc_flags = VIFF_USE_IFINDEX, - .vifc_lcl_ifindex = self->veth_ifindex, - }; int err; err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); /* Let cleanup_net() remove veth0 and VIF. */ @@ -237,49 +300,42 @@ TEST_F(ipmr, mrt_del_vif_netns_dismantle) TEST_F(ipmr, mrt_add_mfc) { - struct mfcctl mfc = {}; int err; /* MRT_ADD_MFC / MRT_ADD_MFC_PROXY does not need vif to exist (unlike netlink). */ err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_MFC - MRT_BASE], - &mfc, sizeof(mfc)); + &self->mfc, variant->mfc_size); ASSERT_EQ(0, err); /* (0.0.0.0 -> 0.0.0.0) */ - err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' "); + err = system(variant->mfc_check_cmd); ASSERT_EQ(0, err); err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_DEL_MFC - MRT_BASE], - &mfc, sizeof(mfc)); + &self->mfc, variant->mfc_size); } TEST_F(ipmr, mrt_add_mfc_proxy) { - struct mfcctl mfc = {}; int err; err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_MFC_PROXY - MRT_BASE], - &mfc, sizeof(mfc)); + &self->mfc, variant->mfc_size); ASSERT_EQ(0, err); - err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' "); + err = system(variant->mfc_check_cmd); ASSERT_EQ(0, err); err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_DEL_MFC_PROXY - MRT_BASE], - &mfc, sizeof(mfc)); + &self->mfc, variant->mfc_size); } TEST_F(ipmr, mrt_add_mfc_netlink) { - struct vifctl vif = { - .vifc_vifi = 0, - .vifc_flags = VIFF_USE_IFINDEX, - .vifc_lcl_ifindex = self->veth_ifindex, - }; struct mfc_attr mfc_attr = { .table = RT_TABLE_DEFAULT, .origin = 0, @@ -291,26 +347,21 @@ TEST_F(ipmr, mrt_add_mfc_netlink) err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); - err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr); ASSERT_EQ(0, err); - err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' "); + err = system(variant->mfc_check_cmd); ASSERT_EQ(0, err); - err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_DELROUTE, &mfc_attr); ASSERT_EQ(0, err); } TEST_F(ipmr, mrt_add_mfc_netlink_proxy) { - struct vifctl vif = { - .vifc_vifi = 0, - .vifc_flags = VIFF_USE_IFINDEX, - .vifc_lcl_ifindex = self->veth_ifindex, - }; struct mfc_attr mfc_attr = { .table = RT_TABLE_DEFAULT, .origin = 0, @@ -322,16 +373,16 @@ TEST_F(ipmr, mrt_add_mfc_netlink_proxy) err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); - err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr); ASSERT_EQ(0, err); - err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' "); + err = system(variant->mfc_check_cmd); ASSERT_EQ(0, err); - err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_DELROUTE, &mfc_attr); ASSERT_EQ(0, err); } @@ -347,12 +398,12 @@ TEST_F(ipmr, mrt_add_mfc_netlink_no_vif) /* netlink always requires RTA_IIF of an existing vif. */ mfc_attr.ifindex = 0; - err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr); ASSERT_EQ(-ENFILE, err); /* netlink always requires RTA_IIF of an existing vif. */ mfc_attr.ifindex = self->veth_ifindex; - err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr); ASSERT_EQ(-ENFILE, err); } @@ -387,10 +438,10 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle) } /* Create a MFC for mrt->vif_table[0]. */ - err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr); ASSERT_EQ(0, err); - err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' "); + err = system(variant->mfc_check_cmd); ASSERT_EQ(0, err); /* Remove mrt->vif_table[0]. */ @@ -398,13 +449,13 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle) ASSERT_EQ(0, err); /* MFC entry is NOT removed even if the tied VIF is removed... */ - err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' "); + err = system(variant->mfc_check_cmd); ASSERT_EQ(0, err); /* ... and netlink is not capable of removing such an entry * because netlink always requires a valid RTA_IIF ... :/ */ - err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr); + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_DELROUTE, &mfc_attr); ASSERT_EQ(-ENODEV, err); /* It can be removed by setsockopt(), but let cleanup_net() remove this time. */ @@ -412,11 +463,6 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle) TEST_F(ipmr, mrt_table_flush) { - struct vifctl vif = { - .vifc_vifi = 0, - .vifc_flags = VIFF_USE_IFINDEX, - .vifc_lcl_ifindex = self->veth_ifindex, - }; struct mfc_attr mfc_attr = { .origin = 0, .group = 0, @@ -424,7 +470,7 @@ TEST_F(ipmr, mrt_table_flush) .proxy = false, }; int table_id = 92; - int err, flags; + int err; /* Set a random table id rather than RT_TABLE_DEFAULT. * Note that /proc/net/ip_mr_{vif,cache} only supports RT_TABLE_DEFAULT. @@ -436,20 +482,29 @@ TEST_F(ipmr, mrt_table_flush) err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE], - &vif, sizeof(vif)); + &self->vif, variant->vif_size); ASSERT_EQ(0, err); - mfc_attr.table = table_id; - err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr); + if (variant->family == AF_INET) { + mfc_attr.table = table_id; + err = nl_sendmsg_mfc(_metadata, self, variant, RTM_NEWROUTE, &mfc_attr); + } else { + err = setsockopt(self->raw_sk, + variant->level, variant->opts[MRT_ADD_MFC - MRT_BASE], + &self->mfc, variant->mfc_size); + } ASSERT_EQ(0, err); /* Flush mrt->vif_table[] and all caches. */ - flags = MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC | - MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC; err = setsockopt(self->raw_sk, variant->level, variant->opts[MRT_FLUSH - MRT_BASE], - &flags, sizeof(flags)); + &variant->flush_flags, sizeof(variant->flush_flags)); ASSERT_EQ(0, err); } +XFAIL_ADD(ipmr, ipv6, mrt_add_mfc_netlink); +XFAIL_ADD(ipmr, ipv6, mrt_add_mfc_netlink_proxy); +XFAIL_ADD(ipmr, ipv6, mrt_add_mfc_netlink_no_vif); +XFAIL_ADD(ipmr, ipv6, mrt_del_mfc_netlink_netns_dismantle); + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c new file mode 100644 index 000000000000..209569354d0e --- /dev/null +++ b/tools/testing/selftests/net/getsockopt_iter.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Quick test for getsockopt{_iter} tests. + * + * Each fixture targets one converted protocol and pins down the + * returned-length / errno semantics across buffer-size variations, + * an unknown optname and a bogus level. + * + * - netlink: NETLINK_PKTINFO covers the flag-style int path; the + * NETLINK_LIST_MEMBERSHIPS cases cover the size-discovery path + * that always reports the required buffer length back via optlen, + * even when the user buffer is too small to receive any group bits. + * - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path. + * + * Author: Breno Leitao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kselftest_harness.h" + +#ifndef AF_VSOCK +#define AF_VSOCK 40 +#endif + +/* ---------- netlink ---------- */ + +FIXTURE(netlink) +{ + int fd; +}; + +FIXTURE_SETUP(netlink) +{ + int group = RTNLGRP_LINK; + + self->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (self->fd < 0) + SKIP(return, "AF_NETLINK socket: %s", strerror(errno)); + + /* Joining a multicast group grows nlk->ngroups so the + * NETLINK_LIST_MEMBERSHIPS path has a non-zero size to report. + */ + if (setsockopt(self->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)) < 0) + SKIP(return, "NETLINK_ADD_MEMBERSHIP: %s", strerror(errno)); +} + +FIXTURE_TEARDOWN(netlink) +{ + if (self->fd >= 0) + close(self->fd); +} + +TEST_F(netlink, pktinfo_exact) +{ + socklen_t optlen; + int val = -1; + + optlen = sizeof(val); + + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, + &val, &optlen)); + ASSERT_EQ(sizeof(int), optlen); + ASSERT_TRUE(val == 0 || val == 1); +} + +TEST_F(netlink, pktinfo_oversize_clamped) +{ + char buf[16] = {}; + socklen_t optlen; + + optlen = sizeof(buf); + + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, + buf, &optlen)); + ASSERT_EQ(sizeof(int), optlen); +} + +TEST_F(netlink, pktinfo_undersize) +{ + char buf[2] = {}; + socklen_t optlen; + + optlen = sizeof(buf); + + ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, + buf, &optlen)); + ASSERT_EQ(EINVAL, errno); + ASSERT_EQ(sizeof(buf), optlen); +} + +TEST_F(netlink, list_memberships_size_discovery) +{ + socklen_t optlen = 0; + char dummy; + + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, + NETLINK_LIST_MEMBERSHIPS, + &dummy, &optlen)); + ASSERT_GT(optlen, 0); + ASSERT_EQ(0, optlen % sizeof(__u32)); +} + +TEST_F(netlink, list_memberships_full_read) +{ + __u32 buf[64] = {}; + socklen_t optlen; + + optlen = sizeof(buf); + + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, + NETLINK_LIST_MEMBERSHIPS, + buf, &optlen)); + ASSERT_GT(optlen, 0); + ASSERT_LE(optlen, sizeof(buf)); + ASSERT_EQ(0, optlen % sizeof(__u32)); +} + +TEST_F(netlink, bad_level) +{ + socklen_t optlen; + int val; + + optlen = sizeof(val); + + ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO, + &val, &optlen)); + ASSERT_EQ(ENOPROTOOPT, errno); + ASSERT_EQ(sizeof(val), optlen); +} + +TEST_F(netlink, bad_optname) +{ + socklen_t optlen; + int val; + + optlen = sizeof(val); + + ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff, + &val, &optlen)); + ASSERT_EQ(ENOPROTOOPT, errno); + ASSERT_EQ(sizeof(val), optlen); +} + +/* ---------- vsock ---------- */ + +FIXTURE(vsock) +{ + int fd; +}; + +FIXTURE_SETUP(vsock) +{ + self->fd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (self->fd < 0) + SKIP(return, "AF_VSOCK socket: %s", strerror(errno)); +} + +FIXTURE_TEARDOWN(vsock) +{ + if (self->fd >= 0) + close(self->fd); +} + +TEST_F(vsock, buffer_size_exact) +{ + socklen_t optlen; + uint64_t val = 0; + + optlen = sizeof(val); + + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_BUFFER_SIZE, + &val, &optlen)); + ASSERT_EQ(sizeof(uint64_t), optlen); + ASSERT_GT(val, 0); +} + +TEST_F(vsock, buffer_size_oversize_clamped) +{ + char buf[16] = {}; + socklen_t optlen; + + optlen = sizeof(buf); + + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_BUFFER_SIZE, + buf, &optlen)); + ASSERT_EQ(sizeof(uint64_t), optlen); +} + +TEST_F(vsock, buffer_size_undersize) +{ + char buf[4] = {}; + socklen_t optlen; + + optlen = sizeof(buf); + + ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_BUFFER_SIZE, + buf, &optlen)); + ASSERT_EQ(EINVAL, errno); + ASSERT_EQ(sizeof(buf), optlen); +} + +TEST_F(vsock, bad_level) +{ + socklen_t optlen; + uint64_t val; + + optlen = sizeof(val); + + ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, + SO_VM_SOCKETS_BUFFER_SIZE, + &val, &optlen)); + ASSERT_EQ(ENOPROTOOPT, errno); + ASSERT_EQ(sizeof(val), optlen); +} + +TEST_F(vsock, bad_optname) +{ + socklen_t optlen; + uint64_t val; + + optlen = sizeof(val); + + ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff, + &val, &optlen)); + ASSERT_EQ(ENOPROTOOPT, errno); + ASSERT_EQ(sizeof(val), optlen); +} + +/* SO_VM_SOCKETS_CONNECT_TIMEOUT_{NEW,OLD} return a sock_timeval-shaped + * payload, which is wider than u64 on 64-bit. They exercise the path + * where the protocol's reported lv (16 bytes) is larger than the + * common 8-byte u64 case covered above. + */ +TEST_F(vsock, connect_timeout_new_exact) +{ + struct __kernel_sock_timeval tv = {}; + socklen_t optlen; + + optlen = sizeof(tv); + + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW, + &tv, &optlen)); + ASSERT_EQ(sizeof(tv), optlen); +} + +TEST_F(vsock, connect_timeout_new_oversize_clamped) +{ + char buf[sizeof(struct __kernel_sock_timeval) * 2] = {}; + socklen_t optlen; + + optlen = sizeof(buf); + + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW, + buf, &optlen)); + ASSERT_EQ(sizeof(struct __kernel_sock_timeval), optlen); +} + +TEST_F(vsock, connect_timeout_new_undersize) +{ + socklen_t optlen; + uint64_t val; + + optlen = sizeof(val); + + ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW, + &val, &optlen)); + ASSERT_EQ(EINVAL, errno); + ASSERT_EQ(sizeof(val), optlen); +} + +TEST_F(vsock, connect_timeout_old_exact) +{ + struct __kernel_old_timeval tv = {}; + socklen_t optlen; + + optlen = sizeof(tv); + + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, + SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD, + &tv, &optlen)); + ASSERT_EQ(sizeof(tv), optlen); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c index 11b16ae5f0e8..7a333155de1a 100644 --- a/tools/testing/selftests/net/lib/gro.c +++ b/tools/testing/selftests/net/lib/gro.c @@ -67,12 +67,14 @@ #include #include #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -102,9 +104,12 @@ #define MAX_LARGE_PKT_CNT ((IP_MAXPACKET - (MAX_HDR_LEN - ETH_HLEN)) / \ (ASSUMED_MTU - (MAX_HDR_LEN - ETH_HLEN))) #define MIN_EXTHDR_SIZE 8 +#define L2_HLEN_MAX (ETH_HLEN + PPPOE_SES_HLEN) #define EXT_PAYLOAD_1 "\x00\x00\x00\x00\x00\x00" #define EXT_PAYLOAD_2 "\x11\x11\x11\x11\x11\x11" +#define EXIT_OVER_COALESCE 42 + #define ipv6_optlen(p) (((p)->hdrlen+1) << 3) /* calculate IPv6 extension header len */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) @@ -134,6 +139,7 @@ static int total_hdr_len = -1; static int ethhdr_proto = -1; static bool ipip; static bool ip6ip6; +static bool pppoe; static uint64_t txtime_ns; static int num_flows = 4; static bool order_check; @@ -171,6 +177,22 @@ static void vlog(const char *fmt, ...) } } +static void fill_pppoelayer(void *buf, int payload_len, uint16_t sid) +{ + struct pppoe_ppp_hdr { + struct pppoe_hdr eh; + __be16 proto; + } *ph = buf; + + payload_len += sizeof(struct tcphdr); + ph->eh.type = 1; + ph->eh.ver = 1; + ph->eh.code = 0; + ph->eh.sid = htons(sid); + ph->eh.length = htons(payload_len + sizeof(ph->proto)); + ph->proto = htons(proto == PF_INET ? PPP_IP : PPP_IPV6); +} + static void setup_sock_filter(int fd) { const int dport_off = tcp_offset + offsetof(struct tcphdr, dest); @@ -412,11 +434,15 @@ static void create_packet(void *buf, int seq_offset, int ack_offset, fill_networklayer(buf + inner_ip_off, payload_len, IPPROTO_TCP); if (inner_ip_off > ETH_HLEN) { - int encap_proto = (proto == PF_INET) ? - IPPROTO_IPIP : IPPROTO_IPV6; + if (pppoe) { + fill_pppoelayer(buf + ETH_HLEN, payload_len + ip_hdr_len, 0x1234); + } else { + int encap_proto = (proto == PF_INET) ? + IPPROTO_IPIP : IPPROTO_IPV6; - fill_networklayer(buf + ETH_HLEN, - payload_len + ip_hdr_len, encap_proto); + fill_networklayer(buf + ETH_HLEN, + payload_len + ip_hdr_len, encap_proto); + } } fill_datalinklayer(buf); @@ -526,7 +552,7 @@ static void send_flags(int fd, struct sockaddr_ll *daddr, int psh, int syn, static void send_data_pkts(int fd, struct sockaddr_ll *daddr, int payload_len1, int payload_len2) { - static char buf[ETH_HLEN + IP_MAXPACKET]; + static char buf[L2_HLEN_MAX + IP_MAXPACKET]; create_packet(buf, 0, 0, payload_len1, 0); write_packet(fd, buf, total_hdr_len + payload_len1, daddr); @@ -1071,6 +1097,20 @@ static void send_fragment6(int fd, struct sockaddr_ll *daddr) write_packet(fd, buf, bufpkt_len, daddr); } +static void send_changed_pppoe_sid(int fd, struct sockaddr_ll *daddr) +{ + static char buf[MAX_HDR_LEN + PAYLOAD_LEN]; + int pkt_size = total_hdr_len + PAYLOAD_LEN; + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(buf + ETH_HLEN); + + create_packet(buf, 0, 0, PAYLOAD_LEN, 0); + write_packet(fd, buf, pkt_size, daddr); + + create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0); + hdr->sid = htons(0x4321); + write_packet(fd, buf, pkt_size, daddr); +} + static void bind_packetsocket(int fd) { struct sockaddr_ll daddr = {}; @@ -1121,11 +1161,14 @@ static void recv_error(int fd, int rcv_errno) static void check_recv_pkts(int fd, int *correct_payload, int correct_num_pkts) { - static char buffer[IP_MAXPACKET + ETH_HLEN + 1]; - struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN); - struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN); + static char buffer[IP_MAXPACKET + L2_HLEN_MAX + 1]; + int nhoff = ETH_HLEN + (pppoe ? PPPOE_SES_HLEN : 0); + struct iphdr *iph = (struct iphdr *)(buffer + nhoff); + struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + nhoff); struct tcphdr *tcph; bool bad_packet = false; + int bytes_expected = 0; + int bytes_received = 0; int tcp_ext_len = 0; int ip_ext_len = 0; int pkt_size = -1; @@ -1134,13 +1177,15 @@ static void check_recv_pkts(int fd, int *correct_payload, int i; vlog("Expected {"); - for (i = 0; i < correct_num_pkts; i++) + for (i = 0; i < correct_num_pkts; i++) { vlog("%d ", correct_payload[i]); + bytes_expected += correct_payload[i]; + } vlog("}, Total %d packets\nReceived {", correct_num_pkts); while (1) { ip_ext_len = 0; - pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0); + pkt_size = recv(fd, buffer, sizeof(buffer), 0); if (pkt_size < 0) recv_error(fd, errno); @@ -1170,9 +1215,17 @@ static void check_recv_pkts(int fd, int *correct_payload, vlog("[!=%d]", correct_payload[num_pkt]); bad_packet = true; } + bytes_received += data_len; num_pkt++; } vlog("}, Total %d packets.\n", num_pkt); + /* Signal over-coalescing explicitly, it's a hard failure, unlike + * under-coalescing which could be timing- or loss-related. + */ + if (num_pkt < correct_num_pkts && bytes_received == bytes_expected) + error(EXIT_OVER_COALESCE, 0, + "over-coalesced: got %d pkts vs expected %d (%d B)", + num_pkt, correct_num_pkts, bytes_received); if (num_pkt != correct_num_pkts) error(1, 0, "incorrect number of packets"); if (bad_packet) @@ -1183,9 +1236,10 @@ static void check_recv_pkts(int fd, int *correct_payload, static void check_capacity_pkts(int fd) { - static char buffer[IP_MAXPACKET + ETH_HLEN + 1]; - struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN); - struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN); + static char buffer[IP_MAXPACKET + L2_HLEN_MAX + 1]; + int nhoff = ETH_HLEN + (pppoe ? PPPOE_SES_HLEN : 0); + struct iphdr *iph = (struct iphdr *)(buffer + nhoff); + struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + nhoff); int num_pkt = 0, num_coal = 0, pkt_idx; const char *fail_reason = NULL; int flow_order[num_flows * 2]; @@ -1203,7 +1257,7 @@ static void check_capacity_pkts(int fd) while (1) { ip_ext_len = 0; - pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0); + pkt_size = recv(fd, buffer, sizeof(buffer), 0); if (pkt_size < 0) recv_error(fd, errno); @@ -1499,6 +1553,12 @@ static void gro_sender(void) usleep(fin_delay_us); write_packet(txfd, fin_pkt, total_hdr_len, &daddr); + /* PPPoE sub-tests */ + } else if (strcmp(testname, "pppoe_sid") == 0) { + send_changed_pppoe_sid(txfd, &daddr); + usleep(fin_delay_us); + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); + } else { error(1, 0, "Unknown testcase: %s", testname); } @@ -1716,6 +1776,12 @@ static void gro_receiver(void) } else if (strcmp(testname, "capacity") == 0) { check_capacity_pkts(rxfd); + } else if (strcmp(testname, "pppoe_sid") == 0) { + correct_payload[0] = PAYLOAD_LEN; + correct_payload[1] = PAYLOAD_LEN; + printf("different PPPoE session ID doesn't coalesce: "); + check_recv_pkts(rxfd, correct_payload, 2); + } else { error(1, 0, "Test case error: unknown testname %s", testname); } @@ -1734,6 +1800,8 @@ static void parse_args(int argc, char **argv) { "ipv6", no_argument, NULL, '6' }, { "ipip", no_argument, NULL, 'e' }, { "ip6ip6", no_argument, NULL, 'E' }, + { "pppoev4", no_argument, NULL, 'p' }, + { "pppoev6", no_argument, NULL, 'P' }, { "num-flows", required_argument, NULL, 'n' }, { "rx", no_argument, NULL, 'r' }, { "saddr", required_argument, NULL, 's' }, @@ -1745,7 +1813,7 @@ static void parse_args(int argc, char **argv) }; int c; - while ((c = getopt_long(argc, argv, "46d:D:eEi:n:rs:S:t:ov", opts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "46d:D:eEi:n:pPrs:S:t:ov", opts, NULL)) != -1) { switch (c) { case '4': proto = PF_INET; @@ -1765,6 +1833,16 @@ static void parse_args(int argc, char **argv) proto = PF_INET6; ethhdr_proto = htons(ETH_P_IPV6); break; + case 'p': + pppoe = true; + proto = PF_INET; + ethhdr_proto = htons(ETH_P_PPP_SES); + break; + case 'P': + pppoe = true; + proto = PF_INET6; + ethhdr_proto = htons(ETH_P_PPP_SES); + break; case 'd': addr4_dst = addr6_dst = optarg; break; @@ -1812,6 +1890,10 @@ int main(int argc, char **argv) } else if (ip6ip6) { tcp_offset = ETH_HLEN + sizeof(struct ipv6hdr) * 2; total_hdr_len = tcp_offset + sizeof(struct tcphdr); + } else if (pppoe) { + tcp_offset = ETH_HLEN + PPPOE_SES_HLEN + + (proto == PF_INET ? sizeof(struct iphdr) : sizeof(struct ipv6hdr)); + total_hdr_len = tcp_offset + sizeof(struct tcphdr); } else if (proto == PF_INET) { tcp_offset = ETH_HLEN + sizeof(struct iphdr); total_hdr_len = tcp_offset + sizeof(struct tcphdr); diff --git a/tools/testing/selftests/net/lib/py/__init__.py b/tools/testing/selftests/net/lib/py/__init__.py index 7c81d86a7e97..e58bdbdc58ee 100644 --- a/tools/testing/selftests/net/lib/py/__init__.py +++ b/tools/testing/selftests/net/lib/py/__init__.py @@ -10,11 +10,11 @@ from .ksft import KsftFailEx, KsftSkipEx, KsftXfailEx, ksft_pr, ksft_eq, \ ksft_ge, ksft_gt, ksft_lt, ksft_raises, ksft_busy_wait, \ ktap_result, ksft_disruptive, ksft_setup, ksft_run, ksft_exit, \ ksft_variants, KsftNamedVariant -from .netns import NetNS, NetNSEnter +from .netns import NetNS, NetNSEnter, UserNetNS from .nsim import NetdevSim, NetdevSimDev from .utils import CmdExitFailure, fd_read_timeout, cmd, bkg, defer, \ bpftool, ip, ethtool, bpftrace, rand_port, rand_ports, wait_port_listen, \ - wait_file, tool + wait_file, tool, tc from .bpf import bpf_map_set, bpf_map_dump, bpf_prog_map_ids from .ynl import NlError, NlctrlFamily, YnlFamily, \ EthtoolFamily, NetdevFamily, RtnlFamily, RtnlAddrFamily @@ -26,10 +26,10 @@ __all__ = ["KSRC", "ksft_is", "ksft_ge", "ksft_gt", "ksft_lt", "ksft_raises", "ksft_busy_wait", "ktap_result", "ksft_disruptive", "ksft_setup", "ksft_run", "ksft_exit", "ksft_variants", "KsftNamedVariant", - "NetNS", "NetNSEnter", + "NetNS", "NetNSEnter", "UserNetNS", "CmdExitFailure", "fd_read_timeout", "cmd", "bkg", "defer", "bpftool", "ip", "ethtool", "bpftrace", "rand_port", "rand_ports", - "wait_port_listen", "wait_file", "tool", + "wait_port_listen", "wait_file", "tool", "tc", "bpf_map_set", "bpf_map_dump", "bpf_prog_map_ids", "NetdevSim", "NetdevSimDev", "NetshaperFamily", "DevlinkFamily", "PSPFamily", "NlError", diff --git a/tools/testing/selftests/net/lib/py/netns.py b/tools/testing/selftests/net/lib/py/netns.py index 8e9317044eef..526f6aa80077 100644 --- a/tools/testing/selftests/net/lib/py/netns.py +++ b/tools/testing/selftests/net/lib/py/netns.py @@ -1,9 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 -from .utils import ip import ctypes +import os import random import string +import subprocess +import time +from pathlib import Path + +from .utils import ip libc = ctypes.cdll.LoadLibrary('libc.so.6') @@ -34,6 +39,74 @@ class NetNS: return f"NetNS({self.name})" +class UserNetNS: + """Network namespace owned by a non-init user namespace.""" + + def __init__(self): + self.name = ''.join( + random.choice(string.ascii_lowercase) for _ in range(8)) + self.user_ns_path = f"/run/userns/{self.name}" + self.net_ns_path = f"/run/netns/{self.name}" + self._user_mounted = False + self._net_mounted = False + + os.makedirs("/run/userns", exist_ok=True) + os.makedirs("/run/netns", exist_ok=True) + + Path(self.user_ns_path).touch() + Path(self.net_ns_path).touch() + + with subprocess.Popen( + ["unshare", "--user", "--net", "--map-root-user", + "sleep", "infinity"]) as proc: + try: + pid = proc.pid + init_user = os.readlink("/proc/self/ns/user") + for _ in range(200): + try: + if os.readlink(f"/proc/{pid}/ns/user") != init_user: + break + except OSError: + pass + time.sleep(0.01) + else: + raise RuntimeError("unshare child did not create userns") + + subprocess.run(["mount", "--bind", f"/proc/{pid}/ns/user", + self.user_ns_path], check=True) + self._user_mounted = True + subprocess.run(["mount", "--bind", f"/proc/{pid}/ns/net", + self.net_ns_path], check=True) + self._net_mounted = True + finally: + proc.kill() + + def __del__(self): + if self._net_mounted: + subprocess.run(["umount", self.net_ns_path], check=False) + self._net_mounted = False + if self._user_mounted: + subprocess.run(["umount", self.user_ns_path], check=False) + self._user_mounted = False + for path in (self.net_ns_path, self.user_ns_path): + try: + os.unlink(path) + except OSError: + pass + + def __enter__(self): + return self + + def __exit__(self, ex_type, ex_value, ex_tb): + self.__del__() + + def __str__(self): + return self.name + + def __repr__(self): + return f"UserNetNS({self.name})" + + class NetNSEnter: def __init__(self, ns_name): self.ns_path = f"/run/netns/{ns_name}" diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py index 6c44a3d2bbf7..87eae79d01c1 100644 --- a/tools/testing/selftests/net/lib/py/utils.py +++ b/tools/testing/selftests/net/lib/py/utils.py @@ -23,6 +23,10 @@ class CmdExitFailure(Exception): self.cmd = cmd_obj +class CmdExitZeroFailure(CmdExitFailure): + """ Command succeeded (returned zero exit code), but expected failure. """ + + def fd_read_timeout(fd, timeout): rlist, _, _ = select.select([fd], [], [], timeout) if rlist: @@ -39,10 +43,16 @@ class cmd: Use bkg() instead to run a command in the background. """ - def __init__(self, comm, shell=None, fail=True, ns=None, background=False, - host=None, timeout=5, ksft_ready=None, ksft_wait=None): + def __init__(self, comm, shell=None, fail=True, expect_fail=False, ns=None, + background=False, host=None, timeout=5, ksft_ready=None, + ksft_wait=None): if ns: - comm = f'ip netns exec {ns} ' + comm + if hasattr(ns, 'user_ns_path'): + comm = (f'nsenter --user={ns.user_ns_path} ' + f'--net={ns.net_ns_path} --setuid=0 --setgid=0 -- ' + + comm) + else: + comm = f'ip netns exec {ns} ' + comm self.stdout = None self.stderr = None @@ -88,7 +98,8 @@ class cmd: self._process_terminate(terminate=terminate, timeout=1) raise CmdInitFailure("Did not receive ready message", self) if not background: - self.process(terminate=False, fail=fail, timeout=timeout) + self.process(terminate=False, fail=fail, expect_fail=expect_fail, + timeout=timeout) def _process_terminate(self, terminate, timeout): if terminate: @@ -102,7 +113,7 @@ class cmd: return stdout, stderr - def process(self, terminate=True, fail=None, timeout=5): + def process(self, terminate=True, fail=None, expect_fail=False, timeout=5): if fail is None: fail = not terminate @@ -111,10 +122,19 @@ class cmd: stdout, stderr = self._process_terminate(terminate=terminate, timeout=timeout) - if self.proc.returncode != 0 and fail: + + # Fail on unexpected test failure if fail. + # Fail on unexpected test success if expect_fail. + # Fail on negative returncode if either: + # Set by subprocess on crash or signal, this is never expected failure. + if (self.proc.returncode != 0 and fail or + (self.proc.returncode < 0 and expect_fail)): if len(stderr) > 0 and stderr[-1] == "\n": stderr = stderr[:-1] raise CmdExitFailure("Command failed", self) + elif self.proc.returncode == 0 and expect_fail: + raise CmdExitZeroFailure("Command succeeded (expected fail)", self) + def __repr__(self): def str_fmt(name, s): @@ -157,14 +177,17 @@ class bkg(cmd): with bkg("my_binary", ksft_wait=5): """ - def __init__(self, comm, shell=None, fail=None, ns=None, host=None, - exit_wait=False, ksft_ready=None, ksft_wait=None): + def __init__(self, comm, shell=None, fail=None, expect_fail=None, + ns=None, host=None, exit_wait=False, ksft_ready=None, + ksft_wait=None): super().__init__(comm, background=True, - shell=shell, fail=fail, ns=ns, host=host, - ksft_ready=ksft_ready, ksft_wait=ksft_wait) + shell=shell, fail=fail, expect_fail=expect_fail, + ns=ns, host=host, ksft_ready=ksft_ready, + ksft_wait=ksft_wait) self.terminate = not exit_wait and not ksft_wait self._exit_wait = exit_wait self.check_fail = fail + self.expect_fail = expect_fail if shell and self.terminate: print("# Warning: combining shell and terminate is risky!") @@ -179,7 +202,8 @@ class bkg(cmd): # since forcing termination silences failures with fail=None if self.proc.poll() is None: terminate = terminate or (self._exit_wait and ex_type is not None) - return self.process(terminate=terminate, fail=self.check_fail) + return self.process(terminate=terminate, fail=self.check_fail, + expect_fail=self.expect_fail) GLOBAL_DEFER_QUEUE = [] @@ -220,7 +244,10 @@ class defer: def tool(name, args, json=None, ns=None, host=None): cmd_str = name + ' ' if json: - cmd_str += '--json ' + if name == 'tc': + cmd_str += '-json ' + else: + cmd_str += '--json ' cmd_str += args cmd_obj = cmd(cmd_str, ns=ns, host=host) if json: @@ -238,6 +265,13 @@ def ip(args, json=None, ns=None, host=None): return tool('ip', args, json=json, host=host) +def tc(args, json=None, ns=None, host=None): + """ Helper to call tc with standard set of optional args. """ + if ns: + args = f'-netns {ns} ' + args + return tool('tc', args, json=json, host=host) + + def ethtool(args, json=None, ns=None, host=None): return tool('ethtool', args, json=json, ns=ns, host=host) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 4b3f71e66609..c0aeffd5cb71 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -63,6 +63,7 @@ unset fastclose unset fullmesh unset speed unset bind_addr +unset ifaces_nr unset join_syn_rej unset join_csum_ns1 unset join_csum_ns2 @@ -86,6 +87,10 @@ unset fb_mpc_data unset fb_md5_sig unset fb_dss +unset add_addr_tx_nr +unset add_addr_echo_tx_nr +unset add_addr_drop_tx_nr + # generated using "nfbpf_compile '(ip && (ip[54] & 0xf0) == 0x30) || # (ip6 && (ip6[74] & 0xf0) == 0x30)'" CBPF_MPTCP_SUBOPTION_ADD_ADDR="14, @@ -146,7 +151,7 @@ init_partial() # ns1eth4 ns2eth4 local i - for i in $(seq 1 4); do + for i in $(seq 1 "${ifaces_nr:-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 @@ -165,7 +170,7 @@ init_partial() init_shapers() { local i - for i in $(seq 1 4); do + for i in $(seq 1 "${ifaces_nr:-4}"); do tc -n $ns1 qdisc add dev ns1eth$i root netem rate 20mbit delay 1ms tc -n $ns2 qdisc add dev ns2eth$i root netem rate 20mbit delay 1ms done @@ -512,6 +517,19 @@ reset_with_tcp_filter() fi } +# For kernel supporting limits above 8 +# $1: title ; $2,4: addrs limit ns1,2 ; $3,5: subflows limit ns1,2 +reset_with_high_limits() +{ + reset "${1}" || return 1 + + if ! pm_nl_set_limits "${ns1}" "${2}" "${3}" 2>/dev/null || + ! pm_nl_set_limits "${ns2}" "${4}" "${5}" 2>/dev/null; then + mark_as_skipped "unable to set the limits to ${*:2}" + return 1 + fi +} + # $1: err msg fail_test() { @@ -1696,6 +1714,9 @@ chk_add_nr() local ack_nr=$port_nr local mis_syn_nr=0 local mis_ack_nr=0 + local add_tx_nr=${add_addr_tx_nr:-${add_nr}} + local echo_tx_nr=${add_addr_echo_tx_nr:-${echo_nr}} + local drop_tx_nr=${add_addr_drop_tx_nr:-0} local ns_tx=$ns1 local ns_rx=$ns2 local tx="" @@ -1797,50 +1818,25 @@ chk_add_nr() print_ok fi fi -} -chk_add_tx_nr() -{ - local add_tx_nr=$1 - local echo_tx_nr=$2 - local count - - print_check "add addr tx" - count=$(mptcp_lib_get_counter ${ns1} "MPTcpExtAddAddrTx") - if [ -z "$count" ]; then - print_skip + count=$(mptcp_lib_get_counter ${ns_tx} "MPTcpExtAddAddrTx") # Tolerate more ADD_ADDR then expected (if any), due to retransmissions - elif [ "$count" != "$add_tx_nr" ] && - { [ "$add_tx_nr" -eq 0 ] || [ "$count" -lt "$add_tx_nr" ]; }; then + if [ -n "$count" ] && [ "$count" != "$add_tx_nr" ] && + { [ "$add_tx_nr" -eq 0 ] || [ "$count" -lt "$add_tx_nr" ]; }; then + print_check "add addr tx" fail_test "got $count ADD_ADDR[s] TX, expected $add_tx_nr" - else - print_ok fi - print_check "add addr echo tx" - count=$(mptcp_lib_get_counter ${ns2} "MPTcpExtEchoAddTx") - if [ -z "$count" ]; then - print_skip - elif [ "$count" != "$echo_tx_nr" ]; then + count=$(mptcp_lib_get_counter ${ns_rx} "MPTcpExtEchoAddTx") + if [ -n "$count" ] && [ "$count" != "$echo_tx_nr" ]; then + print_check "add addr echo tx" fail_test "got $count ADD_ADDR echo[s] TX, expected $echo_tx_nr" - else - print_ok fi -} -chk_add_drop_tx_nr() -{ - local drop_tx_nr=$1 - local count - - print_check "add addr tx drop" - count=$(mptcp_lib_get_counter ${ns1} "MPTcpExtAddAddrTxDrop") - if [ -z "$count" ]; then - print_skip - elif [ "$count" != "$drop_tx_nr" ]; then + count=$(mptcp_lib_get_counter ${ns_tx} "MPTcpExtAddAddrTxDrop") + if [ -n "$count" ] && [ "$count" != "$drop_tx_nr" ]; then + print_check "add addr tx drop" fail_test "got $count ADD_ADDR drop[s] TX, expected $drop_tx_nr" - else - print_ok fi } @@ -2253,7 +2249,6 @@ signal_address_tests() 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_tx_nr 1 1 chk_add_nr 1 1 fi @@ -2531,8 +2526,8 @@ add_addr_timeout_tests() speed=slow \ run_tests $ns1 $ns2 10.0.1.1 chk_join_nr 1 1 1 - chk_add_tx_nr 4 4 - chk_add_nr 4 0 + add_addr_echo_tx_nr=4 \ + chk_add_nr 4 0 fi # add_addr timeout IPv6 @@ -2543,7 +2538,8 @@ add_addr_timeout_tests() speed=slow \ run_tests $ns1 $ns2 dead:beef:1::1 chk_join_nr 1 1 1 - chk_add_nr 4 0 + add_addr_echo_tx_nr=4 \ + chk_add_nr 4 0 fi # signal addresses timeout @@ -2555,7 +2551,8 @@ add_addr_timeout_tests() speed=10 \ run_tests $ns1 $ns2 10.0.1.1 chk_join_nr 2 2 2 - chk_add_nr 8 0 + add_addr_echo_tx_nr=8 \ + chk_add_nr 8 0 fi # signal invalid addresses timeout @@ -2568,7 +2565,8 @@ add_addr_timeout_tests() run_tests $ns1 $ns2 10.0.1.1 join_syn_tx=2 \ chk_join_nr 1 1 1 - chk_add_nr 8 0 + add_addr_echo_tx_nr=7 \ + chk_add_nr 8 0 fi } @@ -3200,6 +3198,17 @@ add_addr_ports_tests() chk_add_nr 1 1 1 fi + # signal address v6 with port + if reset "signal address v6 with port" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/add_addr_v6_port_drop_ts'; 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 port 10100 + run_tests $ns1 $ns2 dead:beef:1::1 + chk_join_nr 1 1 1 + chk_add_nr 1 1 1 + fi + # subflow and signal with port if reset "subflow and signal with port"; then pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 @@ -3299,15 +3308,15 @@ add_addr_ports_tests() if reset "signal addr list progresses after tx drop"; then pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 1 0 + ip netns exec $ns1 sysctl -q net.mptcp.add_addr_v6_port_drop_ts=0 2>/dev/null || true ip netns exec $ns1 sysctl -q net.ipv4.tcp_timestamps=1 ip netns exec $ns2 sysctl -q net.ipv4.tcp_timestamps=1 pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal port 10100 pm_nl_add_endpoint $ns1 dead:beef:3::1 flags signal run_tests $ns1 $ns2 dead:beef:1::1 - chk_add_drop_tx_nr 1 - chk_add_tx_nr 1 1 - chk_add_nr 1 1 0 + add_addr_drop_tx_nr=1 \ + chk_add_nr 1 1 0 fi } @@ -3700,6 +3709,21 @@ fullmesh_tests() chk_prio_nr 0 1 1 0 chk_rm_nr 0 1 fi + + # fullmesh in 8x8 to create 63 additional subflows + if ifaces_nr=8 reset_with_high_limits "fullmesh 8x8" 64 64 64 64; then + # higher chance to lose ADD_ADDR: allow retransmissions + ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 + local i + for i in $(seq 1 8); do + pm_nl_add_endpoint $ns2 10.0.$i.2 flags subflow,fullmesh + pm_nl_add_endpoint $ns1 10.0.$i.1 flags signal + done + speed=slow \ + run_tests $ns1 $ns2 10.0.1.1 + chk_join_nr 63 63 63 + fi + } fastclose_tests() diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index ab8bce06b262..e850a87429b6 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -355,10 +355,10 @@ sin=$(mktemp) sout=$(mktemp) cin=$(mktemp) cout=$(mktemp) +trap cleanup EXIT init make_file "$cin" "client" 1 make_file "$sin" "server" 1 -trap cleanup EXIT mptcp_lib_subtests_last_ts_reset run_tests $ns1 $ns2 10.0.1.1 diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 04594dfc22b1..21bfe1311f11 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -66,6 +66,15 @@ get_limits() { fi } +get_limits_nb() { + if mptcp_lib_is_ip_mptcp; then + ip -n "${ns1}" mptcp limits | awk '{ print $2" "$4 }' + else + ip netns exec "${ns1}" ./pm_nl_ctl limits | \ + awk '{ printf "%s ", $2 }' + fi +} + format_endpoints() { mptcp_lib_pm_nl_format_endpoints "${@}" } @@ -164,6 +173,7 @@ check "get_endpoint 2" "" "simple del addr" 1 check "show_endpoints" \ "$(format_endpoints "1,10.0.1.1" \ "3,10.0.1.3,signal backup")" "dump addrs after del" +add_endpoint 10.0.1.2 id 2 add_endpoint 10.0.1.3 2>/dev/null check "get_endpoint 4" "" "duplicate addr" 1 @@ -171,25 +181,29 @@ check "get_endpoint 4" "" "duplicate addr" 1 add_endpoint 10.0.1.4 flags signal check "get_endpoint 4" "$(format_endpoints "4,10.0.1.4,signal")" "id addr increment" -for i in $(seq 5 9); do - add_endpoint "10.0.1.${i}" flags signal >/dev/null 2>&1 -done -check "get_endpoint 9" "$(format_endpoints "9,10.0.1.9,signal")" "hard addr limit" -check "get_endpoint 10" "" "above hard addr limit" 1 +read -r -a default_limits_nb <<< "$(get_limits_nb)" +# limits have been increased: from 8 to 64 for subflows/add_addr & 255 for endp +if mptcp_lib_expect_all_features || set_limits 9 9 2>/dev/null; then + max_endp=255 + max_limits=64 +else + max_endp=8 + max_limits=8 +fi +set_limits "${default_limits_nb[@]}" -del_endpoint 9 -for i in $(seq 10 255); do - add_endpoint 10.0.0.9 id "${i}" - del_endpoint "${i}" +for i in $(seq 5 ${max_endp}); do + add_endpoint "10.0.0.${i}" id "${i}" done -check "show_endpoints" \ - "$(format_endpoints "1,10.0.1.1" \ - "3,10.0.1.3,signal backup" \ - "4,10.0.1.4,signal" \ - "5,10.0.1.5,signal" \ - "6,10.0.1.6,signal" \ - "7,10.0.1.7,signal" \ - "8,10.0.1.8,signal")" "id limit" +check "get_endpoint ${max_endp}" \ + "$(format_endpoints "${max_endp},10.0.0.${max_endp}")" "id limit" + +if add_endpoint '10.0.0.1' &>/dev/null; then + hardlimit="no error" +else + hardlimit="error" +fi +check "echo ${hardlimit}" "error" "above hard addr limit" flush_endpoint check "show_endpoints" "" "flush addrs" @@ -202,15 +216,15 @@ if ! mptcp_lib_is_ip_mptcp; then flush_endpoint fi -set_limits 9 1 2>/dev/null +set_limits $((max_limits + 1)) 1 2>/dev/null check "get_limits" "${default_limits}" "rcv addrs above hard limit" -set_limits 1 9 2>/dev/null +set_limits 1 $((max_limits + 1)) 2>/dev/null check "get_limits" "${default_limits}" "subflows above hard limit" -set_limits 8 8 +set_limits ${max_limits} ${max_limits} flush_endpoint ## to make sure it doesn't affect the limits -check "get_limits" "$(format_limits 8 8)" "set limits" +check "get_limits" "$(format_limits ${max_limits} ${max_limits})" "set limits" flush_endpoint add_endpoint 10.0.1.1 diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c index 99eecccbf0c8..78180da1efcc 100644 --- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c +++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c @@ -217,8 +217,6 @@ static int capture_events(int fd, int event_group) /* do a netlink command and, if max > 0, fetch the reply ; nh's size >1024B */ static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max) { - struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; - socklen_t addr_len; void *data = nh; int rem, ret; int err = 0; @@ -230,15 +228,15 @@ static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max) } nh->nlmsg_len = len; - ret = sendto(fd, data, len, 0, (void *)&nladdr, sizeof(nladdr)); + ret = send(fd, data, len, 0); if (ret != len) error(1, errno, "send netlink: %uB != %uB\n", ret, len); - addr_len = sizeof(nladdr); - rem = ret = recvfrom(fd, data, max, 0, (void *)&nladdr, &addr_len); + ret = recv(fd, data, max, 0); if (ret < 0) error(1, errno, "recv netlink: %uB\n", ret); + rem = ret; /* Beware: the NLMSG_NEXT macro updates the 'rem' argument */ for (; NLMSG_OK(nh, rem); nh = NLMSG_NEXT(nh, rem)) { if (nh->nlmsg_type == NLMSG_DONE) diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh index d11a8b949aab..7b9aabe10170 100755 --- a/tools/testing/selftests/net/mptcp/simult_flows.sh +++ b/tools/testing/selftests/net/mptcp/simult_flows.sh @@ -76,13 +76,13 @@ setup() ip -net "$ns1" addr add 10.0.1.1/24 dev ns1eth1 ip -net "$ns1" addr add dead:beef:1::1/64 dev ns1eth1 nodad - ip -net "$ns1" link set ns1eth1 up mtu 1500 + ip -net "$ns1" link set ns1eth1 up mtu 1500 gso_max_segs 0 ip -net "$ns1" route add default via 10.0.1.2 ip -net "$ns1" route add default via dead:beef:1::2 ip -net "$ns1" addr add 10.0.2.1/24 dev ns1eth2 ip -net "$ns1" addr add dead:beef:2::1/64 dev ns1eth2 nodad - ip -net "$ns1" link set ns1eth2 up mtu 1500 + ip -net "$ns1" link set ns1eth2 up mtu 1500 gso_max_segs 0 ip -net "$ns1" route add default via 10.0.2.2 metric 101 ip -net "$ns1" route add default via dead:beef:2::2 metric 101 @@ -91,21 +91,21 @@ setup() ip -net "$ns2" addr add 10.0.1.2/24 dev ns2eth1 ip -net "$ns2" addr add dead:beef:1::2/64 dev ns2eth1 nodad - ip -net "$ns2" link set ns2eth1 up mtu 1500 + ip -net "$ns2" link set ns2eth1 up mtu 1500 gso_max_segs 0 ip -net "$ns2" addr add 10.0.2.2/24 dev ns2eth2 ip -net "$ns2" addr add dead:beef:2::2/64 dev ns2eth2 nodad - ip -net "$ns2" link set ns2eth2 up mtu 1500 + ip -net "$ns2" link set ns2eth2 up mtu 1500 gso_max_segs 0 ip -net "$ns2" addr add 10.0.3.2/24 dev ns2eth3 ip -net "$ns2" addr add dead:beef:3::2/64 dev ns2eth3 nodad - ip -net "$ns2" link set ns2eth3 up mtu 1500 + ip -net "$ns2" link set ns2eth3 up mtu 1500 gso_max_segs 0 ip netns exec "$ns2" sysctl -q net.ipv4.ip_forward=1 ip netns exec "$ns2" sysctl -q net.ipv6.conf.all.forwarding=1 ip -net "$ns3" addr add 10.0.3.3/24 dev ns3eth1 ip -net "$ns3" addr add dead:beef:3::3/64 dev ns3eth1 nodad - ip -net "$ns3" link set ns3eth1 up mtu 1500 + ip -net "$ns3" link set ns3eth1 up mtu 1500 gso_max_segs 0 ip -net "$ns3" route add default via 10.0.3.2 ip -net "$ns3" route add default via dead:beef:3::2 @@ -223,9 +223,11 @@ run_test() local rate2=$2 local delay1=$3 local delay2=$4 + local limit1=$5 + local limit2=$6 local lret local dev - shift 4 + shift 6 local msg=$* [ $delay1 -gt 0 ] && delay1="delay ${delay1}ms" || delay1="" @@ -240,10 +242,10 @@ run_test() # keep the queued pkts number low, or the RTT estimator will see # increasing latency over time. - tc -n $ns1 qdisc add dev ns1eth1 root netem rate ${rate1}mbit $delay1 limit 50 - tc -n $ns1 qdisc add dev ns1eth2 root netem rate ${rate2}mbit $delay2 limit 50 - tc -n $ns2 qdisc add dev ns2eth1 root netem rate ${rate1}mbit $delay1 limit 50 - tc -n $ns2 qdisc add dev ns2eth2 root netem rate ${rate2}mbit $delay2 limit 50 + tc -n $ns1 qdisc add dev ns1eth1 root netem rate ${rate1}mbit $delay1 limit ${limit1} + tc -n $ns1 qdisc add dev ns1eth2 root netem rate ${rate2}mbit $delay2 limit ${limit2} + tc -n $ns2 qdisc add dev ns2eth1 root netem rate ${rate1}mbit $delay1 limit ${limit1} + tc -n $ns2 qdisc add dev ns2eth2 root netem rate ${rate2}mbit $delay2 limit ${limit2} # time is measured in ms, account for transfer size, aggregated link speed # and header overhead (10%) @@ -301,13 +303,13 @@ done setup mptcp_lib_subtests_last_ts_reset -run_test 10 10 0 0 "balanced bwidth" -run_test 10 10 1 25 "balanced bwidth with unbalanced delay" +run_test 10 10 0 0 20 20 "balanced bwidth" +run_test 10 10 1 25 20 50 "balanced bwidth with unbalanced delay" # we still need some additional infrastructure to pass the following test-cases -MPTCP_LIB_SUBTEST_FLAKY=1 run_test 10 3 0 0 "unbalanced bwidth" -run_test 10 3 1 25 "unbalanced bwidth with unbalanced delay" -run_test 10 3 25 1 "unbalanced bwidth with opposed, unbalanced delay" +MPTCP_LIB_SUBTEST_FLAKY=1 run_test 10 3 0 0 30 20 "unbalanced bwidth" +run_test 10 3 1 25 40 30 "unbalanced bwidth with unbalanced delay" +run_test 10 3 25 1 50 30 "unbalanced bwidth with opposed, unbalanced delay" mptcp_lib_result_print_all_tap exit $ret diff --git a/tools/testing/selftests/net/netfilter/Makefile b/tools/testing/selftests/net/netfilter/Makefile index d953ee218c0f..f88dd4ef8d26 100644 --- a/tools/testing/selftests/net/netfilter/Makefile +++ b/tools/testing/selftests/net/netfilter/Makefile @@ -32,6 +32,7 @@ TEST_PROGS := \ nft_meta.sh \ nft_nat.sh \ nft_nat_zones.sh \ + nft_offload.sh \ nft_queue.sh \ nft_synproxy.sh \ nft_tproxy_tcp.sh \ diff --git a/tools/testing/selftests/net/netfilter/config b/tools/testing/selftests/net/netfilter/config index 979cff56e1f5..c3c121b6f300 100644 --- a/tools/testing/selftests/net/netfilter/config +++ b/tools/testing/selftests/net/netfilter/config @@ -11,7 +11,12 @@ CONFIG_BRIDGE_NF_EBTABLES_LEGACY=m CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_CGROUP_BPF=y CONFIG_CRYPTO_SHA1=m +CONFIG_DEBUG_FS=y CONFIG_DUMMY=m +CONFIG_FAIL_FUNCTION=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FUNCTION_ERROR_INJECTION=y CONFIG_INET_DIAG=m CONFIG_INET_ESP=m CONFIG_INET_SCTP_DIAG=m @@ -36,6 +41,7 @@ CONFIG_IP_VS_RR=m CONFIG_MACVLAN=m CONFIG_NAMESPACES=y CONFIG_NET_CLS_U32=m +CONFIG_NETDEVSIM=m CONFIG_NETFILTER=y CONFIG_NETFILTER_ADVANCED=y CONFIG_NETFILTER_NETLINK=m diff --git a/tools/testing/selftests/net/netfilter/nft_offload.sh b/tools/testing/selftests/net/netfilter/nft_offload.sh new file mode 100755 index 000000000000..859bdedf1a51 --- /dev/null +++ b/tools/testing/selftests/net/netfilter/nft_offload.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source lib.sh + +checktool "nft --version" "run test without nft tool" +modprobe -q netdevsim + +sysfs="/sys/kernel/debug/fail_function" +failname="/proc/self/make-it-fail" +duration=30 +fault=0 +ret=0 +file_ft="" +file_rs="" +id=$((RANDOM%65536)) + +read -r t < /proc/sys/kernel/tainted +if [ "$t" -ne 0 ];then + echo SKIP: kernel is tainted + exit $ksft_skip +fi + +cleanup() { + cleanup_netdevsim "$id" "$NS" + cleanup_ns "$NS" + [ "$fault" -eq 1 ] && echo '!nsim_setup_tc' > "$sysfs/inject" + rm -f "$file_ft" "$file_rs" +} +trap cleanup EXIT + +skip() { + echo "SKIP: $*" + [ $ret -eq 0 ] && exit 4 + + exit $ret +} + +set -e +setup_ns NS + +create_netdevsim "$id" "$NS" >/dev/null +nsim_port=$(create_netdevsim_port "$id" "$NS" 2) + +file_ft=$(mktemp) +cat > "$file_ft" < "$file_rs" < "$sysfs/task-filter" +echo 0 > "$sysfs/verbose" +echo "nsim_setup_tc" > "$sysfs/inject" +fault=1 + +p=$(((RANDOM%90) + 10)) +echo $p > "$sysfs/probability" +echo -1 > "$sysfs/times" + +count=0 +ok=0 + +now=$(date +%s) +stop=$((now+duration)) + +# fault-injection enabled rule loads are expected to fail. +set +e +while [ "$now" -le "$stop" ]; do + for f in "$file_ft" "$file_rs"; do + if ip netns exec "$NS" bash -c "echo 1 > $failname ; ip netns exec \"$NS\" nft -f $f" 2> /dev/null;then + ok=$((ok+1)) + fi + count=$((count+1)) + done + now=$(date +%s) +done + +sleep 5 + +read -r t < /proc/sys/kernel/tainted +if [ "$t" -eq 0 ];then + echo "PASS: Not tainted. $count rounds, $ok successful ruleset loads with P $p." +else + echo "ERROR: Tainted. $count rounds, $ok successful ruleset loads with P $p." + dmesg + ret=1 +fi + +exit $ret diff --git a/tools/testing/selftests/net/nl_netdev.py b/tools/testing/selftests/net/nl_netdev.py index eff55c64a012..ceb44c8e1fec 100755 --- a/tools/testing/selftests/net/nl_netdev.py +++ b/tools/testing/selftests/net/nl_netdev.py @@ -9,7 +9,7 @@ import errno from os import system from lib.py import ksft_run, ksft_exit from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_raises, ksft_busy_wait -from lib.py import NetdevFamily, NetdevSimDev, NlError, ip +from lib.py import NetdevFamily, NetdevSimDev, NlError, defer, ip def empty_check(nf) -> None: @@ -255,6 +255,117 @@ def page_pool_check(nf) -> None: nsim.dfs_write("pp_hold", "y") +def page_pool_dump_ifindex(nf) -> None: + """Test page pool dump filtering by ifindex.""" + nsimdev1 = NetdevSimDev(queue_count=3) + rm_nsim1 = defer(nsimdev1.remove) + nsimdev2 = NetdevSimDev(queue_count=5) + defer(nsimdev2.remove) + + nsim1 = nsimdev1.nsims[0] + nsim2 = nsimdev2.nsims[0] + + ip(f"link set dev {nsim1.ifname} up") + ip(f"link set dev {nsim2.ifname} up") + + # Unfiltered dump should have pools from both devices + all_pp = nf.page_pool_get({}, dump=True) + pp1_all = [pp for pp in all_pp + if pp.get("ifindex") == nsim1.ifindex] + pp2_all = [pp for pp in all_pp + if pp.get("ifindex") == nsim2.ifindex] + ksft_ge(len(pp1_all), 1) + ksft_ge(len(pp2_all), 1) + + # Filtered dump should only return pools for that device + pp1_flt = nf.page_pool_get({'ifindex': nsim1.ifindex}, dump=True) + ksft_eq(pp1_flt, pp1_all) + + pp2_flt = nf.page_pool_get({'ifindex': nsim2.ifindex}, dump=True) + ksft_eq(pp2_flt, pp2_all) + + # Non-existent ifindex should return empty dump + pp_none = nf.page_pool_get({'ifindex': 12345678}, dump=True) + ksft_eq(len(pp_none), 0) + + # Device down - no pools for that ifindex + ip(f"link set dev {nsim1.ifname} down") + pp1_down = nf.page_pool_get({'ifindex': nsim1.ifindex}, dump=True) + ksft_eq(len(pp1_down), 0) + + # Remove device, dump by its old ifindex should return empty + old_ifindex = nsim1.ifindex + rm_nsim1.exec() + pp1_gone = nf.page_pool_get({'ifindex': old_ifindex}, dump=True) + ksft_eq(len(pp1_gone), 0) + + +def page_pool_ifindex_leak_check(nf) -> None: + """Test that zombie page pools don't show up under the original ifindex.""" + nsimdev = NetdevSimDev() + rm_nsim = defer(nsimdev.remove) + nsim = nsimdev.nsims[0] + + ip(f"link set dev {nsim.ifname} up") + nsim.dfs_write("pp_hold", "y") + + pp_up = nf.page_pool_get({'ifindex': nsim.ifindex}, dump=True) + ksft_ge(len(pp_up), 1) + + # Remove device with leaked page - pool becomes zombie (orphaned to lo) + old_ifindex = nsim.ifindex + rm_nsim.exec() + + # Zombie pool should NOT appear under the original device + pp_down = nf.page_pool_get({'ifindex': old_ifindex}, dump=True) + ksft_eq(len(pp_down), 0) + + # But it should appear in an unfiltered dump (under loopback) + pp_all = nf.page_pool_get({}, dump=True) + orphans = [pp for pp in pp_all + if "detach-time" in pp and "ifindex" not in pp] + ksft_ge(len(orphans), 1) + + +def page_pool_stats_ifindex_check(nf) -> None: + """Test page pool stats dump filtering by ifindex.""" + nsimdev1 = NetdevSimDev(queue_count=3) + defer(nsimdev1.remove) + nsimdev2 = NetdevSimDev(queue_count=5) + defer(nsimdev2.remove) + + nsim1 = nsimdev1.nsims[0] + nsim2 = nsimdev2.nsims[0] + + ip(f"link set dev {nsim1.ifname} up") + ip(f"link set dev {nsim2.ifname} up") + + # Unfiltered stats dump + all_stats = nf.page_pool_stats_get({}, dump=True) + s1_all = [s for s in all_stats + if s.get("info", {}).get("ifindex") == nsim1.ifindex] + s2_all = [s for s in all_stats + if s.get("info", {}).get("ifindex") == nsim2.ifindex] + ksft_ge(len(s1_all), 1) + ksft_ge(len(s2_all), 1) + + # Filtered stats dump + s1_flt = nf.page_pool_stats_get({'info': {'ifindex': nsim1.ifindex}}, + dump=True) + ksft_eq(s1_flt, s1_all) + + # Non-existent ifindex should return empty + s_none = nf.page_pool_stats_get({'info': {'ifindex': 12345678}}, dump=True) + ksft_eq(len(s_none), 0) + + # info.id should be rejected for stats dump + with ksft_raises(NlError) as cm: + nf.page_pool_stats_get({'info': {'id': s1_all[0]['info']['id']}}, + dump=True) + ksft_eq(cm.exception.nl_msg.error, -errno.EINVAL) + ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.info.id') + + def main() -> None: """ Ksft boiler plate main """ nf = NetdevFamily() @@ -265,7 +376,11 @@ def main() -> None: napi_set_threaded, dev_set_threaded, nsim_rxq_reset_down, - page_pool_check], + page_pool_check, + page_pool_dump_ifindex, + page_pool_ifindex_leak_check, + page_pool_stats_ifindex_check + ], args=(nf, )) ksft_exit() diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh index 3cdd953f6813..2954245129a2 100755 --- a/tools/testing/selftests/net/openvswitch/openvswitch.sh +++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh @@ -28,6 +28,10 @@ tests=" tunnel_metadata ovs: test extraction of tunnel metadata tunnel_refcount ovs: test tunnel vport reference cleanup drop_reason drop: test drop reasons are emitted + pop_vlan vlan: POP_VLAN action strips tag + dec_ttl ttl: dec_ttl decrements IP TTL + flow_set flow-set: Flow modify + action_set set: SET action rewrites fields psample psample: Sampling packets with psample" info() { @@ -191,6 +195,23 @@ ovs_add_flow () { return 0 } +ovs_mod_flow () { + if [ -n "$4" ]; then + info "Modifying flow: sbx:$1 br:$2 flow:$3 act:$4" + ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py \ + mod-flow "$2" "$3" "$4" + else + info "Modifying flow (no actions): sbx:$1 br:$2 flow:$3" + ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py \ + mod-flow "$2" "$3" + fi + if [ $? -ne 0 ]; then + info "Flow modify [ $3 ] failed" + return 1 + fi + return 0 +} + ovs_del_flows () { info "Deleting all flows from DP: sbx:$1 br:$2" ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py del-flows "$2" @@ -244,6 +265,184 @@ usage() { } +test_dec_ttl() { + sbx_add "test_dec_ttl" || return $? + ovs_add_dp "test_dec_ttl" decttl || return 1 + + info "create namespaces" + for ns in client server; do + ovs_add_netns_and_veths "test_dec_ttl" "decttl" "$ns" \ + "${ns:0:1}0" "${ns:0:1}1" || return 1 + done + + ip netns exec client ip addr add 10.0.0.1/24 dev c1 + ip netns exec client ip link set c1 up + ip netns exec server ip addr add 10.0.0.2/24 dev s1 + ip netns exec server ip link set s1 up + + # Probe: check if kernel supports dec_ttl action. + ovs_add_flow "test_dec_ttl" decttl \ + 'in_port(1),eth(),eth_type(0x0800),ipv4()' \ + 'dec_ttl(le_1())' &>/dev/null + if [ $? -ne 0 ]; then + info "no support for dec_ttl - skipping" + ovs_exit_sig + return $ksft_skip + fi + + ovs_del_flows "test_dec_ttl" decttl + + # ARP flows (bidirectional) + ovs_add_flow "test_dec_ttl" decttl \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "test_dec_ttl" decttl \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + + # IP flows with dec_ttl action + ovs_add_flow "test_dec_ttl" decttl \ + 'in_port(1),eth(),eth_type(0x0800),ipv4()' \ + 'dec_ttl(le_1()),2' || return 1 + ovs_add_flow "test_dec_ttl" decttl \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' \ + 'dec_ttl(le_1()),1' || return 1 + + info "verify connectivity with dec_ttl" + ovs_sbx "test_dec_ttl" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 || return 1 + + info "verify TTL=1 is dropped by dec_ttl" + ovs_sbx "test_dec_ttl" ip netns exec client ping -c 1 -W 2 \ + -t 1 10.0.0.2 >/dev/null 2>&1 \ + && { info "FAIL: ping should fail with TTL=1 and dec_ttl" + return 1; } + + return 0 +} + +test_flow_set() { + sbx_add "test_flow_set" || return $? + ovs_add_dp "test_flow_set" flowset || return 1 + + info "create namespaces" + for ns in client server; do + ovs_add_netns_and_veths "test_flow_set" "flowset" "$ns" \ + "${ns:0:1}0" "${ns:0:1}1" || return 1 + done + + ip netns exec client ip addr add 10.0.0.1/24 dev c1 + ip netns exec client ip link set c1 up + ip netns exec server ip addr add 10.0.0.2/24 dev s1 + ip netns exec server ip link set s1 up + + ovs_add_flow "test_flow_set" flowset \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "test_flow_set" flowset \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + + local fwd_flow="ufid:00000001-0002-0003-0004-000500060007" + fwd_flow="$fwd_flow,in_port(1),eth(),eth_type(0x0800),ipv4()" + + ovs_add_flow "test_flow_set" flowset "$fwd_flow" '2' \ + || return 1 + ovs_add_flow "test_flow_set" flowset \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1 + + info "verify initial forwarding" + ovs_sbx "test_flow_set" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 || return 1 + + info "mod-flow with new actions (change to drop)" + ovs_mod_flow "test_flow_set" flowset "$fwd_flow" 'drop' \ + || return 1 + + info "verify traffic is now dropped" + ovs_sbx "test_flow_set" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 >/dev/null 2>&1 \ + && { info "FAIL: ping should fail after mod-flow to drop" + return 1; } + + info "mod-flow without actions" + ovs_mod_flow "test_flow_set" flowset "$fwd_flow" || return 1 + + info "verify flow retained drop action via dump" + python3 "$ovs_base/ovs-dpctl.py" dump-flows flowset \ + | grep -q "actions:drop" || \ + { info "FAIL: flow not showing drop action"; return 1; } + + info "verify drop actions unchanged" + ovs_sbx "test_flow_set" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 >/dev/null 2>&1 \ + && { info "FAIL: ping should still fail after no-actions set" + return 1; } + + return 0 +} + +test_action_set() { + sbx_add "test_action_set" || return $? + ovs_add_dp "test_action_set" settest || return 1 + + info "create namespaces" + for ns in client server; do + ovs_add_netns_and_veths "test_action_set" "settest" "$ns" \ + "${ns:0:1}0" "${ns:0:1}1" || return 1 + done + + ip netns exec client ip addr add 10.0.0.1/24 dev c1 + ip netns exec client ip link set c1 up + ip netns exec server ip addr add 10.0.0.2/24 dev s1 + ip netns exec server ip link set s1 up + + ovs_add_flow "test_action_set" settest \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + + ovs_add_flow "test_action_set" settest \ + 'in_port(1),eth(),eth_type(0x0800),ipv4()' '2' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1 + + info "verify connectivity without SET" + ovs_sbx "test_action_set" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 || return 1 + + ovs_del_flows "test_action_set" settest + ovs_add_flow "test_action_set" settest \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + + info "set ipv4 dst to unreachable address" + ovs_add_flow "test_action_set" settest \ + 'in_port(1),eth(),eth_type(0x0800),ipv4()' \ + 'set(ipv4(dst=10.0.0.99)),2' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1 + + info "verify ping fails with rewritten dst" + ovs_sbx "test_action_set" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 >/dev/null 2>&1 \ + && { info "FAIL: ping should fail with dst rewritten" + return 1; } + + ovs_del_flows "test_action_set" settest + ovs_add_flow "test_action_set" settest \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(1),eth(),eth_type(0x0800),ipv4()' '2' || return 1 + ovs_add_flow "test_action_set" settest \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1 + + info "verify connectivity restored without SET" + ovs_sbx "test_action_set" ip netns exec client ping -c 1 -W 2 \ + 10.0.0.2 || return 1 + + return 0 +} + # psample test # - use psample to observe packets test_psample() { @@ -304,6 +503,8 @@ test_psample() { # sFlow / IPFIX. nlpid=$(grep -E "listening on upcall packet handler" \ $ovs_dir/s0.out | cut -d ":" -f 2 | tr -d ' ') + [ -z "$nlpid" ] && \ + { info "failed to get upcall PID"; return 1; } ovs_add_flow "test_psample" psample \ "in_port(2),eth(),eth_type(0x0800),ipv4()" \ @@ -337,6 +538,10 @@ test_drop_reason() { ovs_drop_subsys=$(pahole -C skb_drop_reason_subsys | awk '/OPENVSWITCH/ { print $3; }' | tr -d ,) + if [ -z "$ovs_drop_subsys" ]; then + info "failed to get OVS drop subsys ID" + return $ksft_skip + fi sbx_add "test_drop_reason" || return $? @@ -435,13 +640,19 @@ test_arp_ping () { # Setup client namespace ip netns exec client ip addr add 172.31.110.10/24 dev c1 ip netns exec client ip link set c1 up - HW_CLIENT=`ip netns exec client ip link show dev c1 | grep -E 'link/ether [0-9a-f:]+' | awk '{print $2;}'` + HW_CLIENT=$(ip netns exec client ip link show dev c1 \ + | awk '/link\/ether/ {print $2}') + [ -z "$HW_CLIENT" ] && \ + { info "failed to get client hwaddr"; return 1; } info "Client hwaddr: $HW_CLIENT" # Setup server namespace ip netns exec server ip addr add 172.31.110.20/24 dev s1 ip netns exec server ip link set s1 up - HW_SERVER=`ip netns exec server ip link show dev s1 | grep -E 'link/ether [0-9a-f:]+' | awk '{print $2;}'` + HW_SERVER=$(ip netns exec server ip link show dev s1 \ + | awk '/link\/ether/ {print $2}') + [ -z "$HW_SERVER" ] && \ + { info "failed to get server hwaddr"; return 1; } info "Server hwaddr: $HW_SERVER" ovs_add_flow "test_arp_ping" arpping \ @@ -864,6 +1075,83 @@ test_tunnel_refcount() { ovs_wait dev_removed dp-${tun_type} || return 1 ovs_wait dev_removed ovs-${tun_type}0 || return 1 done + + return 0 +} + +test_pop_vlan() { + local sbx="test_pop_vlan" + sbx_add "$sbx" || return $? + ovs_add_dp "$sbx" vlandp || return 1 + + ovs_add_netns_and_veths "$sbx" vlandp \ + ns1 veth1 ns1veth 192.0.2.1/24 || return 1 + ovs_add_netns_and_veths "$sbx" vlandp \ + ns2 veth2 ns2veth 192.0.2.2/24 || return 1 + + # Baseline: untagged bidirectional forwarding + ovs_add_flow "$sbx" vlandp \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "$sbx" vlandp \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + ovs_add_flow "$sbx" vlandp \ + 'in_port(1),eth(),eth_type(0x0800),ipv4()' '2' || return 1 + ovs_add_flow "$sbx" vlandp \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1 + ovs_sbx "$sbx" ip netns exec ns1 ping -c 3 -W 2 \ + 192.0.2.2 || return 1 + + # VLAN topology: ns1 uses VLAN sub-interface, ns2 is plain + ip -n ns1 link add link ns1veth name ns1veth.10 \ + type vlan id 10 || return 1 + on_exit "ip -n ns1 link del ns1veth.10 2>/dev/null" + ip -n ns1 addr add 198.51.100.1/24 dev ns1veth.10 || return 1 + ip -n ns1 link set ns1veth.10 up || return 1 + ip -n ns2 addr add 198.51.100.2/24 dev ns2veth || return 1 + + ovs_del_flows "$sbx" vlandp + + # Static ARP: avoids VLAN-tagged ARP complexity + local ns1veth10mac ns2mac + ns1veth10mac=$(ip -n ns1 link show ns1veth.10 \ + | awk '/link\/ether/ {print $2}') + [ -z "$ns1veth10mac" ] && \ + { info "failed to get ns1veth10mac"; return 1; } + ns2mac=$(ip -n ns2 link show ns2veth \ + | awk '/link\/ether/ {print $2}') + [ -z "$ns2mac" ] && \ + { info "failed to get ns2mac"; return 1; } + ip -n ns1 neigh replace 198.51.100.2 lladdr "$ns2mac" \ + dev ns1veth.10 nud permanent || return 1 + ip -n ns2 neigh replace 198.51.100.1 \ + lladdr "$ns1veth10mac" \ + dev ns2veth nud permanent || return 1 + + local vlan_match='in_port(1),eth(),eth_type(0x8100),' + vlan_match+='vlan(vid=10),' + vlan_match+='encap(eth_type(0x0800),' + vlan_match+='ipv4(src=198.51.100.1,proto=1),icmp())' + + # Negative: forward without pop_vlan -- tagged frame + # is invisible to ns2 (no VLAN sub-interface), ping fails + ovs_add_flow "$sbx" vlandp "$vlan_match" '2' || return 1 + ovs_sbx "$sbx" ip netns exec ns1 ping -I ns1veth.10 \ + -c 3 -W 1 198.51.100.2 >/dev/null 2>&1 \ + && { info "FAIL: ping should fail without pop_vlan" + return 1; } + + ovs_del_flows "$sbx" vlandp + + # Positive: pop_vlan strips tag on forward path, + # push_vlan restores tag on return path -- ping succeeds + ovs_add_flow "$sbx" vlandp \ + "$vlan_match" 'pop_vlan,2' || return 1 + ovs_add_flow "$sbx" vlandp \ + 'in_port(2),eth(),eth_type(0x0800),ipv4()' \ + 'push_vlan(vid=10,pcp=0,tpid=0x8100),1' || return 1 + ovs_sbx "$sbx" ip netns exec ns1 ping -I ns1veth.10 \ + -c 3 -W 2 198.51.100.2 || return 1 + return 0 } diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py index bbe35e2718d2..e1ecfad2c03e 100644 --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -369,7 +369,7 @@ class ovsactions(nla): ("OVS_ACTION_ATTR_OUTPUT", "uint32"), ("OVS_ACTION_ATTR_USERSPACE", "userspace"), ("OVS_ACTION_ATTR_SET", "ovskey"), - ("OVS_ACTION_ATTR_PUSH_VLAN", "none"), + ("OVS_ACTION_ATTR_PUSH_VLAN", "push_vlan"), ("OVS_ACTION_ATTR_POP_VLAN", "flag"), ("OVS_ACTION_ATTR_SAMPLE", "sample"), ("OVS_ACTION_ATTR_RECIRC", "uint32"), @@ -388,11 +388,21 @@ class ovsactions(nla): ("OVS_ACTION_ATTR_CLONE", "recursive"), ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"), ("OVS_ACTION_ATTR_ADD_MPLS", "none"), - ("OVS_ACTION_ATTR_DEC_TTL", "none"), + ("OVS_ACTION_ATTR_DEC_TTL", "dec_ttl"), ("OVS_ACTION_ATTR_DROP", "uint32"), ("OVS_ACTION_ATTR_PSAMPLE", "psample"), ) + class dec_ttl(nla): # pylint: disable=invalid-name + """Nested OVS_DEC_TTL_ATTR_* sub-attributes.""" + + nla_flags = NLA_F_NESTED + + nla_map = ( + ("OVS_DEC_TTL_ATTR_UNSPEC", "none"), + ("OVS_DEC_TTL_ATTR_ACTION", "actions"), + ) + class psample(nla): nla_flags = NLA_F_NESTED @@ -426,6 +436,9 @@ class ovsactions(nla): return actstr + class push_vlan(nla): + fields = (("vlan_tpid", "!H"), ("vlan_tci", "!H")) + class sample(nla): nla_flags = NLA_F_NESTED @@ -632,6 +645,21 @@ class ovsactions(nla): print_str += "ct_clear" elif field[0] == "OVS_ACTION_ATTR_POP_VLAN": print_str += "pop_vlan" + elif field[0] == "OVS_ACTION_ATTR_DEC_TTL": + datum = self.get_attr(field[0]) + print_str += "dec_ttl(le_1(" + subacts = datum.get_attr("OVS_DEC_TTL_ATTR_ACTION") + if subacts and subacts.get("attrs"): + print_str += subacts.dpstr(more) + print_str += "))" + elif field[0] == "OVS_ACTION_ATTR_PUSH_VLAN": + datum = self.get_attr(field[0]) + tpid = datum["vlan_tpid"] + tci = datum["vlan_tci"] + vid = tci & 0x0FFF + pcp = (tci >> 13) & 0x7 + print_str += "push_vlan(vid=%d,pcp=%d" \ + ",tpid=0x%04x)" % (vid, pcp, tpid) elif field[0] == "OVS_ACTION_ATTR_POP_ETH": print_str += "pop_eth" elif field[0] == "OVS_ACTION_ATTR_POP_NSH": @@ -725,7 +753,71 @@ class ovsactions(nla): actstr = actstr[strspn(actstr, ", ") :] parsed = True - if parse_starts_block(actstr, "clone(", False): + if parse_starts_block(actstr, "push_vlan(", False): + actstr = actstr[len("push_vlan("):] + vid = 0 + pcp = 0 + tpid = 0x8100 + if ")" not in actstr: + raise ValueError( + "push_vlan(): missing ')'") + paren = actstr.index(")") + if not actstr[:paren].strip(): + raise ValueError("push_vlan(): no fields") + for kv in actstr[:paren].split(","): + if "=" not in kv: + raise ValueError( + "push_vlan(): bad field '%s'" + % kv.strip()) + k = kv[:kv.index("=")].strip() + v = kv[kv.index("=") + 1:].strip() + if k == "vid": + vid = int(v, 0) + if vid < 0 or vid > 0xFFF: + raise ValueError( + "push_vlan(): vid=%d out of " + "range (0-4095)" % vid) + elif k == "pcp": + pcp = int(v, 0) + if pcp < 0 or pcp > 7: + raise ValueError( + "push_vlan(): pcp=%d out of " + "range (0-7)" % pcp) + elif k == "tpid": + tpid = int(v, 0) + if tpid < 0 or tpid > 0xFFFF: + raise ValueError( + "push_vlan(): tpid=0x%x out " + "of range (0-0xffff)" % tpid) + else: + raise ValueError( + "push_vlan(): unknown key '%s'" + % k) + tci = (vid & 0x0FFF) | ((pcp & 0x7) << 13) \ + | 0x1000 + pvact = self.push_vlan() + pvact["vlan_tpid"] = tpid + pvact["vlan_tci"] = tci + self["attrs"].append( + ["OVS_ACTION_ATTR_PUSH_VLAN", pvact]) + actstr = actstr[paren + 1:] + parsed = True + + elif parse_starts_block(actstr, "dec_ttl(le_1(", False): + parencount += 2 + subacts = ovsactions() + actstr = actstr[len("dec_ttl(le_1("):] + parsed_len = subacts.parse(actstr) + decttl = ovsactions.dec_ttl() + decttl["attrs"].append( + ("OVS_DEC_TTL_ATTR_ACTION", subacts) + ) + self["attrs"].append( + ("OVS_ACTION_ATTR_DEC_TTL", decttl) + ) + actstr = actstr[parsed_len:] + parsed = True + elif parse_starts_block(actstr, "clone(", False): parencount += 1 subacts = ovsactions() actstr = actstr[len("clone("):] @@ -896,15 +988,21 @@ class ovsactions(nla): return (totallen - len(actstr)) +# pyroute2 resolves nla_map types via getattr(self, name). +# dec_ttl needs "actions" to resolve to ovsactions, but +# ovsactions is not defined when dec_ttl class body runs. +ovsactions.dec_ttl.actions = ovsactions + + class ovskey(nla): nla_flags = NLA_F_NESTED nla_map = ( ("OVS_KEY_ATTR_UNSPEC", "none"), - ("OVS_KEY_ATTR_ENCAP", "none"), + ("OVS_KEY_ATTR_ENCAP", "encap_ovskey"), ("OVS_KEY_ATTR_PRIORITY", "uint32"), ("OVS_KEY_ATTR_IN_PORT", "uint32"), ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), - ("OVS_KEY_ATTR_VLAN", "uint16"), + ("OVS_KEY_ATTR_VLAN", "be16"), ("OVS_KEY_ATTR_ETHERTYPE", "be16"), ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), @@ -1635,6 +1733,194 @@ class ovskey(nla): class ovs_key_mpls(nla): fields = (("lse", ">I"),) + # 802.1Q CFI (Canonical Format Indicator) bit, always set for Ethernet + _VLAN_CFI_MASK = 0x1000 + + @staticmethod + def _vlan_dpstr(tci): + """Format VLAN TCI as vid=X,pcp=Y,cfi=Z or tci=0xNNNN. + + When cfi=1 (standard Ethernet VLAN), outputs decomposed + vid/pcp/cfi fields. When cfi=0 (truncated VLAN header), + falls back to raw tci=0x%04x to ensure round-trip + correctness: the parser auto-adds cfi=1 for vid/pcp + format, so cfi=0 would be lost on re-parse.""" + vid = tci & 0x0FFF + pcp = (tci >> 13) & 0x7 + cfi = (tci >> 12) & 0x1 + if cfi: + return "vid=%d,pcp=%d,cfi=%d" % (vid, pcp, cfi) + return "tci=0x%04x" % tci + + @staticmethod + def _parse_vlan_from_flowstr(flowstr): + """Parse vlan(tci=X) or vlan(vid=X[,pcp=Y,cfi=Z]) from flowstr. + + Returns (remaining_flowstr, key_tci, mask_tci). + TCI values use standard bit layout (VID bits 0-11, + CFI bit 12, PCP bits 13-15); byte order conversion to + big-endian happens in pyroute2 be16 NLA serialization. + The mask covers only the fields the caller specified: + vid -> 0x0FFF, pcp -> 0xE000, cfi -> 0x1000, tci -> 0xFFFF. + + The tci= key sets the raw TCI bitfield (no CFI validation) to allow + non-Ethernet use cases. Use cfi=1 for standard Ethernet VLAN matching. + """ + tci = 0 + mask = 0 + has_tci = False + has_vid = has_pcp = has_cfi = False + _tci_mix_err = "vlan(): 'tci' cannot be mixed " \ + "with 'vid'/'pcp'/'cfi'" + first = True + while True: + flowstr = flowstr.lstrip() + if not flowstr: + raise ValueError("vlan(): missing ')'") + if flowstr[0] == ')': + break + if not first: + flowstr = flowstr[1:] # skip ',' + if not flowstr: + raise ValueError("vlan(): missing ')' after trailing comma") + flowstr = flowstr.lstrip() + if flowstr and flowstr[0] == ')': + break + if flowstr and flowstr[0] == ',': + raise ValueError( + "vlan(): empty or extra comma in field list") + first = False + + eq = flowstr.find('=') + if eq == -1: + raise ValueError( + "vlan(): expected key=value, got '%s'" % flowstr) + key = flowstr[:eq].strip() + flowstr = flowstr[eq + 1:] + + end = flowstr.find(',') + end2 = flowstr.find(')') + if end == -1 and end2 == -1: + raise ValueError("vlan(): missing ')'") + if end == -1 or (end2 != -1 and end2 < end): + end = end2 + val = flowstr[:end].strip() + flowstr = flowstr[end:] + + if not val: + raise ValueError("vlan(): empty value for key '%s'" % key) + try: + v = int(val, 0) + except ValueError as exc: + raise ValueError( + "vlan(): invalid value '%s' for key '%s'" + % (val, key)) from exc + + if key == 'tci': + if has_tci: + raise ValueError("vlan(): duplicate 'tci'") + if has_vid or has_pcp or has_cfi: + raise ValueError(_tci_mix_err) + if v > 0xFFFF or v < 0: + raise ValueError("vlan(): tci=0x%x out of range" % v) + tci = v + mask = 0xFFFF + has_tci = True + elif key == 'vid': + if has_tci: + raise ValueError(_tci_mix_err) + if has_vid: + raise ValueError("vlan(): duplicate 'vid'") + if v < 0 or v > 0xFFF: + raise ValueError("vlan(): vid=%d out of range (0-4095)" % v) + tci |= v + mask |= 0x0FFF + has_vid = True + elif key == 'pcp': + if has_tci: + raise ValueError(_tci_mix_err) + if has_pcp: + raise ValueError("vlan(): duplicate 'pcp'") + if v < 0 or v > 7: + raise ValueError("vlan(): pcp=%d out of range (0-7)" % v) + tci |= (v & 0x7) << 13 + mask |= 0xE000 + has_pcp = True + elif key == 'cfi': + if has_tci: + raise ValueError(_tci_mix_err) + if has_cfi: + raise ValueError("vlan(): duplicate 'cfi'") + if v != 1: + raise ValueError("vlan(): cfi must be 1 for Ethernet") + tci |= ovskey._VLAN_CFI_MASK + mask |= ovskey._VLAN_CFI_MASK + has_cfi = True + else: + raise ValueError("vlan(): unknown key '%s'" % key) + + flowstr = flowstr[1:] # skip ')' + # Catch immediate '))' (user error). A ')' after ',' is consumed + # by parse()'s strspn(flowstr, "), ") inter-field separator stripping. + if flowstr.lstrip().startswith(')'): + raise ValueError("vlan(): unmatched ')'") + # parse() strips trailing ',', ')', ' ' as inter-field separators, + # so we do not need to call strspn here. + + if mask == 0: + raise ValueError("vlan(): no fields specified, " + "use vlan(vid=X[,pcp=Y,cfi=Z]) or vlan(tci=X)") + if not has_tci: + tci |= ovskey._VLAN_CFI_MASK + mask |= ovskey._VLAN_CFI_MASK + return flowstr, tci, mask + + @staticmethod + def _parse_encap_from_flowstr(flowstr): + """Parse encap(inner_flow) from flowstr. + + Returns (remaining_flowstr, inner_key_dict, inner_mask_dict) + where each dict has an 'attrs' key for recursive NLA encoding. + Parenthesis-depth tracking handles nested encap() calls but not + quoted strings containing literal parentheses. + """ + depth = 1 + end = -1 + for i, c in enumerate(flowstr): + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + if depth < 0: + raise ValueError( + "encap(): unmatched ')' at position %d" % i) + if depth == 0: + end = i + break + + if end == -1: + if depth > 1: + raise ValueError("encap(): missing ')' in nested encap") + raise ValueError("encap(): missing ')'") + + inner_str = flowstr[:end].strip() + if not inner_str: + raise ValueError("encap(): empty inner flow") + + flowstr = flowstr[end + 1:] + if flowstr.lstrip().startswith(')'): + raise ValueError("encap(): unmatched ')' after encap()") + + inner_key = encap_ovskey() + inner_mask = encap_ovskey() + remaining = inner_key.parse(inner_str, inner_mask) + if remaining and re.search(r'[^\s,)]', remaining): + raise ValueError( + "encap(): unrecognized trailing " + "content '%s'" % remaining.strip()) + + return flowstr, inner_key, inner_mask + def parse(self, flowstr, mask=None): for field in ( ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), @@ -1656,6 +1942,16 @@ class ovskey(nla): "eth_type", lambda x: intparse(x, "0xffff"), ), + ( + "OVS_KEY_ATTR_VLAN", + "vlan", + ovskey._parse_vlan_from_flowstr, + ), + ( + "OVS_KEY_ATTR_ENCAP", + "encap", + ovskey._parse_encap_from_flowstr, + ), ( "OVS_KEY_ATTR_IPV4", "ipv4", @@ -1793,6 +2089,9 @@ class ovskey(nla): True, ), ("OVS_KEY_ATTR_ETHERNET", None, None, False, False), + ("OVS_KEY_ATTR_VLAN", "vlan", ovskey._vlan_dpstr, + lambda x: False, True), + ("OVS_KEY_ATTR_ENCAP", None, None, False, False), ( "OVS_KEY_ATTR_ETHERTYPE", "eth_type", @@ -1820,22 +2119,63 @@ class ovskey(nla): v = self.get_attr(field[0]) if v is not None: m = None if mask is None else mask.get_attr(field[0]) + fmt = field[2] # str format or callable if field[4] is False: print_str += v.dpstr(m, more) print_str += "," else: if m is None or field[3](m): - print_str += field[1] + "(" - print_str += field[2] % v - print_str += ")," + val = fmt(v) if callable(fmt) else fmt % v + print_str += field[1] + "(" + val + ")," elif more or m != 0: - print_str += field[1] + "(" - print_str += (field[2] % v) + "/" + (field[2] % m) - print_str += ")," + if field[0] == "OVS_KEY_ATTR_VLAN": + val = "tci=0x%04x/0x%04x" % (v, m) + elif callable(fmt): + val = fmt(v) + "/" + fmt(m) + else: + val = (fmt % v) + "/" + (fmt % m) + print_str += field[1] + "(" + val + ")," return print_str +class encap_ovskey(ovskey): + """Inner flow key attributes valid inside 802.1Q ENCAP. + + Only L2-L4 key attributes (slots 0-21) appear inside ENCAP. + Metadata-only attributes (SKB_MARK, DP_HASH, RECIRC_ID, etc.) + are set to "none" -- they never appear inside ENCAP per + ovs_nla_put_vlan() in net/openvswitch/flow_netlink.c. + + nla_map indexes must match OVS_KEY_ATTR_* enum values in + include/uapi/linux/openvswitch.h. + """ + nla_map = ( + ("OVS_KEY_ATTR_UNSPEC", "none"), + ("OVS_KEY_ATTR_ENCAP", "none"), # placeholder, parsed by ovskey + ("OVS_KEY_ATTR_PRIORITY", "none"), # skb metadata, not in ENCAP + ("OVS_KEY_ATTR_IN_PORT", "none"), # skb metadata, not in ENCAP + ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), + ("OVS_KEY_ATTR_VLAN", "be16"), + ("OVS_KEY_ATTR_ETHERTYPE", "be16"), + ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), + ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), + ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), + ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), + ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), + ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), + ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), + ("OVS_KEY_ATTR_ND", "ovs_key_nd"), + ("OVS_KEY_ATTR_SKB_MARK", "none"), # metadata, not in ENCAP + ("OVS_KEY_ATTR_TUNNEL", "none"), # tunnel metadata, not in ENCAP + ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), + ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), + ("OVS_KEY_ATTR_DP_HASH", "none"), # metadata, not in ENCAP + ("OVS_KEY_ATTR_RECIRC_ID", "none"), # metadata, not in ENCAP + ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), + ) + + class OvsPacket(GenericNetlinkSocket): OVS_PACKET_CMD_MISS = 1 # Flow table miss OVS_PACKET_CMD_ACTION = 2 # USERSPACE action @@ -2362,9 +2702,10 @@ class OvsFlow(GenericNetlinkSocket): self["attrs"].append(["OVS_FLOW_ATTR_KEY", k]) self["attrs"].append(["OVS_FLOW_ATTR_MASK", m]) - a = ovsactions() - a.parse(actstr) - self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a]) + if actstr is not None: + a = ovsactions() + a.parse(actstr) + self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a]) def __init__(self): GenericNetlinkSocket.__init__(self) @@ -2398,6 +2739,25 @@ class OvsFlow(GenericNetlinkSocket): raise ne return reply + def mod_flow(self, dpifindex, flowmsg): + """Modify an existing flow in the kernel.""" + flowmsg["cmd"] = OVS_FLOW_CMD_SET + flowmsg["version"] = OVS_DATAPATH_VERSION + flowmsg["reserved"] = 0 + flowmsg["dpifindex"] = dpifindex + + try: + reply = self.nlm_request( + flowmsg, + msg_type=self.prid, + msg_flags=NLM_F_REQUEST | NLM_F_ACK, + ) + reply = reply[0] + except NetlinkError as ne: + print(flowmsg) + raise ne + return reply + def del_flows(self, dpifindex): """ Send a del message to the kernel that will drop all flows. @@ -2583,6 +2943,7 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): def main(argv): + nlmsg_atoms.encap_ovskey = encap_ovskey nlmsg_atoms.ovskey = ovskey nlmsg_atoms.ovsactions = ovsactions @@ -2672,6 +3033,12 @@ def main(argv): addflcmd.add_argument("flow", help="Flow specification") addflcmd.add_argument("acts", help="Flow actions") + modflcmd = subparsers.add_parser("mod-flow") + modflcmd.add_argument("modbr", help="Datapath name") + modflcmd.add_argument("modflow", help="Flow specification") + modflcmd.add_argument("modacts", help="Flow actions", + nargs="?", default=None) + delfscmd = subparsers.add_parser("del-flows") delfscmd.add_argument("flsbr", help="Datapath name") @@ -2769,6 +3136,14 @@ def main(argv): flow = OvsFlow.ovs_flow_msg() flow.parse(args.flow, args.acts, rep["dpifindex"]) ovsflow.add_flow(rep["dpifindex"], flow) + elif hasattr(args, "modbr"): + rep = ovsdp.info(args.modbr, 0) + if rep is None: + print(f"DP '{args.modbr}' not found.") + return 1 + flow = OvsFlow.ovs_flow_msg() + flow.parse(args.modflow, args.modacts, rep["dpifindex"]) + ovsflow.mod_flow(rep["dpifindex"], flow) elif hasattr(args, "flsbr"): rep = ovsdp.info(args.flsbr, 0) if rep is None: diff --git a/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip4_9k.pkt b/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip4_9k.pkt new file mode 100644 index 000000000000..60910069b3d7 --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip4_9k.pkt @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Check syncookies. +// +// Check we are able to rebuild client sack, wscale, ecn and mss options. +// IPv4 msstab[4] = { 536, 1300, 1440, 1460 } + +--ip_version=ipv4 + +`./defaults.sh +sysctl -q net.ipv4.tcp_syncookies=2 +ip link set dev tun0 mtu 9000 +` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 10) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.01 < . 1:1(0) ack 1 win 1024 + + +0 accept(3, ..., ...) = 4 + +// Check we properly infer from the final packet the other peer wanted mss >= 1460, wscale 10, sackOK and no ECN. +// Note that mss is limited to 1460 - 12 because of IPv4 msstab[] +// This is only possible because TCP TS option was used. +// Linux uses the SYNACK TS.val 6 low order bits to encode the options. + + +0 %{ assert tcpi_snd_mss == 1460 - 12, tcpi_snd_mss; \ + assert tcpi_snd_wscale == 10, tcpi_snd_wscale; \ + assert (tcpi_options & TCPI_OPT_SACK) != 0, tcpi_options; \ + assert (tcpi_options & TCPI_OPT_TIMESTAMPS) != 0, tcpi_options; \ + assert (tcpi_options & TCPI_OPT_WSCALE) != 0, tcpi_options; \ + assert (tcpi_options & TCPI_OPT_ECN) == 0, tcpi_options +}% diff --git a/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt b/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt new file mode 100644 index 000000000000..f333c61044bc --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Check syncookies. +// +// Check we are able to rebuild client sack, wscale, ecn and mss options. +// IPv6 msstab[4] = { 1280 - 60, 1480 - 60, 1500 - 60, 9000 - 60 } + +--ip_version=ipv6 + +`./defaults.sh +sysctl -q net.ipv4.tcp_syncookies=2 +ip link set dev tun0 mtu 9000 +` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 10) = 0 + + +0 < S 0:0(0) win 32792 + +0 > S. 0:0(0) ack 1 + +.01 < . 1:1(0) ack 1 win 1024 + + +0 accept(3, ..., ...) = 4 + +// Check we properly infer from the final packet the other peer wanted mss >= 8940, wscale 10, sackOK and no ECN. +// This is only possible because TCP TS option was used. +// Linux uses the SYNACK TS.val 6 low order bits to encode the options. + + +0 %{ assert tcpi_snd_mss == 8940 - 12, tcpi_snd_mss; \ + assert tcpi_snd_wscale == 10, tcpi_snd_wscale; \ + assert (tcpi_options & TCPI_OPT_SACK) != 0, tcpi_options; \ + assert (tcpi_options & TCPI_OPT_TIMESTAMPS) != 0, tcpi_options; \ + assert (tcpi_options & TCPI_OPT_WSCALE) != 0, tcpi_options; \ + assert (tcpi_options & TCPI_OPT_ECN) == 0, tcpi_options +}% diff --git a/tools/testing/selftests/net/ppp/Makefile b/tools/testing/selftests/net/ppp/Makefile index b39b0abadde6..6036fa134351 100644 --- a/tools/testing/selftests/net/ppp/Makefile +++ b/tools/testing/selftests/net/ppp/Makefile @@ -5,6 +5,7 @@ top_srcdir = ../../../../.. TEST_PROGS := \ ppp_async.sh \ pppoe.sh \ + pppol2tp.sh \ # end of TEST_PROGS TEST_FILES := \ diff --git a/tools/testing/selftests/net/ppp/config b/tools/testing/selftests/net/ppp/config index b45d25c5b970..843545df8f03 100644 --- a/tools/testing/selftests/net/ppp/config +++ b/tools/testing/selftests/net/ppp/config @@ -1,4 +1,5 @@ CONFIG_IPV6=y +CONFIG_L2TP=m CONFIG_PACKET=y CONFIG_PPP=m CONFIG_PPP_ASYNC=m @@ -6,4 +7,5 @@ CONFIG_PPP_BSDCOMP=m CONFIG_PPP_DEFLATE=m CONFIG_PPPOE=m CONFIG_PPPOE_HASH_BITS_4=y +CONFIG_PPPOL2TP=m CONFIG_VETH=y diff --git a/tools/testing/selftests/net/ppp/pppoe-server-options b/tools/testing/selftests/net/ppp/pppoe-server-options index 66c8c9d319e9..cd586be7061b 100644 --- a/tools/testing/selftests/net/ppp/pppoe-server-options +++ b/tools/testing/selftests/net/ppp/pppoe-server-options @@ -1,2 +1,3 @@ noauth noipdefault +nomagic diff --git a/tools/testing/selftests/net/ppp/pppol2tp.sh b/tools/testing/selftests/net/ppp/pppol2tp.sh new file mode 100755 index 000000000000..37c4d56c5c6e --- /dev/null +++ b/tools/testing/selftests/net/ppp/pppol2tp.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +source ppp_common.sh + +VETH_SERVER="veth-server" +VETH_CLIENT="veth-client" +OUTER_IP_SERVER="172.16.1.1" +OUTER_IP_CLIENT="172.16.1.2" + +PPPOL2TP_DIR=$(mktemp -d /tmp/pppol2tp.XXXXXX) +PPPOL2TP_LOG="$PPPOL2TP_DIR/l2tp.log" + +# shellcheck disable=SC2329 +cleanup() { + cleanup_all_ns + [ -n "$SOCAT_PID" ] && kill_process "$SOCAT_PID" + rm -rf "$PPPOL2TP_DIR" +} + +trap cleanup EXIT + +require_command xl2tpd +ppp_common_init +modprobe -q l2tp_ppp + +# Create the veth pair +ip link add "$VETH_SERVER" type veth peer name "$VETH_CLIENT" +ip link set "$VETH_SERVER" netns "$NS_SERVER" +ip link set "$VETH_CLIENT" netns "$NS_CLIENT" +ip -netns "$NS_SERVER" link set "$VETH_SERVER" up +ip -netns "$NS_CLIENT" link set "$VETH_CLIENT" up +ip -netns "$NS_SERVER" address add dev "$VETH_SERVER" "$OUTER_IP_SERVER" peer "$OUTER_IP_CLIENT" +ip -netns "$NS_CLIENT" address add dev "$VETH_CLIENT" "$OUTER_IP_CLIENT" peer "$OUTER_IP_SERVER" + +# Start socat as syslog listener +socat -v -u UNIX-RECV:/dev/log OPEN:/dev/null > "$PPPOL2TP_LOG" 2>&1 & +SOCAT_PID=$! + +# Generate configuration files +cat > "$PPPOL2TP_DIR/l2tp-server.conf" < "$PPPOL2TP_DIR/l2tp-client.conf" < "$PPPOL2TP_DIR/l2tp-client.control" + +ppp_test_connectivity + +log_test "PPPoL2TP" + +# Recursion test +RET=0 +# Delete route to LNS IP +ip -netns "$NS_CLIENT" route del "$OUTER_IP_SERVER" +# Add default route through ppp0 +ip -netns "$NS_CLIENT" route add default dev ppp0 +# ping (we expect the ping to fail but not deadlock the system) +ip netns exec "$NS_CLIENT" ping -c 1 "$IP_SERVER" -w 1 +check_fail $? + +log_test "PPPoL2TP Recursion" + +# Dump syslog messages if the test failed +if [ "$EXIT_STATUS" -ne 0 ]; then + while read -r _sign _date _time len _from _to + do len=${len##*=} + read -n "$len" -r LINE + echo "$LINE" + done < "$PPPOL2TP_LOG" +fi + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/protodown.sh b/tools/testing/selftests/net/protodown.sh new file mode 100755 index 000000000000..0a7b78c63c37 --- /dev/null +++ b/tools/testing/selftests/net/protodown.sh @@ -0,0 +1,182 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test the protodown mechanism. Verify basic protodown toggling, protodown +# reasons, operational state when the lower device carrier changes, and correct +# operational state when the lower device has no carrier. + +# shellcheck disable=SC1091,SC2034,SC2154,SC2317 +source lib.sh + +require_command jq + +ALL_TESTS=" + protodown_basic_macvlan + protodown_basic_vxlan + protodown_reasons + protodown_lower_toggle + protodown_lower_down +" + +operstate_get() +{ + local ns=$1; shift + local dev=$1; shift + + ip -n "$ns" -j link show dev "$dev" | jq -r '.[].operstate' +} + +operstate_check() +{ + local ns=$1; shift + local dev=$1; shift + local expected=$1; shift + + local current + current=$(operstate_get "$ns" "$dev") + + [ "$current" = "$expected" ] +} + +setup_prepare() +{ + setup_ns NS + defer cleanup_all_ns + + ip -n "$NS" link add name dummy0 up type dummy + + ip -n "$NS" link add name macvlan0 link dummy0 up type macvlan mode bridge + + ip -n "$NS" link add name vxlan0 up type vxlan id 10010 dstport 4789 +} + +protodown_basic() +{ + local dev=$1; shift + + ip -n "$NS" link set dev "$dev" protodown on + check_err $? "Failed to set protodown on" + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" "$dev" DOWN + check_err $? "Operational state is not DOWN after setting protodown" + + ip -n "$NS" link set dev "$dev" protodown off + check_err $? "Failed to set protodown off" + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" "$dev" UP + check_err $? "Operational state is not UP after clearing protodown" +} + +protodown_basic_macvlan() +{ + RET=0 + + protodown_basic macvlan0 + + log_test "Basic protodown on/off with macvlan" +} + +protodown_basic_vxlan() +{ + RET=0 + + protodown_basic vxlan0 + + log_test "Basic protodown on/off with vxlan" +} + +protodown_reasons() +{ + RET=0 + + ip -n "$NS" link set dev macvlan0 protodown on + + ip -n "$NS" link set dev macvlan0 protodown_reason 0 on + check_err $? "Failed to set protodown reason bit 0" + + # Cannot clear protodown while reasons are active. + ip -n "$NS" link set dev macvlan0 protodown off 2>/dev/null + check_fail $? "Clearing protodown succeeded with active reasons" + + ip -n "$NS" link set dev macvlan0 protodown_reason 0 off + check_err $? "Failed to clear protodown reason bit 0" + + # Can clear protodown when no reasons are active. + ip -n "$NS" link set dev macvlan0 protodown off + check_err $? "Failed to clear protodown with no active reasons" + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 UP + check_err $? "Operational state is not UP after clearing protodown" + + log_test "Protodown reasons" +} + +protodown_lower_toggle() +{ + RET=0 + + ip -n "$NS" link set dev macvlan0 protodown on + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 DOWN + check_err $? "Operational state is not DOWN after setting protodown" + + # Toggle carrier on the lower device. The macvlan should stay DOWN + # because protodown is on. + ip -n "$NS" link set dev dummy0 carrier off + ip -n "$NS" link set dev dummy0 carrier on + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" dummy0 UP + check_err $? "Lower device is not UP after carrier on" + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 DOWN + check_err $? "Macvlan operational state is not DOWN despite protodown" + + # Clear protodown and verify the macvlan comes back up. + ip -n "$NS" link set dev macvlan0 protodown off + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 UP + check_err $? "Operational state is not UP after clearing protodown" + + log_test "Protodown with lower device toggled" +} + +protodown_lower_down() +{ + RET=0 + + # Bring the lower device carrier down first. + ip -n "$NS" link set dev dummy0 carrier off + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 LOWERLAYERDOWN + check_err $? "Macvlan is not LOWERLAYERDOWN with lower carrier off" + + # Toggle protodown on and off while lower has no carrier. The macvlan + # should not transition to UP. + ip -n "$NS" link set dev macvlan0 protodown on + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 LOWERLAYERDOWN + check_err $? "Macvlan is not LOWERLAYERDOWN after setting protodown" + + ip -n "$NS" link set dev macvlan0 protodown off + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 LOWERLAYERDOWN + check_err $? "Macvlan is not LOWERLAYERDOWN after clearing protodown" + + # Bring the lower device carrier up. The macvlan should transition to + # UP. + ip -n "$NS" link set dev dummy0 carrier on + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" dummy0 UP + check_err $? "Lower device is not UP after carrier on" + + busywait "$BUSYWAIT_TIMEOUT" operstate_check "$NS" macvlan0 UP + check_err $? "Macvlan is not UP after lower device is UP" + + log_test "Protodown with lower device down" +} + +trap defer_scopes_cleanup EXIT +setup_prepare +tests_run + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/rds/.gitignore b/tools/testing/selftests/net/rds/.gitignore index 1c6f04e2aa11..7ca4b1440f51 100644 --- a/tools/testing/selftests/net/rds/.gitignore +++ b/tools/testing/selftests/net/rds/.gitignore @@ -1 +1,2 @@ include.sh +getsockopt diff --git a/tools/testing/selftests/net/rds/Makefile b/tools/testing/selftests/net/rds/Makefile index fe363be8e358..ab9e92399a6d 100644 --- a/tools/testing/selftests/net/rds/Makefile +++ b/tools/testing/selftests/net/rds/Makefile @@ -3,7 +3,9 @@ all: @echo mk_build_dir="$(shell pwd)" > include.sh -TEST_PROGS := run.sh +TEST_PROGS := rds_run.sh + +TEST_GEN_PROGS := getsockopt TEST_FILES := \ include.sh \ @@ -16,4 +18,6 @@ EXTRA_CLEAN := \ /tmp/rds_logs \ # end of EXTRA_CLEAN +CFLAGS += $(KHDR_INCLUDES) + include ../../lib.mk diff --git a/tools/testing/selftests/net/rds/README.txt b/tools/testing/selftests/net/rds/README.txt index c6fe003d503b..8aa41148b1b5 100644 --- a/tools/testing/selftests/net/rds/README.txt +++ b/tools/testing/selftests/net/rds/README.txt @@ -1,21 +1,27 @@ RDS self-tests ============== -These scripts provide a coverage test for RDS-TCP by creating two -network namespaces and running rds packets between them. A loopback -network is provisioned with optional probability of packet loss or -corruption. A workload of 50000 hashes, each 64 characters in size, -are passed over an RDS socket on this test network. A passing test means -the RDS-TCP stack was able to recover properly. The provided config.sh -can be used to compile the kernel with the necessary gcov options. The -kernel may optionally be configured to omit the coverage report as well. +These scripts provide a coverage test for RDS-TCP and RDS-RDMA (over +RoCE/RXE) by setting up two endpoints and running RDS packets between +them. The TCP path creates two network namespaces; the RDMA path uses +an RXE (soft RoCE) device backed by a veth pair. A workload of 50000 +hashes, each 64 characters in size, is passed over an RDS socket on +this test network with an optional probability of packet loss or +corruption. A passing test means the RDS stack was able to recover +properly. The provided config.sh can be used to compile the kernel +with the necessary gcov options; pass -r to also enable the kernel +configs required for the RDMA transport. The kernel may optionally be +configured to omit the coverage report as well. USAGE: - run.sh [-d logdir] [-l packet_loss] [-c packet_corruption] - [-u packet_duplcate] + rds_run.sh [-d logdir] [-l packet_loss] [-c packet_corruption] + [-u packet_duplicate] [-t timeout] + [-T tcp|rdma|tcp,rdma] OPTIONS: - -d Log directory. Defaults to tools/testing/selftests/net/rds/rds_logs + -d Log directory. If set, logs will be stored in the + given dir, or skipped if unset. Log dir can also be + set through the RDS_LOG_DIR env variable -l Simulates a percentage of packet loss @@ -23,11 +29,36 @@ OPTIONS: -u Simulates a percentage of packet duplication. + -t Test timeout. Defaults to tools/testing/selftests/net/rds/settings + + -T Comma-separated list of transports to test. Accepts + "tcp", "rdma", or "tcp,rdma". Defaults to "tcp". Use + config.sh -r to enable required RDMA configs + +ENV VARIABLES: + RDS_LOG_DIR Log directory. If set, logs will be stored in + the given dir, or skipped if unset. Log dir + can also be set with the -d flag. + + Use with --rwdir on the CI path to retain logs after + test compleation. Log dir end point must be within + the specified --rwdir path for logs to persist on + the host. + + SUDO_USER The user name that should be used for tcpdump + --relinquish-privileges. Set this to a user + belonging to the sudoers group to avoid drop + privilege errors with the vng 9p filesystem + which may result in empty pcaps + EXAMPLE: # Create a suitable gcov enabled .config tools/testing/selftests/net/rds/config.sh -g + # Optionally add RDMA configs (CONFIG_RDS_RDMA, CONFIG_RDMA_RXE) + tools/testing/selftests/net/rds/config.sh -r + # Alternatly create a gcov disabled .config tools/testing/selftests/net/rds/config.sh @@ -39,6 +70,8 @@ EXAMPLE: # launch the tests in a VM vng -v --rwdir ./ --run . --user root --cpus 4 -- \ - "export PYTHONPATH=tools/testing/selftests/net/; tools/testing/selftests/net/rds/run.sh" + "export PYTHONPATH=tools/testing/selftests/net/; \ + export SUDO_USER=example_user; \ + export RDS_LOG_DIR=tools/testing/selftests/net/rds/rds_logs; \ + tools/testing/selftests/net/rds/rds_run.sh -T tcp,rdma" -An HTML coverage report will be output in tools/testing/selftests/net/rds/rds_logs/coverage/. diff --git a/tools/testing/selftests/net/rds/config b/tools/testing/selftests/net/rds/config index 3d62d0c750a8..97db7ecb892a 100644 --- a/tools/testing/selftests/net/rds/config +++ b/tools/testing/selftests/net/rds/config @@ -1,4 +1,3 @@ -CONFIG_MODULES=n CONFIG_NET_NS=y CONFIG_NET_SCH_NETEM=y CONFIG_RDS=y diff --git a/tools/testing/selftests/net/rds/config.sh b/tools/testing/selftests/net/rds/config.sh index 29a79314dd60..2df2226310ef 100755 --- a/tools/testing/selftests/net/rds/config.sh +++ b/tools/testing/selftests/net/rds/config.sh @@ -10,7 +10,8 @@ CONF_FILE="" FLAGS=() GENERATE_GCOV_REPORT=0 -while getopts "gc:" opt; do +ENABLE_RDMA=0 +while getopts "gc:r" opt; do case ${opt} in g) GENERATE_GCOV_REPORT=1 @@ -18,8 +19,11 @@ while getopts "gc:" opt; do c) CONF_FILE=$OPTARG ;; + r) + ENABLE_RDMA=1 + ;; :) - echo "USAGE: config.sh [-g] [-c config]" + echo "USAGE: config.sh [-g] [-c config] [-r]" exit 1 ;; ?) @@ -33,9 +37,6 @@ if [[ "$CONF_FILE" != "" ]]; then FLAGS=(--file "$CONF_FILE") fi -# no modules -scripts/config "${FLAGS[@]}" --disable CONFIG_MODULES - # enable RDS scripts/config "${FLAGS[@]}" --enable CONFIG_RDS scripts/config "${FLAGS[@]}" --enable CONFIG_RDS_TCP @@ -58,3 +59,10 @@ scripts/config "${FLAGS[@]}" --enable CONFIG_VETH # simulate packet loss scripts/config "${FLAGS[@]}" --enable CONFIG_NET_SCH_NETEM +if [ "$ENABLE_RDMA" -eq 1 ]; then + # enable RDS over InfiniBand / RDMA (rds_rdma test) + scripts/config "${FLAGS[@]}" --enable CONFIG_INFINIBAND + scripts/config "${FLAGS[@]}" --enable CONFIG_INFINIBAND_ADDR_TRANS + scripts/config "${FLAGS[@]}" --enable CONFIG_RDMA_RXE + scripts/config "${FLAGS[@]}" --enable CONFIG_RDS_RDMA +fi diff --git a/tools/testing/selftests/net/rds/getsockopt.c b/tools/testing/selftests/net/rds/getsockopt.c new file mode 100644 index 000000000000..93ff252c69b8 --- /dev/null +++ b/tools/testing/selftests/net/rds/getsockopt.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Exercise the RDS getsockopt() paths that were converted to the + * getsockopt_iter() / sockopt_t callback. + * + * Three distinct paths are covered: + * + * - RDS_RECVERR and SO_RDS_TRANSPORT, which now return their int value + * through copy_to_iter() and report the written length in opt->optlen. + * + * - RDS_INFO_*, which pins the userspace buffer with + * iov_iter_extract_pages() (including a non-zero starting page offset) + * and lets the info producers memcpy the snapshot in under a spinlock. + * + * The kvec (in-kernel buffer) -> -EOPNOTSUPP path of rds_info_getsockopt() + * is not reachable from a userspace getsockopt() and so is not tested here. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "../../kselftest_harness.h" + +#ifndef AF_RDS +#define AF_RDS 21 +#endif + +FIXTURE(rds) { + int fd; +}; + +FIXTURE_SETUP(rds) +{ + self->fd = socket(AF_RDS, SOCK_SEQPACKET, 0); + if (self->fd < 0) + SKIP(return, "AF_RDS unavailable (errno %d) - load the rds module", + errno); +} + +FIXTURE_TEARDOWN(rds) +{ + if (self->fd >= 0) + close(self->fd); +} + +/* RDS_RECVERR defaults to 0 and is reported back as a 4-byte int. */ +TEST_F(rds, recverr_default) +{ + socklen_t len = sizeof(int); + int val = 0xdeadbeef; + + ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, &len)); + EXPECT_EQ(sizeof(int), len); + EXPECT_EQ(0, val); +} + +/* A value set via setsockopt() must be readable back unchanged. */ +TEST_F(rds, recverr_set_get) +{ + socklen_t len = sizeof(int); + int val = 1; + + ASSERT_EQ(0, setsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, len)); + + val = 0; + ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, &len)); + EXPECT_EQ(sizeof(int), len); + EXPECT_EQ(1, val); +} + +/* A buffer smaller than an int is rejected with EINVAL, not silently. */ +TEST_F(rds, recverr_short_buffer) +{ + socklen_t len = sizeof(int) - 1; + char buf[sizeof(int)]; + + EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, buf, &len)); + EXPECT_EQ(EINVAL, errno); +} + +/* An unbound socket reports RDS_TRANS_NONE for SO_RDS_TRANSPORT. */ +TEST_F(rds, transport_unbound) +{ + socklen_t len = sizeof(int); + int val = 0; + + ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, SO_RDS_TRANSPORT, &val, + &len)); + EXPECT_EQ(sizeof(int), len); + EXPECT_EQ(RDS_TRANS_NONE, (unsigned int)val); +} + +TEST_F(rds, transport_short_buffer) +{ + socklen_t len = sizeof(int) - 1; + char buf[sizeof(int)]; + + EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, SO_RDS_TRANSPORT, buf, + &len)); + EXPECT_EQ(EINVAL, errno); +} + +/* + * RDS_INFO_COUNTERS with a zero-length buffer is the "probe" call: it must + * fail with ENOSPC and report the required snapshot size in optlen. + */ +TEST_F(rds, info_counters_probe) +{ + socklen_t len = 0; + + EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, + &len)); + EXPECT_EQ(ENOSPC, errno); + EXPECT_GT(len, 0); + /* The snapshot is an array of fixed-size counter records. */ + EXPECT_EQ(0, len % (socklen_t)sizeof(struct rds_info_counter)); +} + +/* + * A real snapshot into an unaligned userspace buffer exercises the + * iov_iter_extract_pages() path, including the non-zero offset0 handling + * that the patch reworked. Place the buffer at a non-page-aligned address + * spanning into the next page to make sure multi-page pinning works too. + */ +TEST_F(rds, info_counters_snapshot) +{ + struct rds_info_counter *ctr; + socklen_t need = 0, len; + long pagesz = sysconf(_SC_PAGESIZE); + size_t offset, map_len; + unsigned int i, n; + char *region, *buf; + int ret; + + /* Probe for the required size. */ + getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, &need); + ASSERT_GT(need, 0); + + /* + * Place the buffer at a non-page-aligned offset that runs past the + * first page boundary, and size the mapping from the probed length so + * the test keeps working if the counter set grows. + */ + offset = pagesz - 64; + map_len = ((offset + need + pagesz - 1) / pagesz) * pagesz; + + region = mmap(NULL, map_len, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, region); + + buf = region + offset; + + /* + * On success the RDS_INFO path returns the positive per-element size + * (lens.each) rather than 0, and writes the full snapshot length back + * into optlen. + */ + len = need; + ret = getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, buf, &len); + ASSERT_GE(ret, 0) { + TH_LOG("getsockopt snapshot failed: errno %d", errno); + } + EXPECT_EQ(sizeof(struct rds_info_counter), ret); + EXPECT_EQ(need, len); + + /* The counter names must be NUL-terminated, non-empty strings. */ + ctr = (struct rds_info_counter *)buf; + n = len / sizeof(*ctr); + ASSERT_GT(n, 0); + for (i = 0; i < n; i++) { + size_t namelen = strnlen((char *)ctr[i].name, + sizeof(ctr[i].name)); + + EXPECT_GT(namelen, 0); + EXPECT_LT(namelen, sizeof(ctr[i].name)); + } + + munmap(region, map_len); +} + +/* + * A non-zero but too-small buffer must report ENOSPC and the full required + * length, without corrupting memory past the buffer. + */ +TEST_F(rds, info_counters_short_buffer) +{ + socklen_t need = 0, len; + char small[sizeof(struct rds_info_counter)]; + + getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, &need); + ASSERT_GT(need, 0); + + /* Ask with a buffer guaranteed smaller than the full snapshot. */ + if (need <= (socklen_t)sizeof(small)) + SKIP(return, "snapshot fits in one record; nothing to test"); + + len = 1; /* < sizeof(struct rds_info_counter) */ + EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, small, + &len)); + EXPECT_EQ(ENOSPC, errno); + EXPECT_EQ(need, len); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/rds/rds_run.sh b/tools/testing/selftests/net/rds/rds_run.sh new file mode 100755 index 000000000000..cdf487ec97dc --- /dev/null +++ b/tools/testing/selftests/net/rds/rds_run.sh @@ -0,0 +1,319 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 + +set -e +set -u + +unset KBUILD_OUTPUT + +current_dir="$(realpath "$(dirname "$0")")" +build_dir="$current_dir" + +build_include="$current_dir/include.sh" +if test -f "$build_include"; then + # this include will define "$mk_build_dir" as the location the test was + # built. We will need this if the tests are installed in a location + # other than the kernel source + + source "$build_include" + build_dir="$mk_build_dir" +fi + +# Source settings for timeout value (also used by ksft runner) +source "$current_dir"/settings + +# This test requires kernel source and the *.gcda data therein +# Locate the top level of the kernel source, and the net/rds +# subfolder with the appropriate *.gcno object files +ksrc_dir="$(realpath "$build_dir"/../../../../../)" +kconfig="$ksrc_dir/.config" +obj_dir="$ksrc_dir/net/rds" + +GCOV_CMD=gcov + +#check to see if the host has the required packages to generate a gcov report +check_gcov_env() +{ + if ! which "$GCOV_CMD" > /dev/null 2>&1; then + echo "# Warning: Could not find gcov. " + GENERATE_GCOV_REPORT=0 + return + fi + + # the gcov version must match the gcc version + GCC_VER=$(gcc -dumpfullversion) + GCOV_VER=$($GCOV_CMD -v | grep gcov | awk '{print $3}'| awk 'BEGIN {FS="-"}{print $1}') + if [ "$GCOV_VER" != "$GCC_VER" ]; then + #attempt to find a matching gcov version + GCOV_CMD=gcov-$(gcc -dumpversion) + + if ! which "$GCOV_CMD" > /dev/null 2>&1; then + echo "# Warning: Could not find an appropriate gcov installation. \ + gcov version must match gcc version" + GENERATE_GCOV_REPORT=0 + return + fi + + #recheck version number of found gcov executable + GCOV_VER=$($GCOV_CMD -v | grep gcov | awk '{print $3}'| \ + awk 'BEGIN {FS="-"}{print $1}') + if [ "$GCOV_VER" != "$GCC_VER" ]; then + echo "# Warning: Could not find an appropriate gcov installation. \ + gcov version must match gcc version" + GENERATE_GCOV_REPORT=0 + else + echo "# Warning: Mismatched gcc and gcov detected. Using $GCOV_CMD" + fi + fi +} + +# Check to see if the kconfig has the required configs to generate a coverage report +check_gcov_conf() +{ + if ! grep -x "CONFIG_GCOV_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then + echo "# INFO: CONFIG_GCOV_PROFILE_RDS should be enabled for coverage reports" + GENERATE_GCOV_REPORT=0 + fi + if ! grep -x "CONFIG_GCOV_KERNEL=y" "$kconfig" > /dev/null 2>&1; then + echo "# INFO: CONFIG_GCOV_KERNEL should be enabled for coverage reports" + GENERATE_GCOV_REPORT=0 + fi + if grep -x "CONFIG_GCOV_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then + echo "# INFO: CONFIG_GCOV_PROFILE_ALL should be disabled for coverage reports" + GENERATE_GCOV_REPORT=0 + fi + + if [ "$GENERATE_GCOV_REPORT" -eq 0 ]; then + echo "# To enable gcov reports, please run "\ + "\"tools/testing/selftests/net/rds/config.sh -g\" and rebuild the kernel" + else + # if we have the required kernel configs, proceed to check the environment to + # ensure we have the required gcov packages + check_gcov_env + fi +} + +# Checks if a kconfig is enabled (set to =y or =m) +# $1: kconfig symbol to check +# $2: (optional) module name backing $1 +# Ex: check_conf_enabled CONFIG_RDS_TCP rds_tcp +# Modules for configs set to =m will be probed +# If omitted, only a built-in (=y) config is accepted. +# Returns on success. exits 4 on failure +# Kselftest framework requirement - SKIP code is 4. +check_conf_enabled() { + if grep -x "$1=y" "$kconfig" > /dev/null 2>&1; then + return + fi + if [ -n "${2:-}" ] && grep -x "$1=m" "$kconfig" > /dev/null 2>&1; then + probe_module "$2" + return + fi + echo "selftests: [SKIP] This test requires $1 enabled" + echo "Please run" \ + "tools/testing/selftests/net/rds/config.sh and rebuild the kernel" + exit 4 +} + +check_rdma_conf_enabled() { + if grep -x "$1=y" "$kconfig" > /dev/null 2>&1; then + return + fi + if [ -n "${2:-}" ] && grep -x "$1=m" "$kconfig" > /dev/null 2>&1; then + probe_module "$2" + return + fi + echo "selftests: [XFAIL] rdma transport requires $1 enabled" + echo "To enable, run" \ + "tools/testing/selftests/net/rds/config.sh -r and rebuild" + exit 2 +} + +# Load the module backing a config that is built as a loadable module +# (=m). Built-in (=y) configs are already available and don't reach +# here. Exits with the SKIP code if a required module cannot be loaded. +probe_module() { + if ! modprobe -q "$1"; then + echo "selftests: [SKIP] could not load required module $1" + exit 4 + fi +} + +check_conf() { + check_conf_enabled CONFIG_NET_SCH_NETEM sch_netem + check_conf_enabled CONFIG_VETH veth + check_conf_enabled CONFIG_NET_NS + check_conf_enabled CONFIG_RDS_TCP rds_tcp + check_conf_enabled CONFIG_RDS rds +} + +# Check kernel config and host environment for RDS-RDMA support. +# Exits with XFAIL (2) if the user requested rdma but prerequisites +# are not met. +check_rdma_conf() +{ + case "$TRANSPORT" in + *rdma*) ;; + *) return ;; + esac + + # Kconfig will enforce CONFIG_INFINIBAND_* as dependencies + # of CONFIG_RDMA_RXE + check_rdma_conf_enabled CONFIG_RDMA_RXE rdma_rxe + check_rdma_conf_enabled CONFIG_RDS_RDMA rds_rdma + + if ! which rdma > /dev/null 2>&1; then + echo "selftests: [XFAIL] rdma transport requires the 'rdma'" \ + "tool (iproute2)" + exit 2 + fi +} + +check_env() +{ + if ! test -d "$obj_dir"; then + echo "selftests: [SKIP] This test requires a kernel source tree" + exit 4 + fi + if ! test -e "$kconfig"; then + echo "selftests: [SKIP] This test requires a configured kernel source tree" + exit 4 + fi + if ! which strace > /dev/null 2>&1; then + echo "selftests: [SKIP] Could not run test without strace" + exit 4 + fi + if ! which tcpdump > /dev/null 2>&1; then + echo "selftests: [SKIP] Could not run test without tcpdump" + exit 4 + fi + + if ! which python3 > /dev/null 2>&1; then + echo "selftests: [SKIP] Could not run test without python3" + exit 4 + fi + + python_major=$(python3 -c "import sys; print(sys.version_info[0])") + python_minor=$(python3 -c "import sys; print(sys.version_info[1])") + if [[ python_major -lt 3 || ( python_major -eq 3 && python_minor -lt 9 ) ]] ; then + echo "selftests: [SKIP] Could not run test without at least python3.9" + python3 -V + exit 4 + fi +} + +LOG_DIR="${RDS_LOG_DIR:-}" +TIMEOUT=$timeout +GENERATE_GCOV_REPORT=1 +TRANSPORT=tcp +FLAGS=() + +while getopts "d:l:c:u:t:T:" opt; do + case ${opt} in + d) + LOG_DIR=${OPTARG} + ;; + l) + FLAGS+=("-l" "${OPTARG}") + ;; + c) + FLAGS+=("-c" "${OPTARG}") + ;; + t) + TIMEOUT=${OPTARG} + ;; + u) + FLAGS+=("-u" "${OPTARG}") + ;; + T) + TRANSPORT=${OPTARG} + ;; + :) + echo "USAGE: rds_run.sh [-d logdir] [-l packet_loss]" \ + "[-c packet_corruption] [-u packet_duplicate] [-t timeout]" \ + "[-T tcp|rdma|tcp,rdma]" + exit 1 + ;; + ?) + echo "Invalid option: -${OPTARG}." + exit 1 + ;; + esac +done + +# Validate transport tokens +IFS=',' read -ra transports <<< "$TRANSPORT" +for t in "${transports[@]}"; do + if [ "$t" != "tcp" ] && [ "$t" != "rdma" ]; then + echo "rds_run.sh: unknown transport '$t' (expected tcp or rdma)" + exit 1 + fi +done + +FLAGS+=("--transport" "${TRANSPORT}") + +check_env +check_conf +check_gcov_conf +check_rdma_conf + +TRACE_CMD=() +if [[ -n "$LOG_DIR" ]]; then + FLAGS+=("-d" "$LOG_DIR") + + TRACE_FILE="${LOG_DIR}/rds-strace.txt" + COVR_DIR="${LOG_DIR}/coverage/" + DMESG_FILE="${LOG_DIR}/rds-dmesg.out" + + mkdir -p "$LOG_DIR" + mkdir -p "$COVR_DIR" + + rm -f "$TRACE_FILE" + rm -f "$DMESG_FILE" + rm -f "$LOG_DIR"/rds-*.pcap + rm -f "$COVR_DIR"/gcovr* + + echo "# Traces will be logged to ${TRACE_FILE}" + TRACE_CMD=(strace -T -tt -o "${TRACE_FILE}") +fi + +set +e +echo "# running RDS tests..." +"${TRACE_CMD[@]}" python3 "$(dirname "$0")/test.py" "${FLAGS[@]}" -t "$TIMEOUT" + +test_rc=$? + +if [[ -n "$LOG_DIR" ]]; then + dmesg > "${DMESG_FILE}" +fi + +if [[ -n "$LOG_DIR" ]] && [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then + echo "# saving coverage data..." + + # Ensure debugfs is mounted before reading gcov data. + if ! mountpoint -q /sys/kernel/debug 2>/dev/null; then + mount -t debugfs debugfs /sys/kernel/debug 2>/dev/null || true + fi + + (set +x; cd /sys/kernel/debug/gcov; find ./* -name '*.gcda' | \ + while read -r f + do + cat < "/sys/kernel/debug/gcov/$f" > "/$f" + done) + + echo "# running gcovr..." + gcovr -s --html-details --gcov-executable "$GCOV_CMD" --gcov-ignore-parse-errors \ + --root "${ksrc_dir}" -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" \ + > "${LOG_DIR}/gcovr.log" 2>&1 + echo "# gcovr log: ${LOG_DIR}/gcovr.log" +else + echo "# Coverage report will be skipped" +fi + +if [ "$test_rc" -eq 0 ]; then + echo "# PASS: Test completed successfully" +else + echo "# FAIL: Test failed" +fi + +exit "$test_rc" diff --git a/tools/testing/selftests/net/rds/run.sh b/tools/testing/selftests/net/rds/run.sh deleted file mode 100755 index 897d17d1b8db..000000000000 --- a/tools/testing/selftests/net/rds/run.sh +++ /dev/null @@ -1,227 +0,0 @@ -#! /bin/bash -# SPDX-License-Identifier: GPL-2.0 - -set -e -set -u - -unset KBUILD_OUTPUT - -current_dir="$(realpath "$(dirname "$0")")" -build_dir="$current_dir" - -build_include="$current_dir/include.sh" -if test -f "$build_include"; then - # this include will define "$mk_build_dir" as the location the test was - # built. We will need this if the tests are installed in a location - # other than the kernel source - - source "$build_include" - build_dir="$mk_build_dir" -fi - -# Source settings for timeout value (also used by ksft runner) -source "$current_dir"/settings - -# This test requires kernel source and the *.gcda data therein -# Locate the top level of the kernel source, and the net/rds -# subfolder with the appropriate *.gcno object files -ksrc_dir="$(realpath "$build_dir"/../../../../../)" -kconfig="$ksrc_dir/.config" -obj_dir="$ksrc_dir/net/rds" - -GCOV_CMD=gcov - -#check to see if the host has the required packages to generate a gcov report -check_gcov_env() -{ - if ! which "$GCOV_CMD" > /dev/null 2>&1; then - echo "Warning: Could not find gcov. " - GENERATE_GCOV_REPORT=0 - return - fi - - # the gcov version must match the gcc version - GCC_VER=$(gcc -dumpfullversion) - GCOV_VER=$($GCOV_CMD -v | grep gcov | awk '{print $3}'| awk 'BEGIN {FS="-"}{print $1}') - if [ "$GCOV_VER" != "$GCC_VER" ]; then - #attempt to find a matching gcov version - GCOV_CMD=gcov-$(gcc -dumpversion) - - if ! which "$GCOV_CMD" > /dev/null 2>&1; then - echo "Warning: Could not find an appropriate gcov installation. \ - gcov version must match gcc version" - GENERATE_GCOV_REPORT=0 - return - fi - - #recheck version number of found gcov executable - GCOV_VER=$($GCOV_CMD -v | grep gcov | awk '{print $3}'| \ - awk 'BEGIN {FS="-"}{print $1}') - if [ "$GCOV_VER" != "$GCC_VER" ]; then - echo "Warning: Could not find an appropriate gcov installation. \ - gcov version must match gcc version" - GENERATE_GCOV_REPORT=0 - else - echo "Warning: Mismatched gcc and gcov detected. Using $GCOV_CMD" - fi - fi -} - -# Check to see if the kconfig has the required configs to generate a coverage report -check_gcov_conf() -{ - if ! grep -x "CONFIG_GCOV_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then - echo "INFO: CONFIG_GCOV_PROFILE_RDS should be enabled for coverage reports" - GENERATE_GCOV_REPORT=0 - fi - if ! grep -x "CONFIG_GCOV_KERNEL=y" "$kconfig" > /dev/null 2>&1; then - echo "INFO: CONFIG_GCOV_KERNEL should be enabled for coverage reports" - GENERATE_GCOV_REPORT=0 - fi - if grep -x "CONFIG_GCOV_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then - echo "INFO: CONFIG_GCOV_PROFILE_ALL should be disabled for coverage reports" - GENERATE_GCOV_REPORT=0 - fi - - if [ "$GENERATE_GCOV_REPORT" -eq 0 ]; then - echo "To enable gcov reports, please run "\ - "\"tools/testing/selftests/net/rds/config.sh -g\" and rebuild the kernel" - else - # if we have the required kernel configs, proceed to check the environment to - # ensure we have the required gcov packages - check_gcov_env - fi -} - -# Kselftest framework requirement - SKIP code is 4. -check_conf_enabled() { - if ! grep -x "$1=y" "$kconfig" > /dev/null 2>&1; then - echo "selftests: [SKIP] This test requires $1 enabled" - echo "Please run tools/testing/selftests/net/rds/config.sh and rebuild the kernel" - exit 4 - fi -} -check_conf_disabled() { - if grep -x "$1=y" "$kconfig" > /dev/null 2>&1; then - echo "selftests: [SKIP] This test requires $1 disabled" - echo "Please run tools/testing/selftests/net/rds/config.sh and rebuild the kernel" - exit 4 - fi -} -check_conf() { - check_conf_enabled CONFIG_NET_SCH_NETEM - check_conf_enabled CONFIG_VETH - check_conf_enabled CONFIG_NET_NS - check_conf_enabled CONFIG_RDS_TCP - check_conf_enabled CONFIG_RDS - check_conf_disabled CONFIG_MODULES -} - -check_env() -{ - if ! test -d "$obj_dir"; then - echo "selftests: [SKIP] This test requires a kernel source tree" - exit 4 - fi - if ! test -e "$kconfig"; then - echo "selftests: [SKIP] This test requires a configured kernel source tree" - exit 4 - fi - if ! which strace > /dev/null 2>&1; then - echo "selftests: [SKIP] Could not run test without strace" - exit 4 - fi - if ! which tcpdump > /dev/null 2>&1; then - echo "selftests: [SKIP] Could not run test without tcpdump" - exit 4 - fi - - if ! which python3 > /dev/null 2>&1; then - echo "selftests: [SKIP] Could not run test without python3" - exit 4 - fi - - python_major=$(python3 -c "import sys; print(sys.version_info[0])") - python_minor=$(python3 -c "import sys; print(sys.version_info[1])") - if [[ python_major -lt 3 || ( python_major -eq 3 && python_minor -lt 9 ) ]] ; then - echo "selftests: [SKIP] Could not run test without at least python3.9" - python3 -V - exit 4 - fi -} - -LOG_DIR="$current_dir"/rds_logs -PLOSS=0 -PCORRUPT=0 -PDUP=0 -GENERATE_GCOV_REPORT=1 -while getopts "d:l:c:u:" opt; do - case ${opt} in - d) - LOG_DIR=${OPTARG} - ;; - l) - PLOSS=${OPTARG} - ;; - c) - PCORRUPT=${OPTARG} - ;; - u) - PDUP=${OPTARG} - ;; - :) - echo "USAGE: run.sh [-d logdir] [-l packet_loss] [-c packet_corruption]" \ - "[-u packet_duplcate] [-g]" - exit 1 - ;; - ?) - echo "Invalid option: -${OPTARG}." - exit 1 - ;; - esac -done - - -check_env -check_conf -check_gcov_conf - - -rm -fr "$LOG_DIR" -TRACE_FILE="${LOG_DIR}/rds-strace.txt" -COVR_DIR="${LOG_DIR}/coverage/" -mkdir -p "$LOG_DIR" -mkdir -p "$COVR_DIR" - -set +e -echo running RDS tests... -echo Traces will be logged to "$TRACE_FILE" -rm -f "$TRACE_FILE" -strace -T -tt -o "$TRACE_FILE" python3 "$(dirname "$0")/test.py" \ - --timeout "$timeout" -d "$LOG_DIR" -l "$PLOSS" -c "$PCORRUPT" -u "$PDUP" - -test_rc=$? -dmesg > "${LOG_DIR}/dmesg.out" - -if [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then - echo saving coverage data... - (set +x; cd /sys/kernel/debug/gcov; find ./* -name '*.gcda' | \ - while read -r f - do - cat < "/sys/kernel/debug/gcov/$f" > "/$f" - done) - - echo running gcovr... - gcovr -s --html-details --gcov-executable "$GCOV_CMD" --gcov-ignore-parse-errors \ - -o "${COVR_DIR}/gcovr" "${ksrc_dir}/net/rds/" -else - echo "Coverage report will be skipped" -fi - -if [ "$test_rc" -eq 0 ]; then - echo "PASS: Test completed successfully" -else - echo "FAIL: Test failed" -fi - -exit "$test_rc" diff --git a/tools/testing/selftests/net/rds/settings b/tools/testing/selftests/net/rds/settings index d2009a64589c..8cb41e6a83cc 100644 --- a/tools/testing/selftests/net/rds/settings +++ b/tools/testing/selftests/net/rds/settings @@ -1 +1 @@ -timeout=400 +timeout=800 diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py index 93e23e8b256c..9e4df01cb0d4 100755 --- a/tools/testing/selftests/net/rds/test.py +++ b/tools/testing/selftests/net/rds/test.py @@ -1,23 +1,30 @@ #! /usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 +""" +This module provides functional testing for the net/rds component. +""" import argparse +import atexit import ctypes import errno import hashlib import os import select +import re import signal import socket import subprocess import sys -import tempfile -import shutil +import time # Allow utils module to be imported from different directory this_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(this_dir, "../")) -from lib.py.utils import ip +# pylint: disable-next=wrong-import-position,import-error,no-name-in-module +from lib.py.utils import ip, cmd # noqa: E402 +# pylint: disable-next=wrong-import-position,import-error,no-name-in-module +from lib.py.ksft import ksft_pr # noqa: E402 libc = ctypes.cdll.LoadLibrary('libc.so.6') setns = libc.setns @@ -28,6 +35,44 @@ NET1 = 'net1' VETH0 = 'veth0' VETH1 = 'veth1' +tcpdump_procs = [] +tcp_addrs = [ + # we technically don't need different port numbers, but this will + # help identify traffic in the network analyzer + ('10.0.0.1', 10000), + ('10.0.0.2', 20000), +] + +# RDMA network configs +RXE_DEV0 = 'rxe0' +RXE_DEV1 = 'rxe1' + +VETH_RDMA0 = 'veth_rdma0' +VETH_RDMA1 = 'veth_rdma1' + +rdma_addrs = [ + ('10.0.0.3', 30000), + ('10.0.0.4', 30000), +] + +# send_packets flag space +OP_FLAG_TCP = 0x1 +OP_FLAG_RDMA = 0x2 + +# from include/uapi/linux/rds.h: SO_RDS_TRANSPORT pins a socket to a +# specific RDS transport so connection setup cannot silently fall back +# to another (e.g. loopback) transport. +SOL_RDS = 276 +SO_RDS_TRANSPORT = 8 +RDS_TRANS_TCP = 2 +RDS_TRANS_IB = 0 + +signal_handler_label = "" + +tap_idx = 0 +nr_pass = 0 +nr_fail = 0 + # Helper function for creating a socket inside a network namespace. # We need this because otherwise RDS will detect that the two TCP # sockets are on the same interface and use the loop transport instead @@ -43,43 +88,399 @@ def netns_socket(netns, *sock_args): child = os.fork() if child == 0: - # change network namespace - with open(f'/var/run/netns/{netns}', encoding='utf-8') as f: - try: + try: + # change network namespace + with open(f'/var/run/netns/{netns}', encoding='utf-8') as f: setns(f.fileno(), 0) - except IOError as e: - print(e.errno) - print(e) + # create socket in target namespace + sock = socket.socket(*sock_args) - # create socket in target namespace - sock = socket.socket(*sock_args) + # send resulting socket to parent + socket.send_fds(u0, [], [sock.fileno()]) - # send resulting socket to parent - socket.send_fds(u0, [], [sock.fileno()]) - - sys.exit(0) + os._exit(0) + except BaseException: + os._exit(1) # receive socket from child _, fds, _, _ = socket.recv_fds(u1, 0, 1) - os.waitpid(child, 0) + _, status = os.waitpid(child, 0) u0.close() u1.close() + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise RuntimeError( + f"netns_socket child failed in netns {netns} (status={status})") return socket.fromfd(fds[0], *sock_args) +def send_burst(socks, ip_addrs, snd_hashes, nr_sent, nr_total): + """Send until blocked or nr_total reached. Return updated nr_sent.""" + + while nr_sent < nr_total: + data = hashlib.sha256( + f'packet {nr_sent}'.encode('utf-8')).hexdigest().encode('utf-8') + # pseudo-random send/receive pattern + snd_idx = nr_sent % 2 + rcv_idx = 1 - (nr_sent % 3) % 2 + + snd = socks[snd_idx] + rcv = socks[rcv_idx] + try: + snd.sendto(data, ip_addrs[rcv_idx]) + except BlockingIOError: + return nr_sent + except OSError as e: + if e.errno in (errno.ENOBUFS, errno.ECONNRESET, errno.EPIPE): + return nr_sent + raise + snd_hashes.setdefault((snd.fileno(), rcv.fileno()), + hashlib.sha256()).update(f'<{data}>'.encode('utf-8')) + nr_sent += 1 + return nr_sent + +def recv_burst(epoll, socks, ip_addrs, rcv_hashes, nr_rcv): + """Drain whatever's readable from epoll. Return updated nr_recv.""" + for filen, evntmask in epoll.poll(): + if not evntmask & select.EPOLLRDNORM: + continue + rcv = next(s for s in socks if s.fileno() == filen) + while True: + try: + data, adr = rcv.recvfrom(1024) + except BlockingIOError: + break + snd_idx = ip_addrs.index(adr) + snd = socks[snd_idx] + rcv_hashes.setdefault((snd.fileno(), rcv.fileno()), + hashlib.sha256()).update(f'<{data}>'.encode('utf-8')) + nr_rcv += 1 + return nr_rcv + +def check_info(socks): + """ + Check all rds info pages for errors + + :param socks: list of sockets to check + """ + + # the Python socket module doesn't know these + rds_info_first = 10000 + rds_info_last = 10017 + + nr_success = 0 + nr_error = 0 + + for sock in socks: + for optname in range(rds_info_first, rds_info_last + 1): + # Sigh, the Python socket module doesn't allow us to pass + # buffer lengths greater than 1024 for some reason. RDS + # wants multiple pages. + try: + sock.getsockopt(socket.SOL_RDS, optname, 1024) + nr_success = nr_success + 1 + except OSError as e: + nr_error = nr_error + 1 + if e.errno == errno.ENOSPC: + # ignore + pass + + ksft_pr(f"getsockopt(): {nr_success}/{nr_error}") + +def verify_hashes(snd_hashes, rcv_hashes): + """Compare send/recv hashes per (sender, receiver) pair.""" + for key, snd_hash in snd_hashes.items(): + rcv_hash = rcv_hashes.get(key) + if rcv_hash is None: + ksft_pr("FAIL: No data received") + return 1 + if snd_hash.hexdigest() != rcv_hash.hexdigest(): + ksft_pr("FAIL: Send/recv mismatch") + ksft_pr("hash expected:", snd_hash.hexdigest()) + ksft_pr("hash received:", rcv_hash.hexdigest()) + return 1 + ksft_pr(f"{key[0]}/{key[1]}: ok") + return 0 + +def snd_rcv_packets(env): + """ + Send packets on the given network interfaces + + :param env: transport-environment dict for setup_tcp() / setup_rdma(). + "addrs": list of (ip, port) tuples matching the sockets + "netns": list of netns names for TCP or None for RDMA + "flags": OP_FLAG_TCP or OP_FLAG_RDMA, selects sockets + """ + + addrs = env["addrs"] + netns_list = env["netns"] + flags = env.get("flags", 0) + + if (flags & OP_FLAG_TCP) and (flags & OP_FLAG_RDMA): + raise RuntimeError(f"Invalid transport flag sets multiple transports: {flags}") + + if flags & OP_FLAG_TCP: + sockets = [ + netns_socket(netns_list[0], socket.AF_RDS, socket.SOCK_SEQPACKET), + netns_socket(netns_list[1], socket.AF_RDS, socket.SOCK_SEQPACKET), + ] + + # Pin the sockets to the TCP transport so it doesn't fail over to a + # different transport during this test + for s in sockets: + s.setsockopt(SOL_RDS, SO_RDS_TRANSPORT, RDS_TRANS_TCP) + elif flags & OP_FLAG_RDMA: + sockets = [ + socket.socket(socket.AF_RDS, socket.SOCK_SEQPACKET), + socket.socket(socket.AF_RDS, socket.SOCK_SEQPACKET), + ] + + # Pin the sockets to the RDMA transport so it doesn't fail over to a + # different transport during this test + for s in sockets: + s.setsockopt(SOL_RDS, SO_RDS_TRANSPORT, RDS_TRANS_IB) + else: + raise RuntimeError(f"Invalid transport flag sets no transports: {flags}") + + for s, addr in zip(sockets, addrs): + s.bind(addr) + s.setblocking(0) + + send_hashes = {} + recv_hashes = {} + + ep = select.epoll() + + for s in sockets: + ep.register(s, select.EPOLLRDNORM) + + num_packets = 50000 + nr_send = 0 + nr_recv = 0 + + while nr_send < num_packets: + + # Send as much as we can without blocking + ksft_pr("sending...", nr_send, nr_recv) + nr_send = send_burst(sockets, addrs, send_hashes, nr_send, num_packets) + + # Receive as much as we can without blocking + ksft_pr("receiving...", nr_send, nr_recv) + while nr_recv < nr_send: + nr_recv = recv_burst(ep, sockets, addrs, recv_hashes, nr_recv) + + # exercise net/rds/tcp.c:rds_tcp_sysctl_reset() + if netns_list: + for net in netns_list: + ip(f"netns exec {net} /usr/sbin/sysctl net.rds.tcp.rds_tcp_rcvbuf=10000") + ip(f"netns exec {net} /usr/sbin/sysctl net.rds.tcp.rds_tcp_sndbuf=10000") + + ksft_pr("done", nr_send, nr_recv) + + check_info(sockets) + + # We're done sending and receiving stuff, now let's check if what + # we received is what we sent. + rc = verify_hashes(send_hashes, recv_hashes) + + ep.close() + for s in sockets: + s.close() + + return rc + +def stop_pcaps(): + """Stop tcpdump processes. + + We use pop() here to drain the list in the event that the test + completes after the signal handler is fired. List will be empty + if logdir is not set + """ + + if not tcpdump_procs: + return + + ksft_pr("Stopping network packet captures") + while tcpdump_procs: + proc = tcpdump_procs.pop() + proc.terminate() + try: + proc.wait(timeout=5) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait() + def signal_handler(_sig, _frame): """ Test timed out signal handler """ - print('Test timed out') + ksft_pr(f"Test timed out: {signal_handler_label}") + print(f"not ok {tap_idx} rds selftest {signal_handler_label}") sys.exit(1) +def setup_tcp(): + """ + Configure tcp network + """ + + # clean up any leftovers from a previously interrupted run + teardown_tcp() + + ip(f"netns add {NET0}") + ip(f"netns add {NET1}") + ip("link add type veth") + + # Move TCP interfaces into separate namespaces so they can no longer be + # bound directly; this prevents rds from switching over from the tcp + # transport to the loop transport. + ip(f"link set {VETH0} netns {NET0} up") + ip(f"link set {VETH1} netns {NET1} up") + + # add addresses + ip(f"-n {NET0} addr add {tcp_addrs[0][0]}/32 dev {VETH0}") + ip(f"-n {NET1} addr add {tcp_addrs[1][0]}/32 dev {VETH1}") + + # add routes + ip(f"-n {NET0} route add {tcp_addrs[1][0]}/32 dev {VETH0}") + ip(f"-n {NET1} route add {tcp_addrs[0][0]}/32 dev {VETH1}") + + # sanity check that our two interfaces/addresses are correctly set up + # and communicating by doing a single ping + ip(f"netns exec {NET0} ping -c 1 {tcp_addrs[1][0]}") + + # Start a packet capture on each network + if logdir is not None: + for netn in [NET0, NET1]: + pcap = logdir+'/rds-'+netn+'.pcap' + + tcpdump_cmd = ['ip', 'netns', 'exec', netn, '/usr/sbin/tcpdump'] + sudo_user = os.environ.get('SUDO_USER') + if sudo_user: + tcpdump_cmd.extend(['-Z', sudo_user]) + tcpdump_cmd.extend(['-i', 'any', '-w', pcap]) + + # pylint: disable-next=consider-using-with + p = subprocess.Popen(tcpdump_cmd, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + tcpdump_procs.append(p) + + # simulate packet loss, duplication and corruption + for netn, iface in [(NET0, VETH0), (NET1, VETH1)]: + ip(f"netns exec {netn} /usr/sbin/tc qdisc add dev {iface} root netem \ + corrupt {PACKET_CORRUPTION} loss {PACKET_LOSS} duplicate \ + {PACKET_DUPLICATE}") + +def teardown_tcp(): + """ + Tear down the tcp network configured by setup_tcp(). + + Removing the namespaces also removes the veth pair, addresses, + routes, and netem qdisc that live inside them. fail=False so + this is safe to call in error paths after a partial or complete setup. + """ + cmd(f"ip netns del {NET0}", fail=False) + cmd(f"ip netns del {NET1}", fail=False) + +def get_iface_mac(iface): + """Return the MAC address of a local network interface.""" + out = subprocess.check_output(['ip', 'link', 'show', iface], text=True) + mac = re.search(r'link/ether\s+([0-9a-f:]+)', out) + if not mac: + raise RuntimeError(f"Cannot determine MAC address of {iface}") + return mac.group(1) + +def setup_rdma(): + """ + Configure rdma network + """ + + # remove links left over by previously interrupted run. + teardown_rdma() + + # use call here since modprobe may fail if the rdma_rxe + # module is built-in + subprocess.call(['modprobe', 'rdma_rxe'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + ip(f"link add {VETH_RDMA0} type veth peer name {VETH_RDMA1}") + + ip(f"link set {VETH_RDMA0} up") + ip(f"link set {VETH_RDMA1} up") + + # Since both addresses are in the same namespace, the source address + # is always local, so enable accept_local + cmd(f"/usr/sbin/sysctl -q net.ipv4.conf.{VETH_RDMA0}.accept_local=1") + cmd(f"/usr/sbin/sysctl -q net.ipv4.conf.{VETH_RDMA1}.accept_local=1") + + # Reverse path filters must be disabled so that the local routes don't + # cause RPF failures. + cmd(f"/usr/sbin/sysctl -q net.ipv4.conf.{VETH_RDMA0}.rp_filter=0") + cmd(f"/usr/sbin/sysctl -q net.ipv4.conf.{VETH_RDMA1}.rp_filter=0") + + # add addresses + ip(f"addr add {rdma_addrs[0][0]}/32 dev {VETH_RDMA0}") + ip(f"addr add {rdma_addrs[1][0]}/32 dev {VETH_RDMA1}") + + # add routes + ip(f"route add {rdma_addrs[1][0]}/32 dev {VETH_RDMA0}") + ip(f"route add {rdma_addrs[0][0]}/32 dev {VETH_RDMA1}") + + # ARP will not resolve neighbor IPs on /32 routes without a subnet. + # Avoid this by adding neighbors directly so RDMA CM can populate path + # records with correct mac addrs without waiting for the ARP. + mac0 = get_iface_mac(VETH_RDMA0) + mac1 = get_iface_mac(VETH_RDMA1) + ip(f"neigh add {rdma_addrs[1][0]} lladdr {mac1} dev {VETH_RDMA0} nud permanent") + ip(f"neigh add {rdma_addrs[0][0]} lladdr {mac0} dev {VETH_RDMA1} nud permanent") + + cmd(f'rdma link add {RXE_DEV0} type rxe netdev {VETH_RDMA0}') + cmd(f'rdma link add {RXE_DEV1} type rxe netdev {VETH_RDMA1}') + + time.sleep(1) # allow RXE devices to initialise + + # Start a packet capture on each network + if logdir is not None: + for iface in [VETH_RDMA0, VETH_RDMA1]: + pcap = logdir+'/rds-roce-'+iface+'.pcap' + + tcpdump_cmd = ['/usr/sbin/tcpdump'] + sudo_user = os.environ.get('SUDO_USER') + if sudo_user: + tcpdump_cmd.extend(['-Z', sudo_user]) + tcpdump_cmd.extend(['-i', iface, '-w', pcap]) + + # pylint: disable-next=consider-using-with + p = subprocess.Popen(tcpdump_cmd, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + tcpdump_procs.append(p) + + # simulate packet loss, duplication and corruption + for iface in [VETH_RDMA0, VETH_RDMA1]: + cmd(f"/usr/sbin/tc qdisc add dev {iface} root netem \ + corrupt {PACKET_CORRUPTION} loss {PACKET_LOSS} duplicate \ + {PACKET_DUPLICATE}") + +def teardown_rdma(): + """ + Tear down the rdma network configured by setup_rdma(). + """ + + # remove links left over by previously interrupted run. + cmd(f'rdma link del {RXE_DEV0}', fail=False) + cmd(f'rdma link del {RXE_DEV1}', fail=False) + cmd(f'ip link del {VETH_RDMA0}', fail=False) + + #Parse out command line arguments. We take an optional # timeout parameter and an optional log output folder parser = argparse.ArgumentParser(description="init script args", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("-d", "--logdir", action="store", - help="directory to store logs", default="/tmp") -parser.add_argument('--timeout', help="timeout to terminate hung test", + help="directory to store logs", default=None) +parser.add_argument("-T", "--transport", default="tcp", + help="Comma-separated list of transports to test: " + "tcp, rdma, or tcp,rdma. Each matching test " + "is run once per transport. " + "'rdma' requires CONFIG_RDS_RDMA and rdma_rxe.") +parser.add_argument('-t', '--timeout', help="timeout to terminate hung test", type=int, default=0) parser.add_argument('-l', '--loss', help="Simulate tcp packet loss", type=int, default=0) @@ -93,183 +494,63 @@ PACKET_LOSS=str(args.loss)+'%' PACKET_CORRUPTION=str(args.corruption)+'%' PACKET_DUPLICATE=str(args.duplicate)+'%' -ip(f"netns add {NET0}") -ip(f"netns add {NET1}") -ip("link add type veth") +# check transport is either tcp or rdma +transports = [t.strip() for t in args.transport.split(',')] +for t in transports: + if t not in ('tcp', 'rdma'): + raise SystemExit(f"test.py: unknown transport: {t!r}") -addrs = [ - # we technically don't need different port numbers, but this will - # help identify traffic in the network analyzer - ('10.0.0.1', 10000), - ('10.0.0.2', 20000), -] +# Register stop_pcaps before any network setups so that any partially setup +# tcpdumps are still cleaned up on error +atexit.register(stop_pcaps) -# move interfaces to separate namespaces so they can no longer be -# bound directly; this prevents rds from switching over from the tcp -# transport to the loop transport. -ip(f"link set {VETH0} netns {NET0} up") -ip(f"link set {VETH1} netns {NET1} up") +# Set up all requested transports upfront so network plumbing is +# ready before any test runs. +transport_envs = {} +FLAGS = 0 +if 'tcp' in transports: + # Register cleanups before setups to handle partial setups that error'd out + atexit.register(teardown_tcp) + setup_tcp() + transport_envs['tcp'] = { + 'addrs': tcp_addrs, + 'netns': [NET0, NET1], + 'flags': FLAGS | OP_FLAG_TCP, + } +if 'rdma' in transports: + atexit.register(teardown_rdma) + setup_rdma() + transport_envs['rdma'] = { + 'addrs': rdma_addrs, + 'netns': None, + 'flags': FLAGS | OP_FLAG_RDMA, + } +print("TAP version 13") +print(f"1..{len(transport_envs)}") -# add addresses -ip(f"-n {NET0} addr add {addrs[0][0]}/32 dev {VETH0}") -ip(f"-n {NET1} addr add {addrs[1][0]}/32 dev {VETH1}") +for transport, tenv in transport_envs.items(): + tap_idx += 1 -# add routes -ip(f"-n {NET0} route add {addrs[1][0]}/32 dev {VETH0}") -ip(f"-n {NET1} route add {addrs[0][0]}/32 dev {VETH1}") + # add a timeout + if args.timeout > 0: + signal_handler_label = transport + signal.alarm(args.timeout) + signal.signal(signal.SIGALRM, signal_handler) -# sanity check that our two interfaces/addresses are correctly set up -# and communicating by doing a single ping -ip(f"netns exec {NET0} ping -c 1 {addrs[1][0]}") + ret = snd_rcv_packets(tenv) -# Start a packet capture on each network -tcpdump_procs = [] -for net in [NET0, NET1]: - pcap = logdir+'/'+net+'.pcap' - fd, pcap_tmp = tempfile.mkstemp(suffix=".pcap", prefix=f"{net}-", dir="/tmp") - p = subprocess.Popen( - ['ip', 'netns', 'exec', net, - '/usr/sbin/tcpdump', '-i', 'any', '-w', pcap_tmp]) - tcpdump_procs.append((p, pcap_tmp, pcap, fd)) + # cancel timeout + signal.alarm(0) -# simulate packet loss, duplication and corruption -for net, iface in [(NET0, VETH0), (NET1, VETH1)]: - ip(f"netns exec {net} /usr/sbin/tc qdisc add dev {iface} root netem \ - corrupt {PACKET_CORRUPTION} loss {PACKET_LOSS} duplicate \ - {PACKET_DUPLICATE}") + if ret == 0: + ksft_pr("Success") + print(f"ok {tap_idx} rds selftest {transport}") + nr_pass += 1 + else: + print(f"not ok {tap_idx} rds selftest {transport}") + nr_fail += 1 -# add a timeout -if args.timeout > 0: - signal.alarm(args.timeout) - signal.signal(signal.SIGALRM, signal_handler) - -sockets = [ - netns_socket(NET0, socket.AF_RDS, socket.SOCK_SEQPACKET), - netns_socket(NET1, socket.AF_RDS, socket.SOCK_SEQPACKET), -] - -for s, addr in zip(sockets, addrs): - s.bind(addr) - s.setblocking(0) - -fileno_to_socket = { - s.fileno(): s for s in sockets -} - -addr_to_socket = dict(zip(addrs, sockets)) - -socket_to_addr = { - s: addr for addr, s in zip(addrs, sockets) -} - -send_hashes = {} -recv_hashes = {} - -ep = select.epoll() - -for s in sockets: - ep.register(s, select.EPOLLRDNORM) - -NUM_PACKETS = 50000 -nr_send = 0 -nr_recv = 0 - -while nr_send < NUM_PACKETS: - # Send as much as we can without blocking - print("sending...", nr_send, nr_recv) - while nr_send < NUM_PACKETS: - send_data = hashlib.sha256( - f'packet {nr_send}'.encode('utf-8')).hexdigest().encode('utf-8') - - # pseudo-random send/receive pattern - sender = sockets[nr_send % 2] - receiver = sockets[1 - (nr_send % 3) % 2] - - try: - sender.sendto(send_data, socket_to_addr[receiver]) - send_hashes.setdefault((sender.fileno(), receiver.fileno()), - hashlib.sha256()).update(f'<{send_data}>'.encode('utf-8')) - nr_send = nr_send + 1 - except BlockingIOError as e: - break - except OSError as e: - if e.errno in [errno.ENOBUFS, errno.ECONNRESET, errno.EPIPE]: - break - raise - - # Receive as much as we can without blocking - print("receiving...", nr_send, nr_recv) - while nr_recv < nr_send: - for fileno, eventmask in ep.poll(): - receiver = fileno_to_socket[fileno] - - if eventmask & select.EPOLLRDNORM: - while True: - try: - recv_data, address = receiver.recvfrom(1024) - sender = addr_to_socket[address] - recv_hashes.setdefault((sender.fileno(), - receiver.fileno()), hashlib.sha256()).update( - f'<{recv_data}>'.encode('utf-8')) - nr_recv = nr_recv + 1 - except BlockingIOError as e: - break - - # exercise net/rds/tcp.c:rds_tcp_sysctl_reset() - for net in [NET0, NET1]: - ip(f"netns exec {net} /usr/sbin/sysctl net.rds.tcp.rds_tcp_rcvbuf=10000") - ip(f"netns exec {net} /usr/sbin/sysctl net.rds.tcp.rds_tcp_sndbuf=10000") - -print("done", nr_send, nr_recv) - -# the Python socket module doesn't know these -RDS_INFO_FIRST = 10000 -RDS_INFO_LAST = 10017 - -nr_success = 0 -nr_error = 0 - -for s in sockets: - for optname in range(RDS_INFO_FIRST, RDS_INFO_LAST + 1): - # Sigh, the Python socket module doesn't allow us to pass - # buffer lengths greater than 1024 for some reason. RDS - # wants multiple pages. - try: - s.getsockopt(socket.SOL_RDS, optname, 1024) - nr_success = nr_success + 1 - except OSError as e: - nr_error = nr_error + 1 - if e.errno == errno.ENOSPC: - # ignore - pass - -print(f"getsockopt(): {nr_success}/{nr_error}") - -print("Stopping network packet captures") -for p, pcap_tmp, pcap, fd in tcpdump_procs: - p.terminate() - p.wait() - os.close(fd) - shutil.move(pcap_tmp, pcap) - -# We're done sending and receiving stuff, now let's check if what -# we received is what we sent. -for (sender, receiver), send_hash in send_hashes.items(): - recv_hash = recv_hashes.get((sender, receiver)) - - if recv_hash is None: - print("FAIL: No data received") - sys.exit(1) - - if send_hash.hexdigest() != recv_hash.hexdigest(): - print("FAIL: Send/recv mismatch") - print("hash expected:", send_hash.hexdigest()) - print("hash received:", recv_hash.hexdigest()) - sys.exit(1) - - print(f"{sender}/{receiver}: ok") - -print("Success") -sys.exit(0) +ksft_pr(f"Totals: pass:{nr_pass} fail:{nr_fail} skip:0") +sys.exit(1 if nr_fail else 0) diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py index e9ad5e88da97..3622413d793d 100755 --- a/tools/testing/selftests/net/rtnetlink.py +++ b/tools/testing/selftests/net/rtnetlink.py @@ -1,17 +1,20 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 -from lib.py import ksft_exit, ksft_run, ksft_ge, RtnlAddrFamily import socket +import time +from lib.py import bkg, ip, ksft_exit, ksft_run, ksft_ge, ksft_true, KsftSkipEx +from lib.py import CmdExitFailure, NetNS, NetNSEnter, RtnlAddrFamily IPV4_ALL_HOSTS_MULTICAST = b'\xe0\x00\x00\x01' -def dump_mcaddr_check(rtnl: RtnlAddrFamily) -> None: +def dump_mcaddr_check() -> None: """ Verify that at least one interface has the IPv4 all-hosts multicast address. At least the loopback interface should have this address. """ + rtnl = RtnlAddrFamily() addresses = rtnl.getmulticast({"ifa-family": socket.AF_INET}, dump=True) all_host_multicasts = [ @@ -21,9 +24,39 @@ def dump_mcaddr_check(rtnl: RtnlAddrFamily) -> None: ksft_ge(len(all_host_multicasts), 1, "No interface found with the IPv4 all-hosts multicast address") +def ipv4_devconf_notify() -> None: + """ + Configure an interface and set ipv4-devconf values through netlink + to verify that the appropriate netlink notifications are being sent. + """ + + with NetNS() as ns: + with NetNSEnter(str(ns)): + ifname = "dummy1" + ip(f"link add name {ifname} type dummy", ns=str(ns)) + + with bkg("ip monitor", ns=str(ns)) as cmd_obj: + time.sleep(1) + try: + ip(f"link set dev {ifname} inet forwarding on") + ip(f"link set dev {ifname} inet proxy_arp on") + ip(f"link set dev {ifname} inet rp_filter 1") + ip(f"link set dev {ifname} inet ignore_routes_with_linkdown on") + except CmdExitFailure: + raise KsftSkipEx("iproute2 does not support IPv4 devconf attributes") + time.sleep(1) + + ksft_true(f"inet {ifname} ignore_routes_with_linkdown on" in cmd_obj.stdout, + f"No 'ignore_routes_with_linkdown on' notificiation found for interface {ifname}") + ksft_true(f"inet {ifname} rp_filter strict" in cmd_obj.stdout, + f"No 'rp_filter strict' notificiation found for interface {ifname}") + ksft_true(f"inet {ifname} proxy_neigh on" in cmd_obj.stdout, + f"No 'proxy_neigh on' notificiation found for interface {ifname}") + ksft_true(f"inet {ifname} forwarding on" in cmd_obj.stdout, + f"No 'forwarding on' notificiation found for interface {ifname}") + def main() -> None: - rtnl = RtnlAddrFamily() - ksft_run([dump_mcaddr_check], args=(rtnl, )) + ksft_run([dump_mcaddr_check, ipv4_devconf_notify]) ksft_exit() if __name__ == "__main__": diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh deleted file mode 100755 index 5e861ad32a42..000000000000 --- a/tools/testing/selftests/net/so_txtime.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0 -# -# Regression tests for the SO_TXTIME interface - -set -e - -readonly ksft_skip=4 -readonly DEV="veth0" -readonly BIN="./so_txtime" - -readonly RAND="$(mktemp -u XXXXXX)" -readonly NSPREFIX="ns-${RAND}" -readonly NS1="${NSPREFIX}1" -readonly NS2="${NSPREFIX}2" - -readonly SADDR4='192.168.1.1' -readonly DADDR4='192.168.1.2' -readonly SADDR6='fd::1' -readonly DADDR6='fd::2' - -cleanup() { - ip netns del "${NS2}" - ip netns del "${NS1}" -} - -trap cleanup EXIT - -# Create virtual ethernet pair between network namespaces -ip netns add "${NS1}" -ip netns add "${NS2}" - -ip link add "${DEV}" netns "${NS1}" type veth \ - peer name "${DEV}" netns "${NS2}" - -# Bring the devices up -ip -netns "${NS1}" link set "${DEV}" up -ip -netns "${NS2}" link set "${DEV}" up - -# Set fixed MAC addresses on the devices -ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02 -ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06 - -# Add fixed IP addresses to the devices -ip -netns "${NS1}" addr add 192.168.1.1/24 dev "${DEV}" -ip -netns "${NS2}" addr add 192.168.1.2/24 dev "${DEV}" -ip -netns "${NS1}" addr add fd::1/64 dev "${DEV}" nodad -ip -netns "${NS2}" addr add fd::2/64 dev "${DEV}" nodad - -run_test() { - local readonly IP="$1" - local readonly CLOCK="$2" - local readonly TXARGS="$3" - local readonly RXARGS="$4" - - if [[ "${IP}" == "4" ]]; then - local readonly SADDR="${SADDR4}" - local readonly DADDR="${DADDR4}" - elif [[ "${IP}" == "6" ]]; then - local readonly SADDR="${SADDR6}" - local readonly DADDR="${DADDR6}" - else - echo "Invalid IP version ${IP}" - exit 1 - fi - - local readonly START="$(date +%s%N --date="+ 0.1 seconds")" - - ip netns exec "${NS2}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${RXARGS}" -r & - ip netns exec "${NS1}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${TXARGS}" - wait "$!" -} - -do_test() { - run_test $@ - [ $? -ne 0 ] && ret=1 -} - -do_fail_test() { - run_test $@ - [ $? -eq 0 ] && ret=1 -} - -ip netns exec "${NS1}" tc qdisc add dev "${DEV}" root fq -set +e -ret=0 -do_test 4 mono a,-1 a,-1 -do_test 6 mono a,0 a,0 -do_test 6 mono a,10 a,10 -do_test 4 mono a,10,b,20 a,10,b,20 -do_test 6 mono a,20,b,10 b,20,a,20 - -if ip netns exec "${NS1}" tc qdisc replace dev "${DEV}" root etf clockid CLOCK_TAI delta 400000; then - do_fail_test 4 tai a,-1 a,-1 - do_fail_test 6 tai a,0 a,0 - do_test 6 tai a,10 a,10 - do_test 4 tai a,10,b,20 a,10,b,20 - do_test 6 tai a,20,b,10 b,10,a,20 -else - echo "tc ($(tc -V)) does not support qdisc etf. skipping" - [ $ret -eq 0 ] && ret=$ksft_skip -fi - -if [ $ret -eq 0 ]; then - echo OK. All tests passed -elif [[ $ret -ne $ksft_skip && -n "$KSFT_MACHINE_SLOW" ]]; then - echo "Ignoring errors due to slow environment" 1>&2 - ret=0 -fi -exit $ret diff --git a/tools/testing/selftests/net/tcp_ao/config b/tools/testing/selftests/net/tcp_ao/config index f22148512365..1b120bfd89c4 100644 --- a/tools/testing/selftests/net/tcp_ao/config +++ b/tools/testing/selftests/net/tcp_ao/config @@ -1,7 +1,3 @@ -CONFIG_CRYPTO_CMAC=y -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_RMD160=y -CONFIG_CRYPTO_SHA1=y CONFIG_IPV6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NET_L3_MASTER_DEV=y diff --git a/tools/testing/selftests/net/tcp_ao/key-management.c b/tools/testing/selftests/net/tcp_ao/key-management.c index 69d9a7a05d5c..d86bb380b79f 100644 --- a/tools/testing/selftests/net/tcp_ao/key-management.c +++ b/tools/testing/selftests/net/tcp_ao/key-management.c @@ -380,31 +380,6 @@ static void check_listen_socket(void) close(sk); } -static const char *fips_fpath = "/proc/sys/crypto/fips_enabled"; -static bool is_fips_enabled(void) -{ - static int fips_checked = -1; - FILE *fenabled; - int enabled; - - if (fips_checked >= 0) - return !!fips_checked; - if (access(fips_fpath, R_OK)) { - if (errno != ENOENT) - test_error("Can't open %s", fips_fpath); - fips_checked = 0; - return false; - } - fenabled = fopen(fips_fpath, "r"); - if (!fenabled) - test_error("Can't open %s", fips_fpath); - if (fscanf(fenabled, "%d", &enabled) != 1) - test_error("Can't read from %s", fips_fpath); - fclose(fenabled); - fips_checked = !!enabled; - return !!fips_checked; -} - struct test_key { char password[TCP_AO_MAXKEYLEN]; const char *alg; @@ -430,14 +405,7 @@ struct key_collection { static struct key_collection collection; #define TEST_MAX_MACLEN 16 -const char *test_algos[] = { - "cmac(aes128)", - "hmac(sha1)", "hmac(sha512)", "hmac(sha384)", "hmac(sha256)", - "hmac(sha224)", "hmac(sha3-512)", - /* only if !CONFIG_FIPS */ -#define TEST_NON_FIPS_ALGOS 2 - "hmac(rmd160)", "hmac(md5)" -}; +const char *test_algos[] = { "cmac(aes128)", "hmac(sha1)", "hmac(sha256)" }; const unsigned int test_maclens[] = { 1, 4, 12, 16 }; #define MACLEN_SHIFT 2 #define ALGOS_SHIFT 4 @@ -452,7 +420,7 @@ static unsigned int make_mask(unsigned int shift, unsigned int prev_shift) static void init_key_in_collection(unsigned int index, bool randomized) { struct test_key *key = &collection.keys[index]; - unsigned int algos_nr, algos_index; + unsigned int algos_index; /* Same for randomized and non-randomized test flows */ key->client_keyid = index; @@ -474,10 +442,7 @@ static void init_key_in_collection(unsigned int index, bool randomized) key->maclen = test_maclens[index & make_mask(shift, 0)]; algos_index = index & make_mask(ALGOS_SHIFT, shift); } - algos_nr = ARRAY_SIZE(test_algos); - if (is_fips_enabled()) - algos_nr -= TEST_NON_FIPS_ALGOS; - key->alg = test_algos[algos_index % algos_nr]; + key->alg = test_algos[algos_index % ARRAY_SIZE(test_algos)]; } static int init_default_key_collection(unsigned int nr_keys, bool randomized) diff --git a/tools/testing/selftests/net/test_bridge_neigh_suppress.sh b/tools/testing/selftests/net/test_bridge_neigh_suppress.sh index 9067197c9055..e9ed0d750996 100755 --- a/tools/testing/selftests/net/test_bridge_neigh_suppress.sh +++ b/tools/testing/selftests/net/test_bridge_neigh_suppress.sh @@ -56,6 +56,12 @@ TESTS=" neigh_suppress_uc_ns neigh_vlan_suppress_arp neigh_vlan_suppress_ns + neigh_suppress_arp_probe + neigh_suppress_dad_ns + neigh_forward_grat_arp + neigh_forward_grat_na + neigh_vlan_forward_grat_arp + neigh_vlan_forward_grat_na " VERBOSE=0 PAUSE_ON_FAIL=no @@ -74,7 +80,8 @@ log_test() printf "TEST: %-60s [ OK ]\n" "${msg}" nsuccess=$((nsuccess+1)) else - ret=1 + # shellcheck disable=SC2154 + ret=$(ksft_exit_status_merge "$ret" "$ksft_fail") nfail=$((nfail+1)) printf "TEST: %-60s [FAIL]\n" "${msg}" if [ "$VERBOSE" = "1" ]; then @@ -97,6 +104,7 @@ log_test() fi [ "$VERBOSE" = "1" ] && echo + return 0 } run_cmd() @@ -134,6 +142,15 @@ tc_check_packets() [[ $pkts == $count ]] } +neigh_forward_grat_check() +{ + if ! bridge link help 2>&1 | grep -q "neigh_forward_grat"; then + echo "SKIP: iproute2 bridge too old, missing gratuitous ARP/unsolicited NA forwarding control support" + # shellcheck disable=SC2154 + return "$ksft_skip" + fi +} + ################################################################################ # Setup @@ -561,6 +578,17 @@ icmpv6_header_get() echo $p } +icmpv6_na_header_get() +{ + local csum=$1; shift + local tip=$1; shift + + # Type 136 (Neighbor Advertisement), hex format, Override flag set, + # Solicited flag clear (unsolicited NA). + # ICMPv6.type : ICMPv6.code : ICMPv6.checksum : Flags : Target Address + echo "88:00:$csum:20:00:00:00:$tip:" +} + neigh_suppress_uc_ns_common() { local vid=$1; shift @@ -875,6 +903,439 @@ neigh_vlan_suppress_ns() log_test $? 0 "NS suppression (VLAN $vid2)" } +neigh_suppress_arp_probe() +{ + local vid=10 + local tip=192.0.2.2 + local h2_mac + + echo + echo "Per-port ARP probe suppression" + echo "------------------------------" + + run_cmd "tc -n $sw1 qdisc replace dev vx0 clsact" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 101 proto 0x0806 flower indev swp1 arp_tip $tip arp_sip 0.0.0.0 arp_op request action pass" + + # Initial state - check that ARP probes are not suppressed. + run_cmd "ip netns exec $h1 arping -D -q -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "ARP probe suppression" + + # Enable neighbor suppression and check that nothing changes. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on" + + run_cmd "ip netns exec $h1 arping -D -q -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 2 + log_test $? 0 "ARP probe suppression" + + # Install FDB and a neighbor and check that ARP probes are suppressed. + h2_mac=$(ip -n "$h2" -j -p link show eth0."$vid" | jq -r '.[]["address"]') + run_cmd "bridge -n $sw1 fdb replace $h2_mac dev vx0 master static vlan $vid" + run_cmd "ip -n $sw1 neigh replace $tip lladdr $h2_mac nud permanent dev br0.$vid" + log_test $? 0 "FDB and neighbor entry installation" + + run_cmd "ip netns exec $h1 arping -D -q -c 1 -w 5 -I eth0.$vid $tip" + log_test $? 1 "arping" + tc_check_packets "$sw1" "dev vx0 egress" 101 2 + log_test $? 0 "ARP probe suppression" + + # Remove the neighbor entry and check that ARP probes are not suppressed. + run_cmd "ip -n $sw1 neigh del $tip dev br0.$vid" + log_test $? 0 "neighbor removal" + + run_cmd "ip netns exec $h1 arping -D -q -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 3 + log_test $? 0 "ARP probe suppression" + + # Disable neighbor suppression. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_suppress off" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_suppress off\"" + log_test $? 0 "\"neigh_suppress\" is off" + + run_cmd "ip netns exec $h1 arping -D -q -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 4 + log_test $? 0 "ARP probe suppression" +} + +neigh_suppress_dad_ns() +{ + local vid=10 + local tip=2001:db8:1::99 + local mcast=ff02::1:ff00:99 + local dmac=33:33:ff:00:00:99 + local full_tip=20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:99 + local csum="4b:bc" + local smac + local tmac + + echo + echo "Per-port DAD NS suppression" + echo "---------------------------" + + smac=$(ip -n "$h1" -j -p link show eth0."$vid" | jq -r '.[]["address"]') + + run_cmd "tc -n $sw1 qdisc replace dev vx0 clsact" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 101 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $mcast src_ip :: type 135 code 0 action pass" + + # Initial state - check that DAD NS are not suppressed. + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A :: -B $mcast -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum" "$full_tip") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "DAD NS suppression" + + # Enable neighbor suppression and check that nothing changes. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A :: -B $mcast -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum" "$full_tip") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 2 + log_test $? 0 "DAD NS suppression" + + # Install FDB and a neighbor and check that DAD NS are suppressed + # and that a proxy NA is sent back to h1. + tmac=$(ip -n "$h2" -j -p link show eth0."$vid" | jq -r '.[]["address"]') + run_cmd "bridge -n $sw1 fdb replace $tmac dev vx0 master static vlan $vid" + run_cmd "ip -n $sw1 -6 neigh replace $tip lladdr $tmac nud permanent dev br0.$vid" + log_test $? 0 "FDB and neighbor entry installation" + + run_cmd "tc -n $h1 qdisc replace dev eth0.$vid clsact" + run_cmd "tc -n $h1 filter replace dev eth0.$vid ingress pref 1 handle 101 proto ipv6 flower ip_proto icmpv6 dst_ip ff02::1 src_ip $tip type 136 code 0 action pass" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A :: -B $mcast -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum" "$full_tip") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 2 + log_test $? 0 "DAD NS suppression" + tc_check_packets "$h1" "dev eth0.$vid ingress" 101 1 + log_test $? 0 "DAD NS proxy NA reply" + + # Remove the neighbor entry and check that DAD NS are not suppressed. + run_cmd "ip -n $sw1 -6 neigh del $tip dev br0.$vid" + log_test $? 0 "neighbor removal" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A :: -B $mcast -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum" "$full_tip") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 3 + log_test $? 0 "DAD NS suppression" + + # Disable neighbor suppression. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_suppress off" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_suppress off\"" + log_test $? 0 "\"neigh_suppress\" is off" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A :: -B $mcast -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum" "$full_tip") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 4 + log_test $? 0 "DAD NS suppression" +} + +neigh_forward_grat_arp() +{ + local vid=10 + local sip=192.0.2.1 + local tip=$sip + local h2_ip=192.0.2.2 + local h2_mac + + neigh_forward_grat_check || return $? + + echo + echo "Gratuitous ARP forwarding" + echo "-------------------------" + + run_cmd "tc -n $sw1 qdisc replace dev vx0 clsact" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 101 proto 0x0806 flower indev swp1 arp_tip $tip arp_sip $sip arp_op request action pass" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 102 proto 0x0806 flower indev swp1 arp_tip $h2_ip arp_sip $sip arp_op request action pass" + + h2_mac=$(ip -n "$h2" -j -p link show eth0."$vid" | jq -r '.[]["address"]') + run_cmd "bridge -n $sw1 fdb replace $h2_mac dev vx0 master static vlan $vid" + run_cmd "ip -n $sw1 neigh replace $tip lladdr $h2_mac nud permanent dev br0.$vid" + run_cmd "ip -n $sw1 neigh replace $h2_ip lladdr $h2_mac nud permanent dev br0.$vid" + + # Enable neighbor suppression. Gratuitous ARP should be suppressed by + # default (neigh_forward_grat defaults to off). + run_cmd "ip -n $sw1 link set dev vx0 type bridge_slave neigh_suppress on" + run_cmd "ip -n $sw1 -d link show dev vx0 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on" + + # Send gratuitous ARP (sip == tip) and check it's suppressed. + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 0 + log_test $? 0 "Gratuitous ARP suppression" + + # Explicitly enable neigh_forward_grat and verify gratuitous ARP is + # now forwarded. + run_cmd "ip -n $sw1 link set dev vx0 type bridge_slave neigh_forward_grat on" + run_cmd "ip -n $sw1 -d link show dev vx0 | grep \"neigh_forward_grat on\"" + log_test $? 0 "\"neigh_forward_grat\" is on" + + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "Gratuitous ARP forwarding" + + # Verify that regular (non-gratuitous) ARP requests are still + # suppressed when neigh_forward_grat is enabled. + run_cmd "ip netns exec $h1 arping -c 1 -w 5 -I eth0.$vid $h2_ip" + tc_check_packets "$sw1" "dev vx0 egress" 102 0 + log_test $? 0 "Regular ARP suppression with \"neigh_forward_grat\" on" + + # Disable neigh_forward_grat and verify suppression resumes. + run_cmd "ip -n $sw1 link set dev vx0 type bridge_slave neigh_forward_grat off" + run_cmd "ip -n $sw1 -d link show dev vx0 | grep \"neigh_forward_grat off\"" + log_test $? 0 "\"neigh_forward_grat\" is off" + + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid $tip" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "Gratuitous ARP suppression" +} + +# neigh_forward_grat_arp() uses 'ip link' interface, and neigh_forward_grat_na() +# uses 'bridge link' interface to exercise both paths. +neigh_forward_grat_na() +{ + local vid=10 + local saddr=2001:db8:1::1 + local daddr=ff02::1 + local h2_addr=2001:db8:1::2 + local h2_maddr=ff02::1:ff00:2 + local full_addr=20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01 + local h2_full_addr=20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:02 + local csum="fd:32" + local csum_ns="1f:2f" + local dmac=33:33:00:00:00:01 + local h2_dmac=33:33:ff:00:00:02 + local h2_mac + local smac + + neigh_forward_grat_check || return $? + + echo + echo "Unsolicited NA forwarding" + echo "-------------------------" + + smac=$(ip -n "$h1" -j -p link show eth0."$vid" | jq -r '.[]["address"]') + + run_cmd "tc -n $sw1 qdisc replace dev vx0 clsact" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 101 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $daddr src_ip $saddr type 136 code 0 action pass" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 102 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $h2_maddr src_ip $saddr type 135 code 0 action pass" + + h2_mac=$(ip -n "$h2" -j -p link show eth0."$vid" | jq -r '.[]["address"]') + run_cmd "bridge -n $sw1 fdb replace $h2_mac dev vx0 master static vlan $vid" + run_cmd "ip -n $sw1 neigh replace $saddr lladdr $h2_mac nud permanent dev br0.$vid" + run_cmd "ip -n $sw1 neigh replace $h2_addr lladdr $h2_mac nud permanent dev br0.$vid" + + # Enable neighbor suppression. Unsolicited NA should be suppressed by + # default (neigh_forward_grat defaults to off). + run_cmd "bridge -n $sw1 link set dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on" + + # Send unsolicited NA and check it's suppressed. + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A $saddr -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum" "$full_addr") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 0 + log_test $? 0 "Unsolicited NA suppression" + + # Explicitly enable neigh_forward_grat and verify unsolicited NA is + # now forwarded. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_forward_grat on" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_forward_grat on\"" + log_test $? 0 "\"neigh_forward_grat\" is on" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A $saddr -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum" "$full_addr") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "Unsolicited NA forwarding" + + # Verify that solicited NS messages are still suppressed when + # neigh_forward_grat is enabled. + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $h2_dmac -A $saddr -B $h2_maddr -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum_ns" "$h2_full_addr") -q" + tc_check_packets "$sw1" "dev vx0 egress" 102 0 + log_test $? 0 "Solicited NS suppression with \"neigh_forward_grat\" on" + + # Disable neigh_forward_grat and verify suppression resumes. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_forward_grat off" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_forward_grat off\"" + log_test $? 0 "\"neigh_forward_grat\" is off" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid -c 1 -a $smac -b $dmac -A $saddr -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum" "$full_addr") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "Unsolicited NA suppression" +} + +neigh_vlan_forward_grat_arp() +{ + local vid1=10 + local vid2=20 + local sip1=192.0.2.1 + local sip2=192.0.2.17 + local h2_ip1=192.0.2.2 + local h2_mac1 + local h2_mac2 + + neigh_forward_grat_check || return $? + + echo + echo "Per-VLAN gratuitous ARP forwarding" + echo "----------------------------------" + + run_cmd "tc -n $sw1 qdisc replace dev vx0 clsact" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 101 proto 0x0806 flower indev swp1 arp_tip $sip1 arp_sip $sip1 arp_op request action pass" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 102 proto 0x0806 flower indev swp1 arp_tip $sip2 arp_sip $sip2 arp_op request action pass" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 103 proto 0x0806 flower indev swp1 arp_tip $h2_ip1 arp_sip $sip1 arp_op request action pass" + + h2_mac1=$(ip -n "$h2" -j -p link show eth0."$vid1" | jq -r '.[]["address"]') + h2_mac2=$(ip -n "$h2" -j -p link show eth0."$vid2" | jq -r '.[]["address"]') + run_cmd "bridge -n $sw1 fdb replace $h2_mac1 dev vx0 master static vlan $vid1" + run_cmd "bridge -n $sw1 fdb replace $h2_mac2 dev vx0 master static vlan $vid2" + run_cmd "ip -n $sw1 neigh replace $sip1 lladdr $h2_mac1 nud permanent dev br0.$vid1" + run_cmd "ip -n $sw1 neigh replace $sip2 lladdr $h2_mac2 nud permanent dev br0.$vid2" + run_cmd "ip -n $sw1 neigh replace $h2_ip1 lladdr $h2_mac1 nud permanent dev br0.$vid1" + + # Enable per-{Port, VLAN} neighbor suppression. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_vlan_suppress on" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_vlan_suppress on\"" + log_test $? 0 "\"neigh_vlan_suppress\" is on" + + # Enable neighbor suppression on VLAN 10. Gratuitous ARP should be + # suppressed by default on VLAN 10 (neigh_forward_grat defaults to off) + # but not on VLAN 20. + run_cmd "bridge -n $sw1 vlan set vid $vid1 dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on (VLAN $vid1)" + + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid1 $sip1" + tc_check_packets "$sw1" "dev vx0 egress" 101 0 + log_test $? 0 "Gratuitous ARP suppression (VLAN $vid1)" + + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid2 $sip2" + tc_check_packets "$sw1" "dev vx0 egress" 102 1 + log_test $? 0 "Gratuitous ARP forwarding (VLAN $vid2)" + + # Enable neigh_forward_grat on VLAN 10 and verify gratuitous ARP is + # now forwarded. + run_cmd "bridge -n $sw1 vlan set vid $vid1 dev vx0 neigh_forward_grat on" + run_cmd "bridge -n $sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_forward_grat on\"" + log_test $? 0 "\"neigh_forward_grat\" is on (VLAN $vid1)" + + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid1 $sip1" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "Gratuitous ARP forwarding (VLAN $vid1)" + + # Verify that regular (non-gratuitous) ARP requests on VLAN $vid1 are + # still suppressed when neigh_forward_grat is enabled. + run_cmd "ip netns exec $h1 arping -c 1 -w 5 -I eth0.$vid1 $h2_ip1" + tc_check_packets "$sw1" "dev vx0 egress" 103 0 + log_test $? 0 "Regular ARP suppression with \"neigh_forward_grat\" on (VLAN $vid1)" + + # Enable neighbor suppression on VLAN 20 (neigh_forward_grat defaults to + # off), and verify gratuitous ARP is suppressed on VLAN 20. + run_cmd "bridge -n $sw1 vlan set vid $vid2 dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d vlan show dev vx0 vid $vid2 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on (VLAN $vid2)" + + # VLAN 10 should still forward (neigh_forward_grat is on). + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid1 $sip1" + tc_check_packets "$sw1" "dev vx0 egress" 101 2 + log_test $? 0 "Gratuitous ARP forwarding (VLAN $vid1)" + + # VLAN 20 should suppress (neigh_forward_grat defaults to off). + run_cmd "ip netns exec $h1 arping -U -c 1 -w 5 -I eth0.$vid2 $sip2" + tc_check_packets "$sw1" "dev vx0 egress" 102 1 + log_test $? 0 "Gratuitous ARP suppression (VLAN $vid2)" +} + +neigh_vlan_forward_grat_na() +{ + local vid1=10 + local vid2=20 + local saddr1=2001:db8:1::1 + local daddr=ff02::1 + local h2_addr1=2001:db8:1::2 + local h2_maddr1=ff02::1:ff00:2 + local full_addr1=20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01 + local h2_full_addr1=20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:02 + local csum1="fd:32" + local csum_ns1="1f:2f" + local saddr2=2001:db8:2::1 + local full_addr2=20:01:0d:b8:00:02:00:00:00:00:00:00:00:00:00:01 + local csum2="fd:30" + local dmac=33:33:00:00:00:01 + local h2_dmac1=33:33:ff:00:00:02 + local h2_mac1 + local h2_mac2 + local smac + + neigh_forward_grat_check || return $? + + echo + echo "Per-VLAN unsolicited NA forwarding" + echo "----------------------------------" + + smac=$(ip -n "$h1" -j -p link show eth0."$vid1" | jq -r '.[]["address"]') + + run_cmd "tc -n $sw1 qdisc replace dev vx0 clsact" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 101 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $daddr src_ip $saddr1 type 136 code 0 action pass" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 102 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $daddr src_ip $saddr2 type 136 code 0 action pass" + run_cmd "tc -n $sw1 filter replace dev vx0 egress pref 1 handle 103 proto ipv6 flower indev swp1 ip_proto icmpv6 dst_ip $h2_maddr1 src_ip $saddr1 type 135 code 0 action pass" + + h2_mac1=$(ip -n "$h2" -j -p link show eth0."$vid1" | jq -r '.[]["address"]') + h2_mac2=$(ip -n "$h2" -j -p link show eth0."$vid2" | jq -r '.[]["address"]') + run_cmd "bridge -n $sw1 fdb replace $h2_mac1 dev vx0 master static vlan $vid1" + run_cmd "bridge -n $sw1 fdb replace $h2_mac2 dev vx0 master static vlan $vid2" + run_cmd "ip -n $sw1 neigh replace $saddr1 lladdr $h2_mac1 nud permanent dev br0.$vid1" + run_cmd "ip -n $sw1 neigh replace $saddr2 lladdr $h2_mac2 nud permanent dev br0.$vid2" + run_cmd "ip -n $sw1 neigh replace $h2_addr1 lladdr $h2_mac1 nud permanent dev br0.$vid1" + + # Enable per-{Port, VLAN} neighbor suppression. + run_cmd "bridge -n $sw1 link set dev vx0 neigh_vlan_suppress on" + run_cmd "bridge -n $sw1 -d link show dev vx0 | grep \"neigh_vlan_suppress on\"" + log_test $? 0 "\"neigh_vlan_suppress\" is on" + + # Enable neighbor suppression on VLAN 10. Unsolicited NA should be + # suppressed by default on VLAN 10 (neigh_forward_grat defaults to off) + # but not on VLAN 20. + run_cmd "bridge -n $sw1 vlan set vid $vid1 dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on (VLAN $vid1)" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid1 -c 1 -a $smac -b $dmac -A $saddr1 -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum1" "$full_addr1") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 0 + log_test $? 0 "Unsolicited NA suppression (VLAN $vid1)" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid2 -c 1 -a $smac -b $dmac -A $saddr2 -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum2" "$full_addr2") -q" + tc_check_packets "$sw1" "dev vx0 egress" 102 1 + log_test $? 0 "Unsolicited NA forwarding (VLAN $vid2)" + + # Enable neigh_forward_grat on VLAN 10 and verify unsolicited NA is + # now forwarded. + run_cmd "bridge -n $sw1 vlan set vid $vid1 dev vx0 neigh_forward_grat on" + run_cmd "bridge -n $sw1 -d vlan show dev vx0 vid $vid1 | grep \"neigh_forward_grat on\"" + log_test $? 0 "\"neigh_forward_grat\" is on (VLAN $vid1)" + + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid1 -c 1 -a $smac -b $dmac -A $saddr1 -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum1" "$full_addr1") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 1 + log_test $? 0 "Unsolicited NA forwarding (VLAN $vid1)" + + # Verify that solicited NS messages on VLAN $vid1 are still suppressed + # when neigh_forward_grat is enabled. + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid1 -c 1 -a $smac -b $h2_dmac1 -A $saddr1 -B $h2_maddr1 -t ip hop=255,next=58,payload=$(icmpv6_header_get "$csum_ns1" "$h2_full_addr1") -q" + tc_check_packets "$sw1" "dev vx0 egress" 103 0 + log_test $? 0 "Solicited NS suppression with \"neigh_forward_grat\" on (VLAN $vid1)" + + # Enable neighbor suppression on VLAN 20 (neigh_forward_grat defaults to + # off), and verify unsolicited NA is suppressed on VLAN 20. + run_cmd "bridge -n $sw1 vlan set vid $vid2 dev vx0 neigh_suppress on" + run_cmd "bridge -n $sw1 -d vlan show dev vx0 vid $vid2 | grep \"neigh_suppress on\"" + log_test $? 0 "\"neigh_suppress\" is on (VLAN $vid2)" + + # VLAN 10 should still forward (neigh_forward_grat is on). + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid1 -c 1 -a $smac -b $dmac -A $saddr1 -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum1" "$full_addr1") -q" + tc_check_packets "$sw1" "dev vx0 egress" 101 2 + log_test $? 0 "Unsolicited NA forwarding (VLAN $vid1)" + + # VLAN 20 should suppress (neigh_forward_grat defaults to off). + run_cmd "ip netns exec $h1 mausezahn -6 eth0.$vid2 -c 1 -a $smac -b $dmac -A $saddr2 -B $daddr -t ip hop=255,next=58,payload=$(icmpv6_na_header_get "$csum2" "$full_addr2") -q" + tc_check_packets "$sw1" "dev vx0 egress" 102 1 + log_test $? 0 "Unsolicited NA suppression (VLAN $vid2)" +} + ################################################################################ # Usage @@ -961,7 +1422,10 @@ cleanup for t in $TESTS do - setup; $t; cleanup; + setup + $t + ret=$(ksft_exit_status_merge "$ret" $?) + cleanup done if [ "$TESTS" != "none" ]; then diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 30a236b8e9f7..9b9a3cb2700d 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -1549,7 +1549,7 @@ test_mutliproc(struct __test_metadata *_metadata, struct _test_data_tls *self, res = recv(self->cfd, rb, left > sizeof(rb) ? sizeof(rb) : left, 0); - EXPECT_GE(res, 0); + ASSERT_GE(res, 0); left -= res; } } else { @@ -1566,7 +1566,7 @@ test_mutliproc(struct __test_metadata *_metadata, struct _test_data_tls *self, res = send(self->fd, buf, left > file_sz ? file_sz : left, 0); - EXPECT_GE(res, 0); + ASSERT_GE(res, 0); left -= res; } } diff --git a/tools/testing/selftests/tc-testing/config b/tools/testing/selftests/tc-testing/config index c20aa16b1d63..0e5618be0335 100644 --- a/tools/testing/selftests/tc-testing/config +++ b/tools/testing/selftests/tc-testing/config @@ -4,6 +4,7 @@ CONFIG_DUMMY=y CONFIG_VETH=y +CONFIG_IFB=y # # Core Netfilter Configuration diff --git a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py index bb19b8b76d3b..0bece7c74f07 100644 --- a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py +++ b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py @@ -120,6 +120,7 @@ class SubPlugin(TdcPlugin): dev0 = self.args.NAMES["DEV0"]; dev1 = self.args.NAMES["DEV1"]; dummy = self.args.NAMES["DUMMY"]; + ifb = self.args.NAMES['IFB'] if self.args.verbose: print('{}._nl_ns_create'.format(self.sub_class)) @@ -129,6 +130,7 @@ class SubPlugin(TdcPlugin): with IPRoute() as ip: ip.link('add', ifname=dev1, kind='veth', peer={'ifname': dev0, 'net_ns_fd':'/proc/1/ns/net'}) ip.link('add', ifname=dummy, kind='dummy') + ip.link('add', ifname=ifb, kind='ifb') ticks = 20 while True: if ticks == 0: @@ -136,8 +138,10 @@ class SubPlugin(TdcPlugin): try: dev1_idx = ip.link_lookup(ifname=dev1)[0] dummy_idx = ip.link_lookup(ifname=dummy)[0] + ifb_idx = ip.link_lookup(ifname=ifb)[0] ip.link('set', index=dev1_idx, state='up') ip.link('set', index=dummy_idx, state='up') + ip.link('set', index=ifb_idx, state='up') break except: time.sleep(0.1) @@ -169,8 +173,11 @@ class SubPlugin(TdcPlugin): cmds.append(self._replace_keywords('link set $DEV1 netns {}'.format(ns))) cmds.append(self._replace_keywords('link add $DUMMY type dummy'.format(ns))) cmds.append(self._replace_keywords('link set $DUMMY netns {}'.format(ns))) + cmds.append(self._replace_keywords('link add $IFB type ifb')) + cmds.append(self._replace_keywords('link set $IFB netns {}'.format(ns))) cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV1 up'.format(ns))) cmds.append(self._replace_keywords('netns exec {} $IP link set $DUMMY up'.format(ns))) + cmds.append(self._replace_keywords('netns exec {} $IP link set $IFB up'.format(ns))) cmds.append(self._replace_keywords('link set $DEV0 up'.format(ns))) if self.args.device: diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json index 808aef4afe22..ece7ec41bf99 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json @@ -1378,5 +1378,60 @@ "teardown": [ "$TC actions flush action ife" ] + }, + { + "id": "4e6b", + "name": "Decode IFE packet with truncated inner Ethernet header", + "category": [ + "actions", + "ife" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + [ + "$TC actions flush action ife", + 0, + 1, + 255 + ], + "$TC qdisc add dev $DEV1 clsact" + ], + "scapy": [ + { + "iface": "$DEV0", + "count": 1, + "packet": "Ether(type=0xED3E) / Raw(b'\\x00\\x02\\xaa')" + } + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol all matchall action ife decode pipe index 10", + "expExitCode": "0", + "verifyCmd": "$TC -s -j actions get action ife index 10", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "kind": "ife", + "mode": "decode", + "index": 10, + "stats": { + "bytes": 3, + "packets": 1, + "drops": 1 + } + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json b/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json index 37c410332174..d8b685cfc62d 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json @@ -1920,5 +1920,54 @@ "teardown": [ "$TC actions flush action pedit" ] + }, + { + "id": "1a4f", + "name": "Pedit udp dport should not mangle TCP packet dport", + "category": [ + "actions", + "pedit" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + "$TC qdisc add dev $DEV1 clsact", + "$TC filter add dev $DEV1 ingress protocol ip pref 1 matchall action pedit ex munge udp dport set 18053 continue" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol ip pref 2 flower ip_proto tcp dst_port 2222 action drop index 1", + "scapy": { + "iface": "$DEV0", + "count": 1, + "packet": "Ether()/IP(dst='10.10.10.1')/TCP(dport=2222)" + }, + "expExitCode": "0", + "verifyCmd": "$TC -j -s actions get action gact index 1", + "matchJSON": [ + { + "total acts": 0 + }, + { + "actions": [ + { + "order": 1, + "kind": "gact", + "control_action": { + "type": "drop" + }, + "index": 1, + "stats": { + "packets": 1 + } + } + ] + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json index 82c38a13dfbf..a1f97a4b606e 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json @@ -392,26 +392,32 @@ "htb" ], "plugins": { - "requires": "nsPlugin" + "requires": [ + "nsPlugin", + "scapyPlugin" + ] }, "setup": [ - "$IP link set dev $DUMMY up || true", - "$IP addr add 10.10.10.10/24 dev $DUMMY || true", - "$TC qdisc add dev $DUMMY handle 1: root htb default 10", - "$TC class add dev $DUMMY parent 1: classid 1:10 htb rate 1kbit", - "$TC qdisc add dev $DUMMY parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", - "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", - "ping -c 5 -f -I $DUMMY 10.10.10.1 > /dev/null || true", - "sleep 0.1" + "$TC qdisc add dev $IFB handle 1: root htb default 10", + "$TC class add dev $IFB parent 1: classid 1:10 htb rate 1kbit", + "$TC qdisc add dev $IFB parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", + "$TC filter add dev $IFB parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip protocol 1 0xff action mirred egress mirror dev $IFB" ], - "cmdUnderTest": "$TC -s qdisc show dev $DUMMY", + "scapy": { + "iface": "$DEV0", + "count": 5, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + }, + "cmdUnderTest": "$TC -s qdisc show dev $IFB", "expExitCode": "0", - "verifyCmd": "$TC -s qdisc show dev $DUMMY | grep -A 5 'qdisc fq_codel'", + "verifyCmd": "$TC -s qdisc show dev $IFB | grep -A 5 'qdisc fq_codel'", "matchPattern": "dropped [1-9][0-9]*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + "$TC qdisc del dev $IFB root", + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -423,26 +429,32 @@ "qfq" ], "plugins": { - "requires": "nsPlugin" + "requires": [ + "nsPlugin", + "scapyPlugin" + ] }, "setup": [ - "$IP link set dev $DUMMY up || true", - "$IP addr add 10.10.10.10/24 dev $DUMMY || true", - "$TC qdisc add dev $DUMMY handle 1: root qfq", - "$TC class add dev $DUMMY parent 1: classid 1:10 qfq weight 1 maxpkt 1000", - "$TC qdisc add dev $DUMMY parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", - "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", - "ping -c 10 -s 1000 -f -I $DUMMY 10.10.10.1 > /dev/null || true", - "sleep 0.1" + "$TC qdisc add dev $IFB handle 1: root qfq", + "$TC class add dev $IFB parent 1: classid 1:10 qfq weight 1 maxpkt 1000", + "$TC qdisc add dev $IFB parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", + "$TC filter add dev $IFB parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip protocol 1 0xff action mirred egress mirror dev $IFB" ], - "cmdUnderTest": "$TC -s qdisc show dev $DUMMY", + "scapy": { + "iface": "$DEV0", + "count": 10, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + }, + "cmdUnderTest": "$TC -s qdisc show dev $IFB", "expExitCode": "0", - "verifyCmd": "$TC -s qdisc show dev $DUMMY | grep -A 5 'qdisc fq_codel'", + "verifyCmd": "$TC -s qdisc show dev $IFB | grep -A 5 'qdisc fq_codel'", "matchPattern": "dropped [1-9][0-9]*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + "$TC qdisc del dev $IFB root", + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -454,26 +466,32 @@ "hfsc" ], "plugins": { - "requires": "nsPlugin" + "requires": [ + "nsPlugin", + "scapyPlugin" + ] }, "setup": [ - "$IP link set dev $DUMMY up || true", - "$IP addr add 10.10.10.10/24 dev $DUMMY || true", - "$TC qdisc add dev $DUMMY handle 1: root hfsc default 10", - "$TC class add dev $DUMMY parent 1: classid 1:10 hfsc sc rate 1kbit ul rate 1kbit", - "$TC qdisc add dev $DUMMY parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", - "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", - "ping -c 5 -f -I $DUMMY 10.10.10.1 > /dev/null || true", - "sleep 0.1" + "$TC qdisc add dev $IFB handle 1: root hfsc default 10", + "$TC class add dev $IFB parent 1: classid 1:10 hfsc sc rate 1kbit ul rate 1kbit", + "$TC qdisc add dev $IFB parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", + "$TC filter add dev $IFB parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip protocol 1 0xff action mirred egress mirror dev $IFB" ], - "cmdUnderTest": "$TC -s qdisc show dev $DUMMY", + "scapy": { + "iface": "$DEV0", + "count": 5, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + }, + "cmdUnderTest": "$TC -s qdisc show dev $IFB", "expExitCode": "0", - "verifyCmd": "$TC -s qdisc show dev $DUMMY | grep -A 5 'qdisc fq_codel'", + "verifyCmd": "$TC -s qdisc show dev $IFB | grep -A 5 'qdisc fq_codel'", "matchPattern": "dropped [1-9][0-9]*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + "$TC qdisc del dev $IFB root", + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -485,26 +503,32 @@ "drr" ], "plugins": { - "requires": "nsPlugin" + "requires": [ + "nsPlugin", + "scapyPlugin" + ] }, "setup": [ - "$IP link set dev $DUMMY up || true", - "$IP addr add 10.10.10.10/24 dev $DUMMY || true", - "$TC qdisc add dev $DUMMY handle 1: root drr", - "$TC class add dev $DUMMY parent 1: classid 1:10 drr quantum 1500", - "$TC qdisc add dev $DUMMY parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", - "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", - "ping -c 5 -f -I $DUMMY 10.10.10.1 > /dev/null || true", - "sleep 0.1" + "$TC qdisc add dev $IFB handle 1: root drr", + "$TC class add dev $IFB parent 1: classid 1:10 drr quantum 1500", + "$TC qdisc add dev $IFB parent 1:10 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", + "$TC filter add dev $IFB parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:10", + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip protocol 1 0xff action mirred egress mirror dev $IFB" ], - "cmdUnderTest": "$TC -s qdisc show dev $DUMMY", + "scapy": { + "iface": "$DEV0", + "count": 5, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + }, + "cmdUnderTest": "$TC -s qdisc show dev $IFB", "expExitCode": "0", - "verifyCmd": "$TC -s qdisc show dev $DUMMY | grep -A 5 'qdisc fq_codel'", + "verifyCmd": "$TC -s qdisc show dev $IFB | grep -A 5 'qdisc fq_codel'", "matchPattern": "dropped [1-9][0-9]*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + "$TC qdisc del dev $IFB root", + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -516,26 +540,32 @@ "ets" ], "plugins": { - "requires": "nsPlugin" + "requires": [ + "nsPlugin", + "scapyPlugin" + ] }, "setup": [ - "$IP link set dev $DUMMY up || true", - "$IP addr add 10.10.10.10/24 dev $DUMMY || true", - "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 strict 1", - "$TC class change dev $DUMMY parent 1: classid 1:1 ets", - "$TC qdisc add dev $DUMMY parent 1:1 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", - "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:1", - "ping -c 5 -f -I $DUMMY 10.10.10.1 > /dev/null || true", - "sleep 0.1" + "$TC qdisc add dev $IFB handle 1: root ets bands 2 strict 1", + "$TC class change dev $IFB parent 1: classid 1:1 ets", + "$TC qdisc add dev $IFB parent 1:1 handle 10: fq_codel memory_limit 1 flows 1 target 0.1ms interval 1ms", + "$TC filter add dev $IFB parent 1: protocol ip prio 1 u32 match ip protocol 1 0xff flowid 1:1", + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip protocol 1 0xff action mirred egress mirror dev $IFB" ], - "cmdUnderTest": "$TC -s qdisc show dev $DUMMY", + "scapy": { + "iface": "$DEV0", + "count": 5, + "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()" + }, + "cmdUnderTest": "$TC -s qdisc show dev $IFB", "expExitCode": "0", - "verifyCmd": "$TC -s qdisc show dev $DUMMY | grep -A 5 'qdisc fq_codel'", + "verifyCmd": "$TC -s qdisc show dev $IFB | grep -A 5 'qdisc fq_codel'", "matchPattern": "dropped [1-9][0-9]*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + "$TC qdisc del dev $IFB root", + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -1326,5 +1356,189 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] + }, + { + "id": "c797", + "name": "Verify fq_codel won't mistakenly deactivate QFQ parent class during peek", + "category": [ + "qdisc", + "qfq", + "fq_codel" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: qfq", + "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 1 maxpkt 1000", + "$TC class add dev $DUMMY parent 1: classid 1:2 qfq weight 1 maxpkt 1000", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 plug limit 1024", + "$IP l set dev $DUMMY mtu 1500", + "$TC qdisc add dev $DUMMY parent 1:2 handle 10: fq_codel target 1 interval 1 flows 1", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2", + "$IP l set dev $DUMMY mtu 65336", + "ping -c 1 -I $DUMMY 10.10.10.1 -W0.01 > /dev/null || true", + "ping -c 3 -s 2000 -I $DUMMY 10.10.10.2 -W0.01 > /dev/null || true", + "sleep 0.1" + ], + "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 2:0 plug release_indefinite", + "expExitCode": "0", + "verifyCmd": "$TC -s -j qdisc show dev $DUMMY", + "matchJSON": [ + { + "kind": "qfq", + "handle": "1:", + "packets": 3, + "drops": 1, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "plug", + "handle": "2:", + "packets": 1, + "drops": 0, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "fq_codel", + "handle": "10:", + "packets": 2, + "drops": 1, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root", + "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + ] + }, + { + "id": "82d9", + "name": "Verify codel won't mistakenly deactivate QFQ parent class during peek", + "category": [ + "qdisc", + "qfq", + "codel" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true", + "$TC qdisc add dev $DUMMY root handle 1: qfq", + "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 1 maxpkt 1000", + "$TC class add dev $DUMMY parent 1: classid 1:2 qfq weight 1 maxpkt 1000", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 plug limit 1024", + "$IP l set dev $DUMMY mtu 1500", + "$TC qdisc add dev $DUMMY parent 1:2 handle 10: codel target 1ms interval 1ms", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2", + "$IP l set dev $DUMMY mtu 65336", + "ping -c 1 -I $DUMMY 10.10.10.1 -W0.01 > /dev/null || true", + "ping -c 3 -s 2000 -I $DUMMY 10.10.10.2 -W0.01 > /dev/null || true", + "sleep 0.1" + ], + "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 2:0 plug release_indefinite", + "expExitCode": "0", + "verifyCmd": "$TC -s -j qdisc show dev $DUMMY", + "matchJSON": [ + { + "kind": "qfq", + "handle": "1:", + "packets": 3, + "drops": 1, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "plug", + "handle": "2:", + "packets": 1, + "drops": 0, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "codel", + "handle": "10:", + "packets": 2, + "drops": 1, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root", + "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + ] + }, + { + "id": "d3da", + "name": "Verify dualpi2 won't mistakenly deactivate QFQ parent class during peek", + "category": [ + "qdisc", + "qfq", + "dualpi2" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$IP link set dev $DUMMY up || true", + "$IP addr add 10.10.10.10/24 dev $DUMMY || true" , + "$TC qdisc add dev $DUMMY root handle 1: qfq", + "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 1 maxpkt 1000", + "$TC class add dev $DUMMY parent 1: classid 1:2 qfq weight 1 maxpkt 1000", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 plug limit 1024", + "$TC qdisc add dev $DUMMY parent 1:2 handle 10: dualpi2 step_thresh 500ms", + "$TC filter add dev $DUMMY parent 10: protocol ip prio 1 matchall classid 10:1 action ok", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1", + "$TC filter add dev $DUMMY parent 1: protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2", + "ping -c 1 -I $DUMMY 10.10.10.1 -W0.01 || true", + "ping -c 3 -i 0.1 -I $DUMMY 10.10.10.2 -W0.01 || true", + "sleep 0.7", + "ping -c 1 -I $DUMMY 10.10.10.2 -W0.01 || true", + "$TC qdisc change dev $DUMMY handle 2:0 plug release_indefinite" + ], + "cmdUnderTest": "ping -c 1 -I $DUMMY 10.10.10.1 -W0.01", + "expExitCode": "1", + "verifyCmd": "$TC -s -j qdisc show dev $DUMMY", + "matchJSON": [ + { + "kind": "qfq", + "handle": "1:", + "packets": 4, + "drops": 2, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "plug", + "handle": "2:", + "packets": 2, + "drops": 0, + "backlog": 0, + "qlen": 0 + }, + { + "kind": "dualpi2", + "handle": "10:", + "packets": 2, + "drops": 2, + "backlog": 0, + "qlen": 0 + } + ], + "teardown": [ + "$TC qdisc del dev $DUMMY root", + "$IP addr del 10.10.10.10/24 dev $DUMMY || true" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py index 81b4ac3f050c..511d66c36a2a 100755 --- a/tools/testing/selftests/tc-testing/tdc.py +++ b/tools/testing/selftests/tc-testing/tdc.py @@ -378,6 +378,7 @@ def run_one_test(pm, args, index, tidx): dev0 = NAMES['DEV0'] dev1 = NAMES['DEV1'] dummy = NAMES['DUMMY'] + ifb = NAMES['IFB'] result = True tresult = "" tap = "" @@ -414,6 +415,7 @@ def run_one_test(pm, args, index, tidx): NAMES['DEV0'] = '{}id{}'.format(NAMES['DEV0'], tidx['id']) NAMES['DEV1'] = '{}id{}'.format(NAMES['DEV1'], tidx['id']) NAMES['DUMMY'] = '{}id{}'.format(NAMES['DUMMY'], tidx['id']) + NAMES['IFB'] = '{}id{}'.format(NAMES['IFB'], tidx['id']) pm.call_pre_case(tidx) prepare_env(tidx, args, pm, 'setup', "-----> prepare stage", tidx["setup"]) @@ -474,6 +476,7 @@ def run_one_test(pm, args, index, tidx): NAMES['DEV0'] = dev0 NAMES['DEV1'] = dev1 NAMES['DUMMY'] = dummy + NAMES['IFB'] = ifb return res diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py index 9488b03cbc2c..cd0bd42f05a5 100644 --- a/tools/testing/selftests/tc-testing/tdc_config.py +++ b/tools/testing/selftests/tc-testing/tdc_config.py @@ -17,6 +17,7 @@ NAMES = { 'DEV1': 'v0p1', 'DEV2': '', 'DUMMY': 'dummy1', + 'IFB': 'ifbtdc0', 'ETHTOOL': '/usr/sbin/ethtool', 'ETH': 'eth0', 'BATCH_FILE': './batch.txt', diff --git a/tools/testing/selftests/vsock/vmtest.sh b/tools/testing/selftests/vsock/vmtest.sh index d97913a6bdc7..310dfc2a39ad 100755 --- a/tools/testing/selftests/vsock/vmtest.sh +++ b/tools/testing/selftests/vsock/vmtest.sh @@ -330,27 +330,34 @@ check_netns() { return 0 } -check_vng() { - local tested_versions - local version - local ok +# Compare MAJOR.MINOR versions numerically. Returns 0 (true) if $1 < $2. +version_lt() { + local -a a=(${1//./ }) + local -a b=(${2//./ }) - tested_versions=("1.33" "1.36" "1.37") - version="$(vng --version)" - - ok=0 - for tv in "${tested_versions[@]}"; do - if [[ "${version}" == *"${tv}"* ]]; then - ok=1 - break - fi - done - - if [[ ! "${ok}" -eq 1 ]]; then - printf "warning: vng version '%s' has not been tested and may " "${version}" >&2 - printf "not function properly.\n\tThe following versions have been tested: " >&2 - echo "${tested_versions[@]}" >&2 + if [[ "${a[0]}" -lt "${b[0]}" ]]; then + return 0 + elif [[ "${a[0]}" -gt "${b[0]}" ]]; then + return 1 + elif [[ "${a[1]}" -lt "${b[1]}" ]]; then + return 0 fi + + return 1 +} + +check_vng() { + local version + + version="$(vng --version | awk '{print $2}')" + + # Supported: 1.33, or any version >= 1.36. 1.34 and 1.35 are untested. + if [[ "${version}" == "1.33" ]] || ! version_lt "${version}" "1.36"; then + return + fi + + printf "warning: vng version '%s' has not been tested and may " "${version}" >&2 + printf "not function properly.\n\tSupported: 1.33 or >= 1.36\n" >&2 } check_socat() { @@ -438,8 +445,14 @@ vng_dry_run() { # stopped with SIGTTOU and hangs until kselftest's timer expires. # setsid works around this by launching vng in a new session that has # no controlling terminal, so tcsetattr() succeeds. + # + # Fixed in 1.41 (https://github.com/arighi/virtme-ng/pull/453). - setsid -w vng --run "$@" --dry-run &>/dev/null + if version_lt "$(vng --version | awk '{print $2}')" "1.41"; then + setsid -w vng --run "$@" --dry-run &>/dev/null + else + vng --run "$@" --dry-run &>/dev/null + fi } vm_start() {